@zodal/dials-ui 0.1.0
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.cjs +597 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +386 -0
- package/dist/index.d.ts +386 -0
- package/dist/index.js +551 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
// src/widgets.ts
|
|
2
|
+
var KNOWN_WIDGETS = /* @__PURE__ */ new Set([
|
|
3
|
+
"switch",
|
|
4
|
+
"select",
|
|
5
|
+
"radio",
|
|
6
|
+
"slider",
|
|
7
|
+
"number",
|
|
8
|
+
"text",
|
|
9
|
+
"textarea",
|
|
10
|
+
"secret",
|
|
11
|
+
"color",
|
|
12
|
+
"date",
|
|
13
|
+
"path",
|
|
14
|
+
"object",
|
|
15
|
+
"array",
|
|
16
|
+
"rawJson"
|
|
17
|
+
]);
|
|
18
|
+
function widgetKindFor(input) {
|
|
19
|
+
if (typeof input.metaWidget === "string" && KNOWN_WIDGETS.has(input.metaWidget)) {
|
|
20
|
+
return input.metaWidget;
|
|
21
|
+
}
|
|
22
|
+
if (input.sensitivity === "secret") return "secret";
|
|
23
|
+
switch (input.zodType) {
|
|
24
|
+
case "boolean":
|
|
25
|
+
return "switch";
|
|
26
|
+
case "enum":
|
|
27
|
+
return input.enumValues && input.enumValues.length > 0 && input.enumValues.length <= 4 ? "radio" : "select";
|
|
28
|
+
case "number":
|
|
29
|
+
return input.bounds && input.bounds.min !== void 0 && input.bounds.max !== void 0 ? "slider" : "number";
|
|
30
|
+
case "string":
|
|
31
|
+
return "text";
|
|
32
|
+
case "object":
|
|
33
|
+
return "object";
|
|
34
|
+
case "array":
|
|
35
|
+
return "array";
|
|
36
|
+
default:
|
|
37
|
+
return "rawJson";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/registry.ts
|
|
42
|
+
import {
|
|
43
|
+
createRendererRegistry,
|
|
44
|
+
PRIORITY,
|
|
45
|
+
zodTypeIs,
|
|
46
|
+
fieldNameMatches,
|
|
47
|
+
metaMatches,
|
|
48
|
+
hasRefinement,
|
|
49
|
+
editWidgetIs,
|
|
50
|
+
and,
|
|
51
|
+
or
|
|
52
|
+
} from "@zodal/ui";
|
|
53
|
+
function secretRoleIs() {
|
|
54
|
+
return (_field, ctx) => ctx.sensitivity === "secret" ? PRIORITY.OVERRIDE : -1;
|
|
55
|
+
}
|
|
56
|
+
function isStructuredValue() {
|
|
57
|
+
return (field, ctx) => ctx.structured === true || field.zodType === "object" || field.zodType === "array" ? PRIORITY.LIBRARY : -1;
|
|
58
|
+
}
|
|
59
|
+
function isBoolean() {
|
|
60
|
+
return zodTypeIs("boolean");
|
|
61
|
+
}
|
|
62
|
+
function isEnum() {
|
|
63
|
+
return zodTypeIs("enum");
|
|
64
|
+
}
|
|
65
|
+
function isNumber() {
|
|
66
|
+
return zodTypeIs("number");
|
|
67
|
+
}
|
|
68
|
+
function isString() {
|
|
69
|
+
return zodTypeIs("string");
|
|
70
|
+
}
|
|
71
|
+
function alwaysMatch(priority = PRIORITY.FALLBACK) {
|
|
72
|
+
return () => priority;
|
|
73
|
+
}
|
|
74
|
+
function createSettingsRendererRegistry() {
|
|
75
|
+
return createRendererRegistry();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/introspect.ts
|
|
79
|
+
import { baseType, getObjectShape, readMeta } from "@zodal/dials-core";
|
|
80
|
+
import { getEnumValues, getNumericBounds, humanizeFieldName } from "@zodal/core";
|
|
81
|
+
function safe(fn) {
|
|
82
|
+
try {
|
|
83
|
+
return fn();
|
|
84
|
+
} catch {
|
|
85
|
+
return void 0;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function uniqueStrings(values) {
|
|
89
|
+
return [...new Set(values)];
|
|
90
|
+
}
|
|
91
|
+
function describeSettings(dials, options = {}) {
|
|
92
|
+
const shape = getObjectShape(dials.schema);
|
|
93
|
+
return dials.keys.map((key) => {
|
|
94
|
+
const field = shape[key];
|
|
95
|
+
const meta = field ? readMeta(field) : {};
|
|
96
|
+
const zodType = field ? baseType(field) : "unknown";
|
|
97
|
+
const sensitivity = dials.sensitivityFor(key);
|
|
98
|
+
const enumValues = field ? safe(() => getEnumValues(field)) : void 0;
|
|
99
|
+
const rawBounds = field ? safe(() => getNumericBounds(field)) : void 0;
|
|
100
|
+
const min = rawBounds != null && Number.isFinite(rawBounds.min) ? rawBounds.min : void 0;
|
|
101
|
+
const max = rawBounds != null && Number.isFinite(rawBounds.max) ? rawBounds.max : void 0;
|
|
102
|
+
const bounds = min !== void 0 || max !== void 0 ? { ...min !== void 0 ? { min } : {}, ...max !== void 0 ? { max } : {} } : void 0;
|
|
103
|
+
const metaFacets = Array.isArray(meta.facets) ? meta.facets.map((f) => String(f)) : [];
|
|
104
|
+
const facets = uniqueStrings([...metaFacets, ...options.facets?.[key] ?? []]);
|
|
105
|
+
const advanced = facets.includes("advanced") || meta.advanced === true;
|
|
106
|
+
const required = field ? !field.safeParse(void 0).success && dials.defaults[key] === void 0 : false;
|
|
107
|
+
return {
|
|
108
|
+
key,
|
|
109
|
+
label: typeof meta.title === "string" ? meta.title : humanizeFieldName(key),
|
|
110
|
+
description: typeof meta.description === "string" ? meta.description : void 0,
|
|
111
|
+
widget: widgetKindFor({ zodType, sensitivity, bounds, enumValues, metaWidget: meta.editWidget }),
|
|
112
|
+
zodType,
|
|
113
|
+
required,
|
|
114
|
+
readOnly: meta.readOnly === true || meta.editable === false,
|
|
115
|
+
hidden: meta.hidden === true,
|
|
116
|
+
sensitivity,
|
|
117
|
+
mergeStrategy: dials.mergeStrategyFor(key),
|
|
118
|
+
// Never put a secret's plaintext default into the headless config — a renderer that prefills
|
|
119
|
+
// from `defaultValue` would expose it. Secrets carry no default here (the secret backend owns it).
|
|
120
|
+
defaultValue: sensitivity === "secret" ? void 0 : dials.defaults[key],
|
|
121
|
+
enumValues: enumValues && enumValues.length > 0 ? enumValues : void 0,
|
|
122
|
+
bounds,
|
|
123
|
+
facets,
|
|
124
|
+
advanced,
|
|
125
|
+
order: typeof meta.order === "number" ? meta.order : void 0,
|
|
126
|
+
isStructured: zodType === "object" || zodType === "array"
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/facets.ts
|
|
132
|
+
function humanize(id) {
|
|
133
|
+
return id.replace(/^@/, "").replace(/[._-]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
134
|
+
}
|
|
135
|
+
function computedGroups(fields, result) {
|
|
136
|
+
const out = [];
|
|
137
|
+
const add = (id, title, keys, order) => {
|
|
138
|
+
if (keys.length > 0) out.push({ id, title, order, settingKeys: keys, computed: true });
|
|
139
|
+
};
|
|
140
|
+
const fieldKeys = new Set(fields.map((f) => f.key));
|
|
141
|
+
add("@secret", "Secrets", fields.filter((f) => f.sensitivity === "secret").map((f) => f.key), 2e3);
|
|
142
|
+
add("@advanced", "Advanced", fields.filter((f) => f.advanced).map((f) => f.key), 2001);
|
|
143
|
+
if (result) {
|
|
144
|
+
const modified = Object.keys(result.provenance).filter((k) => fieldKeys.has(k) && result.provenance[k].winningScope !== "default");
|
|
145
|
+
const managed = Object.keys(result.provenance).filter((k) => fieldKeys.has(k) && result.provenance[k].managed);
|
|
146
|
+
add("@modified", "Modified", modified, 2002);
|
|
147
|
+
add("@managed", "Managed by policy", managed, 2003);
|
|
148
|
+
}
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
function toGroups(fields, result, options = {}) {
|
|
152
|
+
const declared = new Map((options.facetDefs ?? []).map((f) => [f.id, f]));
|
|
153
|
+
const forward = /* @__PURE__ */ new Map();
|
|
154
|
+
for (const field of fields) {
|
|
155
|
+
for (const facet of field.facets) {
|
|
156
|
+
const list = forward.get(facet);
|
|
157
|
+
if (list) list.push(field.key);
|
|
158
|
+
else forward.set(facet, [field.key]);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const groups = [];
|
|
162
|
+
for (const [facet, keys] of forward) {
|
|
163
|
+
const def = declared.get(facet);
|
|
164
|
+
groups.push({ id: facet, title: def?.title ?? humanize(facet), order: def?.order ?? 100, settingKeys: keys, computed: false });
|
|
165
|
+
}
|
|
166
|
+
const seenIds = new Set(groups.map((g) => g.id));
|
|
167
|
+
const ungrouped = fields.filter((f) => f.facets.length === 0).map((f) => f.key);
|
|
168
|
+
if (ungrouped.length > 0 && !seenIds.has("_ungrouped")) {
|
|
169
|
+
groups.push({ id: "_ungrouped", title: options.ungroupedTitle ?? "Other", order: 1e3, settingKeys: ungrouped, computed: false });
|
|
170
|
+
seenIds.add("_ungrouped");
|
|
171
|
+
}
|
|
172
|
+
if (options.computedGroups !== false) {
|
|
173
|
+
for (const group of computedGroups(fields, result)) {
|
|
174
|
+
if (!seenIds.has(group.id)) {
|
|
175
|
+
groups.push(group);
|
|
176
|
+
seenIds.add(group.id);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return groups.sort((a, b) => a.order - b.order || a.title.localeCompare(b.title));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/search.ts
|
|
184
|
+
function toIndexableSettings(fields) {
|
|
185
|
+
return fields.map((f) => ({
|
|
186
|
+
key: f.key,
|
|
187
|
+
title: f.label,
|
|
188
|
+
description: f.description ?? "",
|
|
189
|
+
enumLabels: f.enumValues ?? [],
|
|
190
|
+
facets: f.facets,
|
|
191
|
+
keywords: [f.key, ...f.key.split(/[._\-/]/).filter(Boolean)]
|
|
192
|
+
}));
|
|
193
|
+
}
|
|
194
|
+
function createSubstringSearchProvider(settings, options = {}) {
|
|
195
|
+
const fields = options.fields ?? ["title", "description", "enumLabels", "facets", "keywords"];
|
|
196
|
+
const haystacks = settings.map((s) => ({
|
|
197
|
+
key: s.key,
|
|
198
|
+
parts: {
|
|
199
|
+
title: s.title.toLowerCase(),
|
|
200
|
+
description: s.description.toLowerCase(),
|
|
201
|
+
enumLabels: s.enumLabels.join(" ").toLowerCase(),
|
|
202
|
+
facets: s.facets.join(" ").toLowerCase(),
|
|
203
|
+
keywords: s.keywords.join(" ").toLowerCase()
|
|
204
|
+
}
|
|
205
|
+
}));
|
|
206
|
+
return {
|
|
207
|
+
search(query) {
|
|
208
|
+
const q = query.trim().toLowerCase();
|
|
209
|
+
if (!q) return settings.map((s) => s.key);
|
|
210
|
+
const scored = [];
|
|
211
|
+
for (const h of haystacks) {
|
|
212
|
+
let score = 0;
|
|
213
|
+
for (const f of fields) {
|
|
214
|
+
if (h.parts[f].includes(q)) score += f === "title" || f === "keywords" ? 3 : 1;
|
|
215
|
+
}
|
|
216
|
+
if (score > 0) scored.push({ key: h.key, score });
|
|
217
|
+
}
|
|
218
|
+
scored.sort((a, b) => b.score - a.score);
|
|
219
|
+
return scored.map((s) => s.key);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function parseScopedQuery(query) {
|
|
224
|
+
const tokens = query.split(/\s+/).filter(Boolean);
|
|
225
|
+
const filters = [];
|
|
226
|
+
const text = [];
|
|
227
|
+
for (const tok of tokens) {
|
|
228
|
+
const m = /^@([a-z]+)(?::(.+))?$/i.exec(tok);
|
|
229
|
+
if (!m) {
|
|
230
|
+
text.push(tok);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const name = m[1].toLowerCase();
|
|
234
|
+
const value = m[2];
|
|
235
|
+
if (name === "modified") filters.push({ type: "modified" });
|
|
236
|
+
else if (name === "managed") filters.push({ type: "managed" });
|
|
237
|
+
else if (name === "secret") filters.push({ type: "secret" });
|
|
238
|
+
else if (name === "advanced") filters.push({ type: "advanced" });
|
|
239
|
+
else if (name === "facet" && value) filters.push({ type: "facet", value });
|
|
240
|
+
else if (name === "scope" && value) filters.push({ type: "scope", value });
|
|
241
|
+
else text.push(tok);
|
|
242
|
+
}
|
|
243
|
+
return { filters, text: text.join(" ") };
|
|
244
|
+
}
|
|
245
|
+
function applyScopedFilters(keys, filters, context) {
|
|
246
|
+
if (filters.length === 0) return keys;
|
|
247
|
+
const byKey = new Map(context.fields.map((f) => [f.key, f]));
|
|
248
|
+
const prov = context.result?.provenance ?? {};
|
|
249
|
+
return keys.filter(
|
|
250
|
+
(key) => filters.every((filter) => {
|
|
251
|
+
const f = byKey.get(key);
|
|
252
|
+
switch (filter.type) {
|
|
253
|
+
case "secret":
|
|
254
|
+
return f?.sensitivity === "secret";
|
|
255
|
+
case "advanced":
|
|
256
|
+
return f?.advanced === true;
|
|
257
|
+
case "facet":
|
|
258
|
+
return f?.facets.includes(filter.value) ?? false;
|
|
259
|
+
case "modified":
|
|
260
|
+
return prov[key] ? prov[key].winningScope !== "default" : false;
|
|
261
|
+
case "managed":
|
|
262
|
+
return prov[key]?.managed === true;
|
|
263
|
+
case "scope":
|
|
264
|
+
return prov[key]?.winningScope === filter.value;
|
|
265
|
+
default:
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
})
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
function searchSettings(query, provider, context) {
|
|
272
|
+
const { filters, text } = parseScopedQuery(query);
|
|
273
|
+
const allKeys = context.fields.map((f) => f.key);
|
|
274
|
+
const filtered = new Set(applyScopedFilters(allKeys, filters, context));
|
|
275
|
+
const ranked = text ? provider.search(text) : allKeys;
|
|
276
|
+
return ranked.filter((k) => filtered.has(k));
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// src/lifecycle.ts
|
|
280
|
+
import {
|
|
281
|
+
UNSET,
|
|
282
|
+
diffJsonPatch,
|
|
283
|
+
invertJsonPatch,
|
|
284
|
+
applyJsonPatch,
|
|
285
|
+
serializeLayer,
|
|
286
|
+
deserializeLayer
|
|
287
|
+
} from "@zodal/dials-core";
|
|
288
|
+
function normalize(value) {
|
|
289
|
+
if (value === UNSET) return "\0unset";
|
|
290
|
+
if (value === void 0) return "\0undefined";
|
|
291
|
+
return JSON.stringify(value);
|
|
292
|
+
}
|
|
293
|
+
function dirtyKeys(current, baseline) {
|
|
294
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(current), ...Object.keys(baseline)]);
|
|
295
|
+
const out = [];
|
|
296
|
+
for (const key of keys) {
|
|
297
|
+
const inCurrent = Object.prototype.hasOwnProperty.call(current, key);
|
|
298
|
+
const inBaseline = Object.prototype.hasOwnProperty.call(baseline, key);
|
|
299
|
+
if (inCurrent !== inBaseline || normalize(current[key]) !== normalize(baseline[key])) out.push(key);
|
|
300
|
+
}
|
|
301
|
+
return out;
|
|
302
|
+
}
|
|
303
|
+
function isDirty(current, baseline) {
|
|
304
|
+
return dirtyKeys(current, baseline).length > 0;
|
|
305
|
+
}
|
|
306
|
+
function resetToDefault(layer, key) {
|
|
307
|
+
const out = { ...layer };
|
|
308
|
+
delete out[key];
|
|
309
|
+
return out;
|
|
310
|
+
}
|
|
311
|
+
function unsetKey(layer, key) {
|
|
312
|
+
return { ...layer, [key]: UNSET };
|
|
313
|
+
}
|
|
314
|
+
function recordLayerChange(before, after) {
|
|
315
|
+
const b = serializeLayer(before);
|
|
316
|
+
const a = serializeLayer(after);
|
|
317
|
+
const forward = diffJsonPatch(b, a);
|
|
318
|
+
return { forward, inverse: invertJsonPatch(forward, b) };
|
|
319
|
+
}
|
|
320
|
+
function applyLayerPatch(layer, ops) {
|
|
321
|
+
const serialized = applyJsonPatch(serializeLayer(layer), ops);
|
|
322
|
+
return deserializeLayer(serialized);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// src/form.ts
|
|
326
|
+
import { isSecretRef, makeSecretRef } from "@zodal/dials-core";
|
|
327
|
+
function toSettingsForm(dials, options = {}) {
|
|
328
|
+
const all = describeSettings(dials, { facets: options.facets });
|
|
329
|
+
const visible = options.includeHidden ? all : all.filter((f) => !f.hidden);
|
|
330
|
+
const fields = [...visible].sort((a, b) => (a.order ?? 1e3) - (b.order ?? 1e3) || a.label.localeCompare(b.label));
|
|
331
|
+
const groups = toGroups(fields, options.result, {
|
|
332
|
+
facetDefs: options.facetDefs,
|
|
333
|
+
computedGroups: options.computedGroups,
|
|
334
|
+
ungroupedTitle: options.ungroupedTitle
|
|
335
|
+
});
|
|
336
|
+
return { fields, groups };
|
|
337
|
+
}
|
|
338
|
+
function toFieldStates(fields, result, dirty = []) {
|
|
339
|
+
const dirtySet = new Set(dirty);
|
|
340
|
+
const states = {};
|
|
341
|
+
for (const field of fields) {
|
|
342
|
+
const prov = result.provenance[field.key];
|
|
343
|
+
const raw = result.effective[field.key];
|
|
344
|
+
const value = field.sensitivity === "secret" && !isSecretRef(raw) ? makeSecretRef(field.key, raw !== void 0 && raw !== null && raw !== "") : raw;
|
|
345
|
+
states[field.key] = {
|
|
346
|
+
value,
|
|
347
|
+
source: prov?.winningScope,
|
|
348
|
+
managed: prov?.managed ?? false,
|
|
349
|
+
shadowed: (prov?.shadowed.length ?? 0) > 0,
|
|
350
|
+
dirty: dirtySet.has(field.key)
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
return states;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// src/store.ts
|
|
357
|
+
import { UNSET as UNSET2, maskEffectiveResult } from "@zodal/dials-core";
|
|
358
|
+
function createSettingsStore(dials, options = {}) {
|
|
359
|
+
const scopeId = options.scope ?? "user";
|
|
360
|
+
const maskSecrets = options.maskSecrets !== false;
|
|
361
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
362
|
+
let scopes = [...options.scopes ?? []];
|
|
363
|
+
let layer = { ...options.layer ?? {} };
|
|
364
|
+
let baseline = { ...layer };
|
|
365
|
+
let state;
|
|
366
|
+
const recompute = () => {
|
|
367
|
+
const raw = dials.resolve([...scopes, { scope: scopeId, layer }]);
|
|
368
|
+
const validation = dials.validate(raw.effective);
|
|
369
|
+
const shown = maskSecrets ? maskEffectiveResult(raw, dials.sensitivityFor) : raw;
|
|
370
|
+
state = {
|
|
371
|
+
effective: shown.effective,
|
|
372
|
+
provenance: shown.provenance,
|
|
373
|
+
conflicts: shown.conflicts,
|
|
374
|
+
layer: { ...layer },
|
|
375
|
+
scopes: [...scopes],
|
|
376
|
+
dirty: dirtyKeys(layer, baseline),
|
|
377
|
+
validation
|
|
378
|
+
};
|
|
379
|
+
for (const listener of [...listeners]) {
|
|
380
|
+
try {
|
|
381
|
+
listener();
|
|
382
|
+
} catch (error) {
|
|
383
|
+
options.onListenerError?.(error);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
recompute();
|
|
388
|
+
const mutate = (next) => {
|
|
389
|
+
layer = next;
|
|
390
|
+
recompute();
|
|
391
|
+
};
|
|
392
|
+
return {
|
|
393
|
+
getState: () => state,
|
|
394
|
+
subscribe(listener) {
|
|
395
|
+
listeners.add(listener);
|
|
396
|
+
return () => listeners.delete(listener);
|
|
397
|
+
},
|
|
398
|
+
set: (key, value) => {
|
|
399
|
+
if (Object.prototype.hasOwnProperty.call(layer, key) && Object.is(layer[key], value)) return;
|
|
400
|
+
mutate({ ...layer, [key]: value });
|
|
401
|
+
},
|
|
402
|
+
unset: (key) => mutate({ ...layer, [key]: UNSET2 }),
|
|
403
|
+
reset: (key) => {
|
|
404
|
+
const next = { ...layer };
|
|
405
|
+
delete next[key];
|
|
406
|
+
mutate(next);
|
|
407
|
+
},
|
|
408
|
+
setLayer: (next) => mutate({ ...next }),
|
|
409
|
+
setScopes: (next) => {
|
|
410
|
+
scopes = [...next];
|
|
411
|
+
recompute();
|
|
412
|
+
},
|
|
413
|
+
markSaved: () => {
|
|
414
|
+
baseline = { ...layer };
|
|
415
|
+
recompute();
|
|
416
|
+
},
|
|
417
|
+
get: (key) => state.effective[key],
|
|
418
|
+
explain: (key) => state.provenance[key]
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// src/profiles.ts
|
|
423
|
+
import { deserializeLayer as deserializeLayer2, redactSecretsFromLayer, serializeLayer as serializeLayer2 } from "@zodal/dials-core";
|
|
424
|
+
function createMemoryProfileStorage(initial = []) {
|
|
425
|
+
let profiles = initial.map((p) => ({ ...p }));
|
|
426
|
+
return {
|
|
427
|
+
read: () => Promise.resolve(profiles.map((p) => ({ ...p }))),
|
|
428
|
+
write: (next) => {
|
|
429
|
+
profiles = next.map((p) => ({ ...p }));
|
|
430
|
+
return Promise.resolve();
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
function createLocalStorageProfileStorage(storageKey = "zodal-dials.profiles") {
|
|
435
|
+
const ls = globalThis.localStorage;
|
|
436
|
+
if (!ls) throw new Error("createLocalStorageProfileStorage: localStorage is not available");
|
|
437
|
+
return {
|
|
438
|
+
read: () => {
|
|
439
|
+
const raw = ls.getItem(storageKey);
|
|
440
|
+
if (!raw) return Promise.resolve([]);
|
|
441
|
+
try {
|
|
442
|
+
const parsed = JSON.parse(raw);
|
|
443
|
+
return Promise.resolve(Array.isArray(parsed) ? parsed : []);
|
|
444
|
+
} catch {
|
|
445
|
+
return Promise.resolve([]);
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
write: (profiles) => {
|
|
449
|
+
ls.setItem(storageKey, JSON.stringify(profiles));
|
|
450
|
+
return Promise.resolve();
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
function createProfileStore(storage, options = {}) {
|
|
455
|
+
const findIndex = (profiles, name) => profiles.findIndex((p) => p.name === name);
|
|
456
|
+
let queue = Promise.resolve();
|
|
457
|
+
const enqueue = (run) => {
|
|
458
|
+
const result = queue.then(run, run);
|
|
459
|
+
queue = result.then(
|
|
460
|
+
() => void 0,
|
|
461
|
+
() => void 0
|
|
462
|
+
);
|
|
463
|
+
return result;
|
|
464
|
+
};
|
|
465
|
+
return {
|
|
466
|
+
async list() {
|
|
467
|
+
const profiles = await storage.read();
|
|
468
|
+
return profiles.map(({ name, meta }) => ({ name, ...meta ? { meta } : {} }));
|
|
469
|
+
},
|
|
470
|
+
save(name, layer, meta) {
|
|
471
|
+
const trimmed = name.trim();
|
|
472
|
+
if (!trimmed) return Promise.reject(new Error("profile name cannot be empty"));
|
|
473
|
+
return enqueue(async () => {
|
|
474
|
+
const profiles = await storage.read();
|
|
475
|
+
let serialized = serializeLayer2(layer);
|
|
476
|
+
if (options.sensitivityFor) serialized = redactSecretsFromLayer(serialized, options.sensitivityFor);
|
|
477
|
+
const entry = { name: trimmed, layer: serialized, ...meta ? { meta } : {} };
|
|
478
|
+
const index = findIndex(profiles, trimmed);
|
|
479
|
+
if (index >= 0) profiles[index] = entry;
|
|
480
|
+
else profiles.push(entry);
|
|
481
|
+
await storage.write(profiles);
|
|
482
|
+
});
|
|
483
|
+
},
|
|
484
|
+
async load(name) {
|
|
485
|
+
const profiles = await storage.read();
|
|
486
|
+
const found = profiles[findIndex(profiles, name)];
|
|
487
|
+
return found ? deserializeLayer2(found.layer) : void 0;
|
|
488
|
+
},
|
|
489
|
+
remove(name) {
|
|
490
|
+
return enqueue(async () => {
|
|
491
|
+
const profiles = await storage.read();
|
|
492
|
+
await storage.write(profiles.filter((p) => p.name !== name));
|
|
493
|
+
});
|
|
494
|
+
},
|
|
495
|
+
rename(from, to) {
|
|
496
|
+
if (from === to) return Promise.resolve();
|
|
497
|
+
return enqueue(async () => {
|
|
498
|
+
const profiles = await storage.read();
|
|
499
|
+
if (findIndex(profiles, to) >= 0) throw new Error(`profile "${to}" already exists`);
|
|
500
|
+
const index = findIndex(profiles, from);
|
|
501
|
+
if (index < 0) return;
|
|
502
|
+
profiles[index] = { ...profiles[index], name: to };
|
|
503
|
+
await storage.write(profiles);
|
|
504
|
+
});
|
|
505
|
+
},
|
|
506
|
+
async has(name) {
|
|
507
|
+
const profiles = await storage.read();
|
|
508
|
+
return findIndex(profiles, name) >= 0;
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
export {
|
|
513
|
+
PRIORITY,
|
|
514
|
+
alwaysMatch,
|
|
515
|
+
and,
|
|
516
|
+
applyLayerPatch,
|
|
517
|
+
applyScopedFilters,
|
|
518
|
+
createLocalStorageProfileStorage,
|
|
519
|
+
createMemoryProfileStorage,
|
|
520
|
+
createProfileStore,
|
|
521
|
+
createRendererRegistry,
|
|
522
|
+
createSettingsRendererRegistry,
|
|
523
|
+
createSettingsStore,
|
|
524
|
+
createSubstringSearchProvider,
|
|
525
|
+
describeSettings,
|
|
526
|
+
dirtyKeys,
|
|
527
|
+
editWidgetIs,
|
|
528
|
+
fieldNameMatches,
|
|
529
|
+
hasRefinement,
|
|
530
|
+
isBoolean,
|
|
531
|
+
isDirty,
|
|
532
|
+
isEnum,
|
|
533
|
+
isNumber,
|
|
534
|
+
isString,
|
|
535
|
+
isStructuredValue,
|
|
536
|
+
metaMatches,
|
|
537
|
+
or,
|
|
538
|
+
parseScopedQuery,
|
|
539
|
+
recordLayerChange,
|
|
540
|
+
resetToDefault,
|
|
541
|
+
searchSettings,
|
|
542
|
+
secretRoleIs,
|
|
543
|
+
toFieldStates,
|
|
544
|
+
toGroups,
|
|
545
|
+
toIndexableSettings,
|
|
546
|
+
toSettingsForm,
|
|
547
|
+
unsetKey,
|
|
548
|
+
widgetKindFor,
|
|
549
|
+
zodTypeIs
|
|
550
|
+
};
|
|
551
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/widgets.ts","../src/registry.ts","../src/introspect.ts","../src/facets.ts","../src/search.ts","../src/lifecycle.ts","../src/form.ts","../src/store.ts","../src/profiles.ts"],"sourcesContent":["/**\n * Type -> widget classification for settings. Pure. Distinguishes a scalar leaf (switch/select/\n * slider/text/…) from an irreducible nested value (object/array), with `rawJson` as the terminal\n * fallback for anything unhandled — so coverage is total and degradation is honest. A `secret`\n * setting always maps to the `secret` widget regardless of its underlying type.\n */\n\nimport type { Sensitivity } from '@zodal/dials-core';\nimport type { WidgetKind } from './types.js';\n\nexport interface WidgetInput {\n zodType: string;\n sensitivity: Sensitivity;\n bounds?: { min?: number; max?: number };\n enumValues?: string[];\n /** An explicit `.meta({ editWidget })` override (wins over inference). */\n metaWidget?: unknown;\n}\n\nconst KNOWN_WIDGETS = new Set<WidgetKind>([\n 'switch', 'select', 'radio', 'slider', 'number', 'text', 'textarea',\n 'secret', 'color', 'date', 'path', 'object', 'array', 'rawJson',\n]);\n\n/** Choose the widget kind for a setting. */\nexport function widgetKindFor(input: WidgetInput): WidgetKind {\n if (typeof input.metaWidget === 'string' && KNOWN_WIDGETS.has(input.metaWidget as WidgetKind)) {\n return input.metaWidget as WidgetKind;\n }\n if (input.sensitivity === 'secret') return 'secret';\n switch (input.zodType) {\n case 'boolean':\n return 'switch';\n case 'enum':\n return input.enumValues && input.enumValues.length > 0 && input.enumValues.length <= 4 ? 'radio' : 'select';\n case 'number':\n return input.bounds && input.bounds.min !== undefined && input.bounds.max !== undefined ? 'slider' : 'number';\n case 'string':\n return 'text';\n case 'object':\n return 'object';\n case 'array':\n return 'array';\n default:\n return 'rawJson';\n }\n}\n","/**\n * The settings renderer registry — a thin specialization of zodal's capability-ranked\n * `RendererRegistry`. Concrete renderer packages (vanilla, shadcn, …) register `(tester, renderer)`\n * entries against the same open-closed API; selection is by PRIORITY band + composable testers, with\n * a terminal `alwaysMatch` entry (the rawJson fallback) guaranteeing total coverage and honest\n * degradation. Settings-specific testers read `sensitivity`/`structured` from the render context.\n */\n\nimport {\n createRendererRegistry,\n PRIORITY,\n zodTypeIs,\n fieldNameMatches,\n metaMatches,\n hasRefinement,\n editWidgetIs,\n and,\n or,\n} from '@zodal/ui';\nimport type { RendererRegistry, RendererTester } from '@zodal/ui';\n\n// Re-export the zodal renderer-registry primitives so renderer authors import everything from one place.\nexport {\n createRendererRegistry,\n PRIORITY,\n zodTypeIs,\n fieldNameMatches,\n metaMatches,\n hasRefinement,\n editWidgetIs,\n and,\n or,\n};\nexport type { RendererRegistry, RendererTester };\n\n/** Match a secret setting (its `sensitivity` is supplied via the render context). High priority so a\n * secret is always rendered with the masked widget regardless of its underlying type. */\nexport function secretRoleIs(): RendererTester {\n return (_field, ctx) => (ctx.sensitivity === 'secret' ? PRIORITY.OVERRIDE : -1);\n}\n\n/** Match an irreducible nested value (`structured: true` in context, or an object/array Zod type). */\nexport function isStructuredValue(): RendererTester {\n return (field, ctx) =>\n ctx.structured === true || field.zodType === 'object' || field.zodType === 'array' ? PRIORITY.LIBRARY : -1;\n}\n\nexport function isBoolean(): RendererTester {\n return zodTypeIs('boolean');\n}\nexport function isEnum(): RendererTester {\n return zodTypeIs('enum');\n}\nexport function isNumber(): RendererTester {\n return zodTypeIs('number');\n}\nexport function isString(): RendererTester {\n return zodTypeIs('string');\n}\n\n/**\n * Terminal renderer tester: ALWAYS matches, at the given priority (default FALLBACK). Pairing this\n * with a raw-JSON renderer guarantees every setting resolves to something (the honest-degradation\n * fallback).\n */\nexport function alwaysMatch(priority: number = PRIORITY.FALLBACK): RendererTester {\n return () => priority;\n}\n\n/** Create a settings renderer registry (a zodal `RendererRegistry`). Concrete renderer packages\n * populate it; remember to register a terminal `alwaysMatch` rawJson renderer for full coverage. */\nexport function createSettingsRendererRegistry<TComponent = unknown>(): RendererRegistry<TComponent> {\n return createRendererRegistry<TComponent>();\n}\n","/**\n * Build the static, value-independent `SettingFieldConfig[]` from a `DialsDefinition`, combining the\n * schema (via dials-core introspection helpers + `@zodal/core` enum/bounds helpers), the dials\n * classification (sensitivity, merge strategy, defaults), and `.meta()` annotations + an optional\n * external facet-assignment map. Pure.\n */\n\nimport type { z } from 'zod';\nimport { baseType, getObjectShape, readMeta } from '@zodal/dials-core';\nimport type { DialsDefinition } from '@zodal/dials-core';\nimport { getEnumValues, getNumericBounds, humanizeFieldName } from '@zodal/core';\nimport { widgetKindFor } from './widgets.js';\nimport type { SettingFieldConfig } from './types.js';\n\nexport interface DescribeOptions {\n /** Extra facet membership, merged with each setting's `.meta({ facets })`: key -> facet ids. */\n facets?: Record<string, string[]>;\n}\n\nfunction safe<T>(fn: () => T): T | undefined {\n try {\n return fn();\n } catch {\n return undefined;\n }\n}\n\nfunction uniqueStrings(values: string[]): string[] {\n return [...new Set(values)];\n}\n\n/** Describe every setting in a dials definition as a headless field config. */\nexport function describeSettings<T extends z.ZodObject<z.ZodRawShape>>(\n dials: DialsDefinition<T>,\n options: DescribeOptions = {},\n): SettingFieldConfig[] {\n const shape = getObjectShape(dials.schema);\n return dials.keys.map((key) => {\n const field = shape[key];\n const meta = field ? readMeta(field) : {};\n const zodType = field ? baseType(field) : 'unknown';\n const sensitivity = dials.sensitivityFor(key);\n\n const enumValues = field ? safe(() => getEnumValues(field) as unknown as string[]) : undefined;\n // getNumericBounds reports an absent bound as a non-finite value (±Infinity, or null in some\n // versions). Keep only FINITE bounds, so an unbounded / one-sided number is not mistaken for a\n // fully-bounded one (which would wrongly pick a slider).\n const rawBounds = field ? safe(() => getNumericBounds(field) as unknown as { min?: number | null; max?: number | null }) : undefined;\n const min = rawBounds != null && Number.isFinite(rawBounds.min) ? (rawBounds.min as number) : undefined;\n const max = rawBounds != null && Number.isFinite(rawBounds.max) ? (rawBounds.max as number) : undefined;\n const bounds = min !== undefined || max !== undefined ? { ...(min !== undefined ? { min } : {}), ...(max !== undefined ? { max } : {}) } : undefined;\n\n const metaFacets = Array.isArray(meta.facets) ? meta.facets.map((f) => String(f)) : [];\n const facets = uniqueStrings([...metaFacets, ...(options.facets?.[key] ?? [])]);\n const advanced = facets.includes('advanced') || meta.advanced === true;\n\n const required = field ? !field.safeParse(undefined).success && dials.defaults[key] === undefined : false;\n\n return {\n key,\n label: typeof meta.title === 'string' ? meta.title : humanizeFieldName(key),\n description: typeof meta.description === 'string' ? meta.description : undefined,\n widget: widgetKindFor({ zodType, sensitivity, bounds, enumValues, metaWidget: meta.editWidget }),\n zodType,\n required,\n readOnly: meta.readOnly === true || meta.editable === false,\n hidden: meta.hidden === true,\n sensitivity,\n mergeStrategy: dials.mergeStrategyFor(key),\n // Never put a secret's plaintext default into the headless config — a renderer that prefills\n // from `defaultValue` would expose it. Secrets carry no default here (the secret backend owns it).\n defaultValue: sensitivity === 'secret' ? undefined : dials.defaults[key],\n enumValues: enumValues && enumValues.length > 0 ? enumValues : undefined,\n bounds,\n facets,\n advanced,\n order: typeof meta.order === 'number' ? meta.order : undefined,\n isStructured: zodType === 'object' || zodType === 'array',\n };\n });\n}\n","/**\n * Faceted organization: project the flat settings surface into groups via a separate grouping layer\n * (facets are canonical; a tree is one projection). The forward index (facet -> keys) drives panels,\n * accordions, and bulk-operation scopes — the same model serves both the open-a-panel and the\n * expand-in-place gesture (the gesture is the renderer's choice, never encoded here). Computed\n * (\"smart\") groups are predicates over field config + resolution state. Pure.\n */\n\nimport type { EffectiveResult, SettingKey } from '@zodal/dials-core';\nimport type { SettingFieldConfig, SettingsGroup } from './types.js';\n\nexport interface FacetDef {\n id: string;\n title?: string;\n order?: number;\n}\n\nexport interface GroupingOptions {\n /** Declared facet titles/order. Facets used by fields but not declared get a humanized default. */\n facetDefs?: FacetDef[];\n /** Include computed/\"smart\" groups (@secret, @advanced, @modified, @managed). Default: true. */\n computedGroups?: boolean;\n /** Title for the catch-all group of settings with no facet. Default: 'Other'. */\n ungroupedTitle?: string;\n}\n\nfunction humanize(id: string): string {\n return id.replace(/^@/, '').replace(/[._-]+/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\nfunction computedGroups(fields: SettingFieldConfig[], result?: EffectiveResult): SettingsGroup[] {\n const out: SettingsGroup[] = [];\n const add = (id: string, title: string, keys: SettingKey[], order: number): void => {\n if (keys.length > 0) out.push({ id, title, order, settingKeys: keys, computed: true });\n };\n const fieldKeys = new Set(fields.map((f) => f.key));\n add('@secret', 'Secrets', fields.filter((f) => f.sensitivity === 'secret').map((f) => f.key), 2000);\n add('@advanced', 'Advanced', fields.filter((f) => f.advanced).map((f) => f.key), 2001);\n if (result) {\n const modified = Object.keys(result.provenance).filter((k) => fieldKeys.has(k) && result.provenance[k].winningScope !== 'default');\n const managed = Object.keys(result.provenance).filter((k) => fieldKeys.has(k) && result.provenance[k].managed);\n add('@modified', 'Modified', modified, 2002);\n add('@managed', 'Managed by policy', managed, 2003);\n }\n return out;\n}\n\n/** Project field configs (in their incoming order) into facet groups, sorted by order then title. */\nexport function toGroups(\n fields: SettingFieldConfig[],\n result?: EffectiveResult,\n options: GroupingOptions = {},\n): SettingsGroup[] {\n const declared = new Map((options.facetDefs ?? []).map((f) => [f.id, f]));\n const forward = new Map<string, SettingKey[]>();\n for (const field of fields) {\n for (const facet of field.facets) {\n const list = forward.get(facet);\n if (list) list.push(field.key);\n else forward.set(facet, [field.key]);\n }\n }\n\n const groups: SettingsGroup[] = [];\n for (const [facet, keys] of forward) {\n const def = declared.get(facet);\n groups.push({ id: facet, title: def?.title ?? humanize(facet), order: def?.order ?? 100, settingKeys: keys, computed: false });\n }\n\n // Group ids must be unique (renderers key off them). A declared facet that collides with the\n // reserved catch-all (`_ungrouped`) or a computed id (`@…`) takes precedence; the reserved group\n // is then skipped rather than duplicated.\n const seenIds = new Set(groups.map((g) => g.id));\n const ungrouped = fields.filter((f) => f.facets.length === 0).map((f) => f.key);\n if (ungrouped.length > 0 && !seenIds.has('_ungrouped')) {\n groups.push({ id: '_ungrouped', title: options.ungroupedTitle ?? 'Other', order: 1000, settingKeys: ungrouped, computed: false });\n seenIds.add('_ungrouped');\n }\n\n if (options.computedGroups !== false) {\n for (const group of computedGroups(fields, result)) {\n if (!seenIds.has(group.id)) {\n groups.push(group);\n seenIds.add(group.id);\n }\n }\n }\n\n return groups.sort((a, b) => a.order - b.order || a.title.localeCompare(b.title));\n}\n","/**\n * Search over the settings surface: a declared, engine-agnostic `IndexableSetting[]` projection, a\n * pluggable `SearchProvider` (zero-dependency substring default; richer engines like MiniSearch or\n * a semantic provider plug in behind the same interface), and an engine-independent scoped `@`-filter\n * parser (`@modified`/`@managed`/`@secret`/`@advanced`/`@facet:<id>`/`@scope:<id>`) that narrows by\n * effective-value/provenance state BEFORE free text reaches the provider. Pure.\n */\n\nimport type { EffectiveResult, SettingKey } from '@zodal/dials-core';\nimport type { IndexableSetting, SearchProvider, SettingFieldConfig } from './types.js';\n\n/** Project field configs into the engine-agnostic indexable surface. */\nexport function toIndexableSettings(fields: SettingFieldConfig[]): IndexableSetting[] {\n return fields.map((f) => ({\n key: f.key,\n title: f.label,\n description: f.description ?? '',\n enumLabels: f.enumValues ?? [],\n facets: f.facets,\n keywords: [f.key, ...f.key.split(/[._\\-/]/).filter(Boolean)],\n }));\n}\n\n/** Which indexable text fields the substring provider searches (and their relative weight). */\nexport type IndexField = 'title' | 'description' | 'enumLabels' | 'facets' | 'keywords';\n\nexport interface SubstringSearchOptions {\n /** Which fields to match (default: all). */\n fields?: IndexField[];\n}\n\n/**\n * Zero-dependency substring search provider (the default). Lowercased-substring match over the\n * selected fields; title/keyword hits rank above description/facet/enum hits. Empty query returns all.\n */\nexport function createSubstringSearchProvider(\n settings: IndexableSetting[],\n options: SubstringSearchOptions = {},\n): SearchProvider {\n const fields: IndexField[] = options.fields ?? ['title', 'description', 'enumLabels', 'facets', 'keywords'];\n const haystacks = settings.map((s) => ({\n key: s.key,\n parts: {\n title: s.title.toLowerCase(),\n description: s.description.toLowerCase(),\n enumLabels: s.enumLabels.join(' ').toLowerCase(),\n facets: s.facets.join(' ').toLowerCase(),\n keywords: s.keywords.join(' ').toLowerCase(),\n } as Record<IndexField, string>,\n }));\n return {\n search(query: string): SettingKey[] {\n const q = query.trim().toLowerCase();\n if (!q) return settings.map((s) => s.key);\n const scored: Array<{ key: SettingKey; score: number }> = [];\n for (const h of haystacks) {\n let score = 0;\n for (const f of fields) {\n if (h.parts[f].includes(q)) score += f === 'title' || f === 'keywords' ? 3 : 1;\n }\n if (score > 0) scored.push({ key: h.key, score });\n }\n scored.sort((a, b) => b.score - a.score);\n return scored.map((s) => s.key);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Scoped @-filters (engine-independent)\n// ---------------------------------------------------------------------------\n\nexport type ScopeFilter =\n | { type: 'modified' }\n | { type: 'managed' }\n | { type: 'secret' }\n | { type: 'advanced' }\n | { type: 'facet'; value: string }\n | { type: 'scope'; value: string };\n\nexport interface ParsedQuery {\n filters: ScopeFilter[];\n text: string;\n}\n\n/** Parse a settings query into scoped `@`-filters + the residual free text. Unrecognized `@tokens`\n * fall back to free text. */\nexport function parseScopedQuery(query: string): ParsedQuery {\n const tokens = query.split(/\\s+/).filter(Boolean);\n const filters: ScopeFilter[] = [];\n const text: string[] = [];\n for (const tok of tokens) {\n const m = /^@([a-z]+)(?::(.+))?$/i.exec(tok);\n if (!m) {\n text.push(tok);\n continue;\n }\n const name = m[1].toLowerCase();\n const value = m[2];\n if (name === 'modified') filters.push({ type: 'modified' });\n else if (name === 'managed') filters.push({ type: 'managed' });\n else if (name === 'secret') filters.push({ type: 'secret' });\n else if (name === 'advanced') filters.push({ type: 'advanced' });\n else if (name === 'facet' && value) filters.push({ type: 'facet', value });\n else if (name === 'scope' && value) filters.push({ type: 'scope', value });\n else text.push(tok);\n }\n return { filters, text: text.join(' ') };\n}\n\nexport interface FilterContext {\n fields: SettingFieldConfig[];\n result?: EffectiveResult;\n}\n\n/** Apply scoped filters to a key set (keys must satisfy ALL filters), using field config + state. */\nexport function applyScopedFilters(keys: SettingKey[], filters: ScopeFilter[], context: FilterContext): SettingKey[] {\n if (filters.length === 0) return keys;\n const byKey = new Map(context.fields.map((f) => [f.key, f]));\n const prov = context.result?.provenance ?? {};\n return keys.filter((key) =>\n filters.every((filter) => {\n const f = byKey.get(key);\n switch (filter.type) {\n case 'secret':\n return f?.sensitivity === 'secret';\n case 'advanced':\n return f?.advanced === true;\n case 'facet':\n return f?.facets.includes(filter.value) ?? false;\n case 'modified':\n return prov[key] ? prov[key].winningScope !== 'default' : false;\n case 'managed':\n return prov[key]?.managed === true;\n case 'scope':\n return prov[key]?.winningScope === filter.value;\n default:\n return true;\n }\n }),\n );\n}\n\n/** End-to-end: parse a query, apply scoped filters, then free-text search the survivors (keeping the\n * provider's ranking order). */\nexport function searchSettings(query: string, provider: SearchProvider, context: FilterContext): SettingKey[] {\n const { filters, text } = parseScopedQuery(query);\n const allKeys = context.fields.map((f) => f.key);\n const filtered = new Set(applyScopedFilters(allKeys, filters, context));\n const ranked = text ? provider.search(text) : allKeys;\n return ranked.filter((k) => filtered.has(k));\n}\n","/**\n * Change-lifecycle helpers (headless): compute the dirty set, reset a key (remove it so a lower\n * scope re-wins) or explicitly UNSET it, and record/undo edits as reversible RFC 6902 patches over\n * the SERIALIZED layer (so the UNSET sentinel survives — `diffJsonPatch` only sees plain JSON). These\n * are thin wrappers over dials-core; consumers wire them to toasts/guards/undo stacks themselves.\n */\n\nimport {\n UNSET,\n diffJsonPatch,\n invertJsonPatch,\n applyJsonPatch,\n serializeLayer,\n deserializeLayer,\n} from '@zodal/dials-core';\nimport type { JsonPatchOp, Layer, SerializedLayer, SettingKey } from '@zodal/dials-core';\n\nfunction normalize(value: unknown): string {\n // Distinguish UNSET, undefined, null, and JSON values as four separate states. The control-char\n // (\u0000) sentinel prefixes cannot collide with any JSON-serialized value.\n if (value === UNSET) return '\u0000unset';\n if (value === undefined) return '\u0000undefined';\n return JSON.stringify(value);\n}\n\n/** Keys whose value in `current` differs from `baseline` (the dirty set). Absence, UNSET, `undefined`,\n * `null`, and a literal value are all distinguished. */\nexport function dirtyKeys(current: Layer, baseline: Layer): SettingKey[] {\n const keys = new Set<string>([...Object.keys(current), ...Object.keys(baseline)]);\n const out: SettingKey[] = [];\n for (const key of keys) {\n const inCurrent = Object.prototype.hasOwnProperty.call(current, key);\n const inBaseline = Object.prototype.hasOwnProperty.call(baseline, key);\n if (inCurrent !== inBaseline || normalize(current[key]) !== normalize(baseline[key])) out.push(key);\n }\n return out;\n}\n\n/** True if any key is dirty. */\nexport function isDirty(current: Layer, baseline: Layer): boolean {\n return dirtyKeys(current, baseline).length > 0;\n}\n\n/** Reset a key by removing it from the layer (a lower scope re-wins). Returns a new layer. */\nexport function resetToDefault(layer: Layer, key: SettingKey): Layer {\n const out = { ...layer };\n delete out[key];\n return out;\n}\n\n/** Explicitly UNSET a key (records an intentional reset, distinct from never-set). Returns a new layer. */\nexport function unsetKey(layer: Layer, key: SettingKey): Layer {\n return { ...layer, [key]: UNSET };\n}\n\n/** A reversible record of an edit (forward + inverse RFC 6902 patches over the serialized layer). */\nexport interface ChangeRecord {\n forward: JsonPatchOp[];\n inverse: JsonPatchOp[];\n}\n\n/** Record an edit from `before` to `after` as a reversible change over the serialized layers. */\nexport function recordLayerChange(before: Layer, after: Layer): ChangeRecord {\n const b = serializeLayer(before);\n const a = serializeLayer(after);\n const forward = diffJsonPatch(b, a);\n return { forward, inverse: invertJsonPatch(forward, b) };\n}\n\n/** Apply a change's `forward` (redo) or `inverse` (undo) patch to a layer, returning a new layer. */\nexport function applyLayerPatch(layer: Layer, ops: JsonPatchOp[]): Layer {\n const serialized = applyJsonPatch(serializeLayer(layer), ops) as SerializedLayer;\n return deserializeLayer(serialized);\n}\n","/**\n * `toSettingsForm` — the top-level headless generator: describe every (non-hidden) setting as a\n * field config, order them, and project them into facet groups. `toFieldStates` derives the\n * value-dependent state (effective value, provenance source, managed/shadowed flags, dirty) from a\n * cascade resolution — the input to provenance badges, locks, and reset affordances. Pure; emits\n * configuration objects only (never DOM).\n */\n\nimport type { z } from 'zod';\nimport { isSecretRef, makeSecretRef } from '@zodal/dials-core';\nimport type { DialsDefinition, EffectiveResult, SettingKey } from '@zodal/dials-core';\nimport { describeSettings } from './introspect.js';\nimport type { DescribeOptions } from './introspect.js';\nimport { toGroups } from './facets.js';\nimport type { GroupingOptions } from './facets.js';\nimport type { SettingFieldConfig, SettingFieldState, SettingsForm } from './types.js';\n\nexport interface ToSettingsFormOptions extends DescribeOptions, GroupingOptions {\n /** A resolution result, used for computed groups (@modified/@managed). */\n result?: EffectiveResult;\n /** Include hidden settings in the form. Default: false. */\n includeHidden?: boolean;\n}\n\n/** Build the full headless settings form (ordered field configs + facet groups). */\nexport function toSettingsForm<T extends z.ZodObject<z.ZodRawShape>>(\n dials: DialsDefinition<T>,\n options: ToSettingsFormOptions = {},\n): SettingsForm {\n const all = describeSettings(dials, { facets: options.facets });\n const visible = options.includeHidden ? all : all.filter((f) => !f.hidden);\n const fields = [...visible].sort((a, b) => (a.order ?? 1000) - (b.order ?? 1000) || a.label.localeCompare(b.label));\n const groups = toGroups(fields, options.result, {\n facetDefs: options.facetDefs,\n computedGroups: options.computedGroups,\n ungroupedTitle: options.ungroupedTitle,\n });\n return { fields, groups };\n}\n\n/** Derive value-dependent field state (value, provenance source, managed/shadowed, dirty) for each\n * field from a cascade resolution and an optional dirty set. */\nexport function toFieldStates(\n fields: SettingFieldConfig[],\n result: EffectiveResult,\n dirty: Iterable<SettingKey> = [],\n): Record<SettingKey, SettingFieldState> {\n const dirtySet = new Set<SettingKey>(dirty);\n const states: Record<SettingKey, SettingFieldState> = {};\n for (const field of fields) {\n const prov = result.provenance[field.key];\n const raw = result.effective[field.key];\n // Defense in depth: never surface a secret's plaintext to a renderer, even if the caller passed\n // an UNMASKED resolution (resolve defaults to maskSecrets:false). Emit a masked SecretRef.\n const value =\n field.sensitivity === 'secret' && !isSecretRef(raw)\n ? makeSecretRef(field.key, raw !== undefined && raw !== null && raw !== '')\n : raw;\n states[field.key] = {\n value,\n source: prov?.winningScope,\n managed: prov?.managed ?? false,\n shadowed: (prov?.shadowed.length ?? 0) > 0,\n dirty: dirtySet.has(field.key),\n };\n }\n return states;\n}\n","/**\n * `createSettingsStore` — a framework-agnostic reactive store of effective settings. It holds the\n * ordered lower-scope stack + the editable (user) layer; every mutation re-resolves the cascade\n * (effective + provenance + conflicts), recomputes the dirty set and validation, masks secrets, and\n * notifies subscribers. No framework dependency: `subscribe`/`getState` plug into React via\n * `useSyncExternalStore`, or into anything via the listener. Constraints/secret-masking are honored\n * by reusing dials-core (`resolve`, `validate`, `maskEffectiveResult`).\n */\n\nimport type { z } from 'zod';\nimport { UNSET, maskEffectiveResult } from '@zodal/dials-core';\nimport type {\n ConstraintResult,\n DialsDefinition,\n EffectiveResult,\n KeyProvenance,\n Layer,\n ScopedLayer,\n SettingKey,\n} from '@zodal/dials-core';\nimport { dirtyKeys } from './lifecycle.js';\n\nexport interface SettingsState {\n /** Effective value per key (secrets masked as `SecretRef`). */\n effective: Record<SettingKey, unknown>;\n /** Provenance per key (also masked for secrets). */\n provenance: Record<SettingKey, KeyProvenance>;\n /** Keys set by multiple layers to differing values. */\n conflicts: EffectiveResult['conflicts'];\n /** The editable (user) layer — holds RAW values (including secrets), as the source to persist.\n * Split secrets out (dials-core `splitBySensitivity`) before saving to a config store. The\n * display surfaces (`effective`/`provenance`) mask secrets; this does not. */\n layer: Layer;\n /** The ordered lower-scope stack. */\n scopes: ScopedLayer[];\n /** Keys whose editable-layer value differs from the last saved baseline. */\n dirty: SettingKey[];\n /** Validation of the (unmasked) effective values. */\n validation: ConstraintResult;\n}\n\nexport interface CreateSettingsStoreOptions {\n /** Initial lower-scope stack (defaults are prepended by resolve). */\n scopes?: ScopedLayer[];\n /** Initial editable layer. */\n layer?: Layer;\n /** Scope id of the editable layer. Default: 'user'. */\n scope?: string;\n /** Mask secret effective values + provenance. Default: true. */\n maskSecrets?: boolean;\n /** Called if a subscriber throws during notification (so one bad listener can't break the others\n * or escape a mutation). Default: rethrow asynchronously is avoided — errors are reported here. */\n onListenerError?: (error: unknown) => void;\n}\n\nexport interface SettingsStore {\n getState(): SettingsState;\n /** Subscribe to state changes; returns an unsubscribe function. */\n subscribe(listener: () => void): () => void;\n /** Set a key in the editable layer. */\n set(key: SettingKey, value: unknown): void;\n /** Explicitly UNSET a key in the editable layer (re-exposes a lower scope). */\n unset(key: SettingKey): void;\n /** Remove a key from the editable layer (reset — a lower scope re-wins). */\n reset(key: SettingKey): void;\n /** Replace the whole editable layer. */\n setLayer(layer: Layer): void;\n /** Replace the lower-scope stack. */\n setScopes(scopes: ScopedLayer[]): void;\n /** Mark the current editable layer as saved (clears the dirty set). */\n markSaved(): void;\n /** Current effective value for a key (masked for secrets). */\n get(key: SettingKey): unknown;\n /** Provenance for a key. */\n explain(key: SettingKey): KeyProvenance | undefined;\n}\n\n/** Create a reactive settings store over a dials definition. */\nexport function createSettingsStore<T extends z.ZodObject<z.ZodRawShape>>(\n dials: DialsDefinition<T>,\n options: CreateSettingsStoreOptions = {},\n): SettingsStore {\n const scopeId = options.scope ?? 'user';\n const maskSecrets = options.maskSecrets !== false;\n const listeners = new Set<() => void>();\n\n let scopes: ScopedLayer[] = [...(options.scopes ?? [])];\n let layer: Layer = { ...(options.layer ?? {}) };\n let baseline: Layer = { ...layer };\n let state: SettingsState;\n\n const recompute = (): void => {\n // Resolve UNMASKED for validation (a masked SecretRef would fail value constraints), then mask\n // for the exposed state.\n const raw = dials.resolve([...scopes, { scope: scopeId, layer }]);\n const validation = dials.validate(raw.effective);\n const shown = maskSecrets ? maskEffectiveResult(raw, dials.sensitivityFor) : raw;\n state = {\n effective: shown.effective,\n provenance: shown.provenance,\n conflicts: shown.conflicts,\n layer: { ...layer },\n scopes: [...scopes],\n dirty: dirtyKeys(layer, baseline),\n validation,\n };\n // Notify defensively: one throwing listener must not break the others or escape the mutation.\n for (const listener of [...listeners]) {\n try {\n listener();\n } catch (error) {\n options.onListenerError?.(error);\n }\n }\n };\n\n recompute();\n\n const mutate = (next: Layer): void => {\n layer = next;\n recompute();\n };\n\n return {\n getState: () => state,\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n set: (key, value) => {\n // No-op guard: skip recompute+notify when a scalar value is unchanged (also breaks the naive\n // \"write back on change\" re-entrancy loop). Deep-equal object no-ops are not detected.\n if (Object.prototype.hasOwnProperty.call(layer, key) && Object.is(layer[key], value)) return;\n mutate({ ...layer, [key]: value });\n },\n unset: (key) => mutate({ ...layer, [key]: UNSET }),\n reset: (key) => {\n const next = { ...layer };\n delete next[key];\n mutate(next);\n },\n setLayer: (next) => mutate({ ...next }),\n setScopes: (next) => {\n scopes = [...next];\n recompute();\n },\n markSaved: () => {\n baseline = { ...layer };\n recompute();\n },\n get: (key) => state.effective[key],\n explain: (key) => state.provenance[key],\n };\n}\n","/**\n * Named profile management — the \"save / load / list named settings bundles\" capability (an app may\n * call these \"presets\", \"schemes\", or — for thoremin — \"instruments\"). A profile is a NAME + a\n * sparse `Layer` (persisted losslessly via `serializeLayer`, so `UNSET` survives) + optional metadata.\n * Persistence is pluggable (`ProfileStorage`): an in-memory default and a `localStorage` adapter are\n * provided. To apply a profile, put its layer into the cascade scope stack (e.g.\n * `store.setScopes([{ scope: 'profile', layer }])`) or replace the editable layer.\n *\n * SECURITY: profiles are plaintext at rest. Pass `sensitivityFor` so `secret` keys are REDACTED on\n * save (never persisted) — fail-closed, mirroring the jsonc store; otherwise split secrets out first.\n * Mutations are serialized per store so concurrent saves cannot lose updates.\n */\n\nimport { deserializeLayer, redactSecretsFromLayer, serializeLayer } from '@zodal/dials-core';\nimport type { Layer, SerializedLayer, Sensitivity, SettingKey } from '@zodal/dials-core';\n\n/** Lightweight profile descriptor (no layer payload) — for listing. */\nexport interface ProfileMeta {\n name: string;\n meta?: Record<string, unknown>;\n}\n\n/** A persisted named profile (a serialized sparse layer + metadata). */\nexport interface NamedProfile extends ProfileMeta {\n layer: SerializedLayer;\n}\n\n/** Pluggable persistence for the profile collection (reads/writes the whole list as JSON). */\nexport interface ProfileStorage {\n read(): Promise<NamedProfile[]>;\n write(profiles: NamedProfile[]): Promise<void>;\n}\n\nexport interface ProfileStoreOptions {\n /** Classify a setting's sensitivity. When provided, `secret` keys are REDACTED on save. */\n sensitivityFor?: (key: SettingKey) => Sensitivity;\n}\n\n/** An in-memory `ProfileStorage` (default; tests / ephemeral use). */\nexport function createMemoryProfileStorage(initial: NamedProfile[] = []): ProfileStorage {\n let profiles: NamedProfile[] = initial.map((p) => ({ ...p }));\n return {\n read: () => Promise.resolve(profiles.map((p) => ({ ...p }))),\n write: (next) => {\n profiles = next.map((p) => ({ ...p }));\n return Promise.resolve();\n },\n };\n}\n\n/** A `localStorage`-backed `ProfileStorage` (browser). Throws if `localStorage` is unavailable. A\n * corrupt stored value degrades to an empty list rather than throwing on every read. */\nexport function createLocalStorageProfileStorage(storageKey = 'zodal-dials.profiles'): ProfileStorage {\n const ls = (globalThis as { localStorage?: Storage }).localStorage;\n if (!ls) throw new Error('createLocalStorageProfileStorage: localStorage is not available');\n return {\n read: () => {\n const raw = ls.getItem(storageKey);\n if (!raw) return Promise.resolve([]);\n try {\n const parsed = JSON.parse(raw);\n return Promise.resolve(Array.isArray(parsed) ? (parsed as NamedProfile[]) : []);\n } catch {\n return Promise.resolve([]); // corrupt blob -> \"no profiles\" rather than a hard break\n }\n },\n write: (profiles) => {\n ls.setItem(storageKey, JSON.stringify(profiles));\n return Promise.resolve();\n },\n };\n}\n\nexport interface ProfileStore {\n /** All saved profiles (name + metadata only). */\n list(): Promise<ProfileMeta[]>;\n /** Save (or overwrite) a profile from a sparse layer. Rejects an empty/whitespace name. */\n save(name: string, layer: Layer, meta?: Record<string, unknown>): Promise<void>;\n /** Load a profile's layer, or undefined if absent. */\n load(name: string): Promise<Layer | undefined>;\n /** Remove a profile. */\n remove(name: string): Promise<void>;\n /** Rename a profile (no-op if `from` is absent or equals `to`; throws if `to` already exists). */\n rename(from: string, to: string): Promise<void>;\n /** Whether a profile exists. */\n has(name: string): Promise<boolean>;\n}\n\n/** Create a profile store over a pluggable storage backend. */\nexport function createProfileStore(storage: ProfileStorage, options: ProfileStoreOptions = {}): ProfileStore {\n const findIndex = (profiles: NamedProfile[], name: string): number => profiles.findIndex((p) => p.name === name);\n\n // Serialize all read-modify-write mutations so concurrent saves cannot lose updates.\n let queue: Promise<unknown> = Promise.resolve();\n const enqueue = <T>(run: () => Promise<T>): Promise<T> => {\n const result = queue.then(run, run);\n queue = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n };\n\n return {\n async list(): Promise<ProfileMeta[]> {\n const profiles = await storage.read();\n return profiles.map(({ name, meta }) => ({ name, ...(meta ? { meta } : {}) }));\n },\n\n save(name, layer, meta): Promise<void> {\n const trimmed = name.trim();\n if (!trimmed) return Promise.reject(new Error('profile name cannot be empty'));\n return enqueue(async () => {\n const profiles = await storage.read();\n let serialized = serializeLayer(layer);\n if (options.sensitivityFor) serialized = redactSecretsFromLayer(serialized, options.sensitivityFor);\n const entry: NamedProfile = { name: trimmed, layer: serialized, ...(meta ? { meta } : {}) };\n const index = findIndex(profiles, trimmed);\n if (index >= 0) profiles[index] = entry;\n else profiles.push(entry);\n await storage.write(profiles);\n });\n },\n\n async load(name): Promise<Layer | undefined> {\n const profiles = await storage.read();\n const found = profiles[findIndex(profiles, name)];\n return found ? deserializeLayer(found.layer) : undefined;\n },\n\n remove(name): Promise<void> {\n return enqueue(async () => {\n const profiles = await storage.read();\n await storage.write(profiles.filter((p) => p.name !== name));\n });\n },\n\n rename(from, to): Promise<void> {\n if (from === to) return Promise.resolve();\n return enqueue(async () => {\n const profiles = await storage.read();\n if (findIndex(profiles, to) >= 0) throw new Error(`profile \"${to}\" already exists`);\n const index = findIndex(profiles, from);\n if (index < 0) return;\n profiles[index] = { ...profiles[index], name: to };\n await storage.write(profiles);\n });\n },\n\n async has(name): Promise<boolean> {\n const profiles = await storage.read();\n return findIndex(profiles, name) >= 0;\n },\n };\n}\n"],"mappings":"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","names":["UNSET","UNSET","deserializeLayer","serializeLayer"]}
|