@weasel-js/prefs 1.9.3 → 1.9.4

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/index.js CHANGED
@@ -1,6 +1,28 @@
1
1
  import { openRecords, openRecordsSync } from '@weasel-js/storage';
2
+ import { isPlainObject } from '@weasel-js/registry';
2
3
  import { unitScale, formatUnit } from '@weasel-js/quantity';
3
4
 
5
+ // src/alias.ts
6
+ function prefAliasTarget(alias, leafAt) {
7
+ const passed = /* @__PURE__ */ new Set();
8
+ let path = alias.of;
9
+ for (; ; ) {
10
+ if (passed.has(path)) return void 0;
11
+ passed.add(path);
12
+ const leaf = leafAt(path);
13
+ if (leaf === void 0) return void 0;
14
+ if (leaf.kind !== "alias") return { path, leaf };
15
+ path = leaf.of;
16
+ }
17
+ }
18
+ function prefAliasedLeaf(alias, target) {
19
+ return {
20
+ ...target,
21
+ name: alias.name === "" ? target.name : alias.name,
22
+ description: alias.description === "" ? target.description : alias.description
23
+ };
24
+ }
25
+
4
26
  // src/helpers.ts
5
27
  function prefDisplayBounds(p) {
6
28
  if (!p.unit) return { min: p.min, max: p.max, step: p.step ?? 1 };
@@ -14,23 +36,61 @@ function prefDisplayBounds(p) {
14
36
  function isPrefLeaf(node) {
15
37
  return "kind" in node;
16
38
  }
39
+ var VALUELESS = /* @__PURE__ */ new Set(["action", "alias", "label", "separator"]);
40
+ function prefHoldsValue(leaf) {
41
+ return !VALUELESS.has(leaf.kind);
42
+ }
43
+ function isPrefSection(node) {
44
+ return !isPrefLeaf(node) && "members" in node;
45
+ }
46
+ function prefSectionLeaves(members) {
47
+ return Object.entries(members).flatMap(
48
+ ([key, child]) => isPrefLeaf(child) ? [[key, child]] : prefSectionLeaves(child.members)
49
+ );
50
+ }
51
+ function prefLeafAt(schema, path) {
52
+ if (isPrefSection(schema)) return prefSectionLeaves(schema.members).find(([key]) => key === path)?.[1];
53
+ let node = schema;
54
+ for (const seg of path.split(".")) {
55
+ if (isPrefLeaf(node) || !Object.hasOwn(node.children, seg)) return void 0;
56
+ node = node.children[seg];
57
+ }
58
+ return isPrefLeaf(node) ? node : void 0;
59
+ }
17
60
  function prefValueAtPath(values, path) {
18
61
  let cur = values;
19
62
  for (const seg of path.split(".")) {
20
- if (cur == null || typeof cur !== "object") return void 0;
63
+ if (cur == null || typeof cur !== "object" || !Object.hasOwn(cur, seg)) return void 0;
21
64
  cur = cur[seg];
22
65
  }
23
66
  return cur;
24
67
  }
25
- function visiblePrefSubtree(node, showHidden) {
26
- if (isPrefLeaf(node)) return node.hidden && !showHidden ? null : node;
68
+ function visibleFields(fields, keepEmpty) {
69
+ const out = {};
70
+ for (const [key, child] of Object.entries(fields)) {
71
+ if (isPrefLeaf(child)) {
72
+ const kept = visiblePrefSubtree(child, false, keepEmpty);
73
+ if (kept) out[key] = kept;
74
+ continue;
75
+ }
76
+ const members = visibleFields(child.members, keepEmpty);
77
+ if (Object.keys(members).length > 0 || keepEmpty) out[key] = { ...child, members };
78
+ }
79
+ return out;
80
+ }
81
+ function visiblePrefSubtree(node, showHidden, keepEmpty = false) {
82
+ if (isPrefLeaf(node)) {
83
+ if (node.hidden && !showHidden) return null;
84
+ if (showHidden || node.kind !== "object") return node;
85
+ return { ...node, children: visibleFields(node.children, keepEmpty) };
86
+ }
27
87
  const group = node;
28
88
  const children = {};
29
89
  for (const [key, child] of Object.entries(group.children)) {
30
- const kept = visiblePrefSubtree(child, showHidden);
90
+ const kept = visiblePrefSubtree(child, showHidden, keepEmpty);
31
91
  if (kept) children[key] = kept;
32
92
  }
33
- if (Object.keys(children).length === 0) return null;
93
+ if (Object.keys(children).length === 0 && !keepEmpty) return null;
34
94
  return { ...node, children };
35
95
  }
36
96
  var VERSION_RECORD = "$version";
@@ -52,13 +112,17 @@ function prefLeaves(schema) {
52
112
  walk(schema, "");
53
113
  return out;
54
114
  }
115
+ function setOwnPrefValue(tree, key, value) {
116
+ Object.defineProperty(tree, key, { value, writable: true, enumerable: true, configurable: true });
117
+ }
55
118
  function assignPrefValueAtPath(root, path, value) {
56
119
  const parts = path.split(".");
57
120
  let cursor = root;
58
- for (let i = 0; i < parts.length - 1; i++) {
59
- cursor = cursor[parts[i]] ??= {};
121
+ for (const part of parts.slice(0, -1)) {
122
+ if (!Object.hasOwn(cursor, part) || cursor[part] == null) setOwnPrefValue(cursor, part, {});
123
+ cursor = cursor[part];
60
124
  }
61
- cursor[parts[parts.length - 1]] = value;
125
+ setOwnPrefValue(cursor, parts[parts.length - 1], value);
62
126
  }
63
127
  function flattenPrefValues(schema, tree) {
64
128
  const out = [];
@@ -73,27 +137,85 @@ function prefLeafMatches(pref, path, query) {
73
137
  if (pref.description?.toLowerCase().includes(query)) return true;
74
138
  return path.toLowerCase().includes(query);
75
139
  }
76
- function filterPrefSubtree(node, query, path = "") {
140
+ function filterPrefSubtree(node, query, path = "", leafAt) {
77
141
  const q = query.trim().toLowerCase();
78
142
  if (q === "") return node;
79
- if (isPrefLeaf(node)) return prefLeafMatches(node, path, q) ? node : null;
143
+ const find = leafAt ?? ((p) => isPrefLeaf(node) ? void 0 : prefLeafAt(node, p));
144
+ if (isPrefLeaf(node)) {
145
+ const target = node.kind === "alias" ? prefAliasTarget(node, find) : void 0;
146
+ const drawn = target ? prefAliasedLeaf(node, target.leaf) : node;
147
+ return prefLeafMatches(drawn, path, q) ? node : null;
148
+ }
80
149
  const group = node;
81
150
  if (group.name.toLowerCase().includes(q)) return node;
82
151
  const children = {};
83
152
  for (const [key, child] of Object.entries(group.children)) {
84
- const kept = filterPrefSubtree(child, q, path === "" ? key : `${path}.${key}`);
153
+ const kept = filterPrefSubtree(child, q, path === "" ? key : `${path}.${key}`, find);
85
154
  if (kept) children[key] = kept;
86
155
  }
87
156
  if (Object.keys(children).length === 0) return null;
88
157
  return { ...node, children };
89
158
  }
90
159
 
91
- // src/migrate.ts
160
+ // src/object.ts
161
+ var isObject = (v) => typeof v === "object" && v !== null;
162
+ function prefObjectBase(pref, held) {
163
+ if (isObject(held)) return held;
164
+ return pref.fromScalar?.(held) ?? (isObject(pref.default) ? pref.default : {});
165
+ }
166
+ function prefObjectWithField(pref, held, key, value) {
167
+ const base = prefObjectBase(pref, held);
168
+ if (value !== void 0) return { ...base, [key]: value };
169
+ const { [key]: _dropped, ...rest } = base;
170
+ return rest;
171
+ }
172
+
173
+ // src/union.ts
174
+ function prefVariantOf(leaf, value) {
175
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
176
+ const key = value[leaf.tag];
177
+ if (typeof key !== "string" || !Object.hasOwn(leaf.variants, key)) return void 0;
178
+ return [key, leaf.variants[key]];
179
+ }
180
+ function prefVariantDefault(leaf, key) {
181
+ const held = leaf.variants[key]?.default;
182
+ const fields = held !== null && typeof held === "object" ? held : {};
183
+ return { ...fields, [leaf.tag]: key };
184
+ }
185
+
186
+ // src/type.ts
187
+ function prefType(name, leaf) {
188
+ return { ...leaf, type: name };
189
+ }
190
+
191
+ // src/groups.ts
192
+ function prefGroupIsPage(group) {
193
+ return group.as === void 0 || group.as === "page";
194
+ }
195
+
196
+ // src/stamp.ts
92
197
  var isVersion = (v) => typeof v === "number" && Number.isInteger(v) && v >= 0;
93
- function runPrefsMigrations(cache, migrations) {
198
+ function stampRecord(value, at) {
199
+ return { $at: at, value };
200
+ }
201
+ function isStampedRecord(record) {
202
+ if (record === null || typeof record !== "object") return false;
203
+ const keys = Object.keys(record);
204
+ return keys.length === 2 && Object.hasOwn(record, "value") && isVersion(record.$at);
205
+ }
206
+ function readRecord(record, unstamped) {
207
+ return isStampedRecord(record) ? { at: record.$at, value: record.value, stamped: true } : { at: unstamped, value: record, stamped: false };
208
+ }
209
+ function recordValue(record) {
210
+ return isStampedRecord(record) ? record.value : record;
211
+ }
212
+
213
+ // src/migrate.ts
214
+ var isVersion2 = (v) => typeof v === "number" && Number.isInteger(v) && v >= 0;
215
+ function runPrefsMigrations(cache, migrations, { stampAll = true } = {}) {
94
216
  const target = migrations.length;
95
217
  const raw = cache.get(VERSION_RECORD);
96
- if (cache.has(VERSION_RECORD) && !isVersion(raw)) {
218
+ if (cache.has(VERSION_RECORD) && !isVersion2(raw)) {
97
219
  console.warn(
98
220
  `[prefs] "${cache.prefix}" has an unreadable ${VERSION_RECORD} (${JSON.stringify(raw)}); opening read-only`
99
221
  );
@@ -101,19 +223,23 @@ function runPrefsMigrations(cache, migrations) {
101
223
  return false;
102
224
  }
103
225
  const stored = cache.has(VERSION_RECORD) ? raw : 0;
104
- if (stored > target) {
226
+ const held = cache.entries().filter(([name]) => name !== VERSION_RECORD).map(([name, record]) => ({ name, ...readRecord(record, Math.min(stored, target)) }));
227
+ const newest = Math.max(stored, ...held.map((r) => r.at));
228
+ if (newest > target) {
105
229
  console.warn(
106
- `[prefs] "${cache.prefix}" was written at version ${stored}, newer than this build's ${target}; opening read-only`
230
+ `[prefs] "${cache.prefix}" was written at version ${newest}, newer than this build's ${target}; opening read-only`
107
231
  );
108
232
  cache.stopWriting();
109
233
  return false;
110
234
  }
111
- if (stored === target || !cache.writable) return false;
112
- const before = cache.entries().filter(([name]) => name !== VERSION_RECORD);
113
- const records = new Map(before.map(([name, value]) => [name, structuredClone(value)]));
114
- for (let v = stored; v < target; v++) {
235
+ if (!cache.writable) return false;
236
+ const at = new Map(held.filter((r) => r.at < target).map((r) => [r.name, r.at]));
237
+ const records = new Map(held.filter((r) => at.has(r.name)).map((r) => [r.name, structuredClone(r.value)]));
238
+ for (let v = Math.min(target, ...at.values()); v < target; v++) {
239
+ const due = new Map([...records].filter(([name]) => at.get(name) <= v));
240
+ const before = [...due.keys()];
115
241
  try {
116
- migrations[v](records);
242
+ migrations[v](due);
117
243
  } catch (error) {
118
244
  console.warn(
119
245
  `[prefs] migration ${v} \u2192 ${v + 1} for "${cache.prefix}" threw; opening read-only on the unmigrated records`,
@@ -122,35 +248,76 @@ function runPrefsMigrations(cache, migrations) {
122
248
  cache.stopWriting();
123
249
  return false;
124
250
  }
251
+ for (const name of before) if (!due.has(name)) records.delete(name);
252
+ for (const [name, value] of due) {
253
+ records.set(name, value);
254
+ at.set(name, v + 1);
255
+ }
256
+ }
257
+ let wrote = false;
258
+ for (const r of held) {
259
+ if (at.has(r.name)) {
260
+ if (!records.has(r.name)) cache.delete(r.name);
261
+ wrote = true;
262
+ } else if (stampAll && !r.stamped) {
263
+ cache.set(r.name, stampRecord(r.value, target));
264
+ wrote = true;
265
+ }
125
266
  }
126
- for (const [name] of before) if (!records.has(name)) cache.delete(name);
127
267
  for (const [name, value] of records) {
128
268
  if (value === void 0) cache.delete(name);
129
- else cache.set(name, value);
269
+ else cache.set(name, stampRecord(value, target));
130
270
  }
131
- cache.set(VERSION_RECORD, target);
132
- return true;
133
- }
134
- function watchPrefsVersion(cache, target) {
135
- return cache.subscribe((changes) => {
136
- for (const change of changes) {
137
- if (change.origin !== "remote" || change.name !== VERSION_RECORD || change.value === void 0) continue;
138
- if (!isVersion(change.value)) {
139
- console.warn(
140
- `[prefs] "${cache.prefix}" has an unreadable ${VERSION_RECORD} (${JSON.stringify(change.value)}) from another writer; no longer persisting`
141
- );
142
- cache.stopWriting();
143
- } else if (change.value > target) {
144
- console.warn(
145
- `[prefs] "${cache.prefix}" was migrated to version ${change.value} by another writer; this build knows ${target}; no longer persisting`
146
- );
147
- cache.stopWriting();
148
- }
271
+ if (stored !== target) {
272
+ cache.set(VERSION_RECORD, target);
273
+ wrote = true;
274
+ }
275
+ return wrote;
276
+ }
277
+ function watchPrefsVersion(cache, migrations) {
278
+ const target = migrations.length;
279
+ let versioned = cache.has(VERSION_RECORD);
280
+ let stopped = false;
281
+ const migrate = () => {
282
+ const cleared = versioned && !cache.has(VERSION_RECORD);
283
+ if (cleared) cache.set(VERSION_RECORD, target);
284
+ else if (!runPrefsMigrations(cache, migrations, { stampAll: false })) return;
285
+ versioned = true;
286
+ void cache.flush();
287
+ };
288
+ void cache.whenRead().then(() => {
289
+ if (!stopped && cache.writable) migrate();
290
+ });
291
+ const heard = (version) => {
292
+ if (!isVersion2(version)) {
293
+ console.warn(
294
+ `[prefs] "${cache.prefix}" has an unreadable ${VERSION_RECORD} (${JSON.stringify(version)}) from another writer; no longer persisting`
295
+ );
296
+ cache.stopWriting();
297
+ } else if (version > target) {
298
+ console.warn(
299
+ `[prefs] "${cache.prefix}" was migrated to version ${version} by another writer; this build knows ${target}; no longer persisting`
300
+ );
301
+ cache.stopWriting();
149
302
  }
303
+ };
304
+ const stopOverruled = cache.onOverruled((changes) => {
305
+ for (const [name, value] of changes) if (name === VERSION_RECORD && value !== void 0) heard(value);
150
306
  });
307
+ const stopChanges = cache.subscribe((changes) => {
308
+ const remote = changes.filter((change) => change.origin === "remote");
309
+ for (const change of remote) {
310
+ if (change.name === VERSION_RECORD && change.value !== void 0) heard(change.value);
311
+ }
312
+ if (remote.length > 0 && cache.writable) migrate();
313
+ versioned ||= cache.has(VERSION_RECORD);
314
+ });
315
+ return () => {
316
+ stopped = true;
317
+ stopOverruled();
318
+ stopChanges();
319
+ };
151
320
  }
152
-
153
- // src/repair.ts
154
321
  function repairPrefValue(leaf, stored, validators) {
155
322
  if (leaf.kind === "number") {
156
323
  if (stored === "Infinity") stored = Infinity;
@@ -180,14 +347,45 @@ function repairPrefValue(leaf, stored, validators) {
180
347
  return e.options.some((o) => o.value === stored) ? stored : leaf.default;
181
348
  }
182
349
  case "object":
183
- if (leaf.fromScalar || isPlainObject(stored)) return stored;
184
- return leaf.default;
350
+ if (isPlainObject(stored)) return repairFields(leaf, stored, validators);
351
+ return leaf.fromScalar ? stored : leaf.default;
352
+ case "map":
353
+ return repairMap(leaf, stored, validators);
354
+ case "union": {
355
+ const variant = prefVariantOf(leaf, stored);
356
+ return variant ? repairFields(variant[1], stored, validators) : leaf.default;
357
+ }
358
+ case "list":
359
+ return repairList(leaf, stored, validators);
185
360
  default:
186
361
  return stored;
187
362
  }
188
363
  }
189
- function isPlainObject(v) {
190
- return v !== null && typeof v === "object" && !Array.isArray(v);
364
+ function repairFields(leaf, stored, validators) {
365
+ let out;
366
+ for (const [key, field] of prefSectionLeaves(leaf.children)) {
367
+ if (!Object.hasOwn(stored, key) || stored[key] === void 0) continue;
368
+ const read = repairPrefValue(field, stored[key], validators);
369
+ if (read !== stored[key]) (out ??= { ...stored })[key] = read;
370
+ }
371
+ return out ?? stored;
372
+ }
373
+ function repairMap(leaf, stored, validators) {
374
+ if (!isPlainObject(stored)) return leaf.default;
375
+ const held = stored;
376
+ let out;
377
+ for (const [key, value] of Object.entries(held)) {
378
+ const read = repairPrefValue(leaf.item, value, validators);
379
+ if (read !== value) (out ??= { ...held })[key] = read;
380
+ }
381
+ return out ?? stored;
382
+ }
383
+ function repairList(leaf, stored, validators) {
384
+ if (!Array.isArray(stored)) return leaf.default;
385
+ if (leaf.minItems !== void 0 && stored.length < leaf.minItems) return leaf.default;
386
+ const kept = leaf.maxItems !== void 0 && stored.length > leaf.maxItems ? stored.slice(0, leaf.maxItems) : stored;
387
+ const read = kept.map((entry) => repairPrefValue(leaf.item, entry, validators));
388
+ return kept === stored && read.every((entry, i) => entry === stored[i]) ? stored : read;
191
389
  }
192
390
  function repairNumber(leaf, stored) {
193
391
  if (typeof stored !== "number" || Number.isNaN(stored)) return leaf.default;
@@ -201,9 +399,30 @@ function repairNumber(leaf, stored) {
201
399
  }
202
400
 
203
401
  // src/store.ts
204
- var encodeInfinity = (leaf, value) => leaf.kind === "number" && (value === Infinity || value === -Infinity) ? String(value) : value;
205
- function createPrefsStore(schema, cache, validators, onClose) {
206
- const leaves = prefLeaves(schema);
402
+ var encodeInfinity = (leaf, value) => {
403
+ if (leaf.kind === "number") return value === Infinity || value === -Infinity ? String(value) : value;
404
+ if (leaf.kind === "list" && Array.isArray(value)) {
405
+ const { item } = leaf;
406
+ return value.map((entry) => encodeInfinity(item, entry));
407
+ }
408
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return value;
409
+ const held = value;
410
+ if (leaf.kind === "map") {
411
+ const { item } = leaf;
412
+ return Object.fromEntries(Object.entries(held).map(([key, entry]) => [key, encodeInfinity(item, entry)]));
413
+ }
414
+ const fields = leaf.kind === "union" ? prefVariantOf(leaf, held)?.[1] : leaf;
415
+ if (fields?.kind !== "object") return value;
416
+ let out;
417
+ for (const [key, field] of prefSectionLeaves(fields.children)) {
418
+ if (!Object.hasOwn(held, key)) continue;
419
+ const encoded = encodeInfinity(field, held[key]);
420
+ if (encoded !== held[key]) (out ??= { ...held })[key] = encoded;
421
+ }
422
+ return out ?? value;
423
+ };
424
+ function createPrefsStore(schema, cache, validators, onClose, version = 0) {
425
+ const leaves = new Map([...prefLeaves(schema)].filter(([, leaf]) => prefHoldsValue(leaf)));
207
426
  let snapshot = null;
208
427
  let previous = null;
209
428
  const build = () => {
@@ -218,9 +437,10 @@ function createPrefsStore(schema, cache, validators, onClose) {
218
437
  value = leaf.default;
219
438
  unset.add(path);
220
439
  } else {
221
- const stored = cache.get(path);
222
- raw.set(path, stored);
223
- value = previous?.raw.has(path) && previous.raw.get(path) === stored ? previous.byPath.get(path) : repairPrefValue(leaf, stored, validators);
440
+ const record = cache.get(path);
441
+ const stored = recordValue(record);
442
+ raw.set(path, record);
443
+ value = previous?.raw.has(path) && previous.raw.get(path) === record ? previous.byPath.get(path) : repairPrefValue(leaf, stored, validators);
224
444
  }
225
445
  byPath.set(path, value);
226
446
  assignPrefValueAtPath(tree, path, value);
@@ -233,8 +453,9 @@ function createPrefsStore(schema, cache, validators, onClose) {
233
453
  const buildStored = () => {
234
454
  const tree = {};
235
455
  const branches = new WeakSet([tree]);
236
- for (const [name, value] of cache.entries()) {
456
+ for (const [name, record] of cache.entries()) {
237
457
  if (name === VERSION_RECORD) continue;
458
+ const value = recordValue(record);
238
459
  const parts = name.split(".");
239
460
  let cursor = tree;
240
461
  for (const part of parts.slice(0, -1)) {
@@ -244,12 +465,12 @@ function createPrefsStore(schema, cache, validators, onClose) {
244
465
  } else {
245
466
  const branch = {};
246
467
  branches.add(branch);
247
- cursor[part] = branch;
468
+ setOwnPrefValue(cursor, part, branch);
248
469
  cursor = branch;
249
470
  }
250
471
  }
251
472
  const last = parts[parts.length - 1];
252
- if (!branches.has(cursor[last])) cursor[last] = value;
473
+ if (!branches.has(cursor[last])) setOwnPrefValue(cursor, last, value);
253
474
  }
254
475
  return tree;
255
476
  };
@@ -273,7 +494,8 @@ function createPrefsStore(schema, cache, validators, onClose) {
273
494
  console.warn(`[prefs] no leaf at "${path}"; ignoring the write`);
274
495
  return;
275
496
  }
276
- cache.set(path, encodeInfinity(leaf, value));
497
+ const stored = encodeInfinity(leaf, value);
498
+ cache.set(path, stored === void 0 ? void 0 : stampRecord(stored, version));
277
499
  },
278
500
  reset: (path) => {
279
501
  for (const p of leaves.keys()) {
@@ -293,6 +515,8 @@ function createPrefsStore(schema, cache, validators, onClose) {
293
515
  get writable() {
294
516
  return cache.writable;
295
517
  },
518
+ read: () => cache.read(),
519
+ whenRead: () => cache.whenRead(),
296
520
  flush: () => cache.flush(),
297
521
  close: async () => {
298
522
  onClose?.();
@@ -308,15 +532,43 @@ async function openPrefs(schema, options) {
308
532
  const cache = await openRecords({ storage: options.storage, prefix: options.prefix });
309
533
  const stop = watch(cache, options);
310
534
  if (runPrefsMigrations(cache, options.migrations ?? [])) await cache.flush();
311
- return createPrefsStore(schema, cache, options.validators, stop);
535
+ return createPrefsStore(schema, cache, options.validators, stop, options.migrations?.length ?? 0);
312
536
  }
313
537
  function openPrefsSync(schema, options) {
314
538
  const cache = openRecordsSync({ storage: options.storage, prefix: options.prefix });
315
539
  const stop = watch(cache, options);
316
540
  if (runPrefsMigrations(cache, options.migrations ?? [])) void cache.flush();
317
- return createPrefsStore(schema, cache, options.validators, stop);
541
+ return createPrefsStore(schema, cache, options.validators, stop, options.migrations?.length ?? 0);
542
+ }
543
+ var watch = (cache, options) => watchPrefsVersion(cache, options.migrations ?? []);
544
+
545
+ // src/kinds.ts
546
+ var ONE = { compound: false, several: false };
547
+ var MANY = { compound: true, several: true };
548
+ var PREF_KIND_TRAITS = {
549
+ number: ONE,
550
+ boolean: ONE,
551
+ string: ONE,
552
+ enum: ONE,
553
+ color: ONE,
554
+ paint: ONE,
555
+ object: MANY,
556
+ list: MANY,
557
+ map: MANY,
558
+ union: MANY,
559
+ field: ONE,
560
+ action: { compound: false, several: true },
561
+ alias: ONE,
562
+ label: ONE,
563
+ separator: ONE
564
+ };
565
+ var traitsOf = (leaf) => Object.hasOwn(PREF_KIND_TRAITS, leaf.kind) ? PREF_KIND_TRAITS[leaf.kind] : ONE;
566
+ function prefIsCompound(leaf) {
567
+ return traitsOf(leaf).compound;
568
+ }
569
+ function prefDrawsSeveral(leaf) {
570
+ return traitsOf(leaf).several;
318
571
  }
319
- var watch = (cache, options) => cache.writable ? watchPrefsVersion(cache, (options.migrations ?? []).length) : void 0;
320
572
  function prefUnit(system, display, opts) {
321
573
  let self;
322
574
  try {
@@ -356,7 +608,14 @@ var PREF_KINDS = {
356
608
  color: true,
357
609
  paint: true,
358
610
  object: true,
359
- field: true
611
+ list: true,
612
+ map: true,
613
+ union: true,
614
+ field: true,
615
+ action: true,
616
+ alias: true,
617
+ label: true,
618
+ separator: true
360
619
  };
361
620
  function pairRowsOf(leaves) {
362
621
  const rows = /* @__PURE__ */ new Map();
@@ -373,6 +632,6 @@ function isBuiltinPref(leaf) {
373
632
  return Object.hasOwn(PREF_KINDS, leaf.kind);
374
633
  }
375
634
 
376
- export { PREF_KINDS, VERSION_RECORD, filterPrefSubtree, flattenPrefValues, isBuiltinPref, isPrefLeaf, openPrefs, openPrefsSync, pairRowsOf, prefDisplayBounds, prefLeaves, prefUnit, prefValueAtPath, repairPrefValue, visiblePrefSubtree };
635
+ export { PREF_KINDS, PREF_KIND_TRAITS, VERSION_RECORD, filterPrefSubtree, flattenPrefValues, isBuiltinPref, isPrefLeaf, isPrefSection, openPrefs, openPrefsSync, pairRowsOf, prefAliasTarget, prefAliasedLeaf, prefDisplayBounds, prefDrawsSeveral, prefGroupIsPage, prefHoldsValue, prefIsCompound, prefLeafAt, prefLeaves, prefObjectBase, prefObjectWithField, prefSectionLeaves, prefType, prefUnit, prefValueAtPath, prefVariantDefault, prefVariantOf, repairPrefValue, visiblePrefSubtree };
377
636
  //# sourceMappingURL=index.js.map
378
637
  //# sourceMappingURL=index.js.map