@richardmcquiston01/dashboard-widgets-toolkit 0.10.0 → 0.11.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.
@@ -0,0 +1,1333 @@
1
+ /**
2
+ * Widget data payloads, one shape per kind, discriminated by `kind`.
3
+ *
4
+ * The kind names are upper case to stay compatible with Maker Toolkit's
5
+ * `WidgetViewType` (`TEXT`, `GAUGE`, `TABLE`, `GRAPH`). `KPI`, `BAR_LIST`
6
+ * and `ALERT_LIST` are new. Every payload is plain JSON: strings, finite
7
+ * numbers, booleans, null, arrays and objects. No dates (send ISO strings),
8
+ * no functions, no class instances.
9
+ */
10
+ /** Every widget kind, in a stable order. */
11
+ declare const WIDGET_KINDS: readonly ["TEXT", "KPI", "GAUGE", "TABLE", "BAR_LIST", "ALERT_LIST", "GRAPH"];
12
+ type WidgetKind = (typeof WIDGET_KINDS)[number];
13
+ declare function isWidgetKind(value: unknown): value is WidgetKind;
14
+ /** How a number is shown: a plain count, money, or a fraction as a percent. */
15
+ declare const VALUE_FORMATS: readonly ["number", "currency", "percent"];
16
+ type ValueFormat = (typeof VALUE_FORMATS)[number];
17
+ /** A single headline value as text, e.g. "12" or "Open". */
18
+ interface TextWidgetData {
19
+ readonly kind: 'TEXT';
20
+ readonly value: string;
21
+ readonly label: string;
22
+ }
23
+ /**
24
+ * A stat tile: one number, optionally compared with the previous period.
25
+ *
26
+ * `value` and `previous` are in major units for `currency` (12.5 is $12.50)
27
+ * and fractions for `percent` (0.25 is 25%).
28
+ */
29
+ interface KpiWidgetData {
30
+ readonly kind: 'KPI';
31
+ readonly value: number;
32
+ /** The same measure for the previous period; null or absent for none. */
33
+ readonly previous?: number | null;
34
+ readonly format: ValueFormat;
35
+ /** ISO 4217 code for `currency`, e.g. "USD". Defaults to USD. */
36
+ readonly currency?: string;
37
+ readonly label: string;
38
+ /** Short context under the value, e.g. "Incl. shipping and tax". */
39
+ readonly hint?: string;
40
+ /** Whether a rise is good news (default true). Colors the change. */
41
+ readonly higherIsBetter?: boolean;
42
+ }
43
+ /** A value against a maximum, drawn as a meter. */
44
+ interface GaugeWidgetData {
45
+ readonly kind: 'GAUGE';
46
+ readonly value: number;
47
+ readonly max: number;
48
+ readonly label: string;
49
+ }
50
+ interface TableColumn {
51
+ readonly label: string;
52
+ /** Right-aligned, tabular figures. */
53
+ readonly numeric?: boolean;
54
+ }
55
+ interface TableCell {
56
+ readonly text: string;
57
+ /** Renders the cell as a link (http, https, mailto or relative only). */
58
+ readonly href?: string;
59
+ /**
60
+ * What the card's table controls (and the detail view) sort and search
61
+ * on, when `text` is formatted for reading: a timestamp behind "3 days
62
+ * ago", a rating behind stars, an amount behind "$1,234.00". Default:
63
+ * `text`.
64
+ */
65
+ readonly value?: string | number;
66
+ }
67
+ interface TableWidgetData {
68
+ readonly kind: 'TABLE';
69
+ readonly columns: readonly TableColumn[];
70
+ /** One array of cells per row, each as long as `columns`. */
71
+ readonly rows: readonly (readonly TableCell[])[];
72
+ /** A note under the table, e.g. "and 12 more". */
73
+ readonly footer?: string;
74
+ }
75
+ interface BarListItem {
76
+ readonly label: string;
77
+ readonly value: number;
78
+ /** Text shown beside the bar; defaults to the formatted value. */
79
+ readonly display?: string;
80
+ }
81
+ /**
82
+ * Labelled horizontal bars, e.g. ratings or purchases by country. Bars are a
83
+ * share of `total` when it is given, otherwise of the largest value.
84
+ */
85
+ interface BarListWidgetData {
86
+ readonly kind: 'BAR_LIST';
87
+ readonly items: readonly BarListItem[];
88
+ readonly total?: number;
89
+ }
90
+ interface AlertListItem {
91
+ readonly title: string;
92
+ /** Renders the title as a link (http, https, mailto or relative only). */
93
+ readonly href?: string;
94
+ /** A small square image (http, https or relative only). */
95
+ readonly thumbnailUrl?: string;
96
+ /** The short figure on the right, e.g. "2 left" or "31 views". */
97
+ readonly valueLabel: string;
98
+ /** A secondary line under the title. */
99
+ readonly detail?: string;
100
+ }
101
+ /**
102
+ * Things that need attention (low stock, stale listings...). `items` may be
103
+ * the first few of `total`; the renderer says "and N more".
104
+ */
105
+ interface AlertListWidgetData {
106
+ readonly kind: 'ALERT_LIST';
107
+ readonly items: readonly AlertListItem[];
108
+ readonly total: number;
109
+ /** Shown when there is nothing to flag, e.g. "Nothing low on stock." */
110
+ readonly emptyText: string;
111
+ }
112
+ interface GraphPoint {
113
+ /** The category or date label on the x axis, e.g. "Oct 2026". */
114
+ readonly label: string;
115
+ readonly value: number;
116
+ }
117
+ interface GraphSeries {
118
+ readonly name: string;
119
+ readonly points: readonly GraphPoint[];
120
+ }
121
+ /** The most series a graph may carry: the categorical palette's eight slots. */
122
+ declare const MAX_GRAPH_SERIES = 8;
123
+ /**
124
+ * A bar (grouped for several series) or line chart. All series share the
125
+ * x labels of the first series, by position, and one y axis.
126
+ */
127
+ interface GraphWidgetData {
128
+ readonly kind: 'GRAPH';
129
+ readonly series: readonly GraphSeries[];
130
+ readonly chartType: 'bar' | 'line';
131
+ readonly valueFormat: ValueFormat;
132
+ /** ISO 4217 code for `currency`. Defaults to USD. */
133
+ readonly currency?: string;
134
+ /** Heading for the label column in the table view, e.g. "Month". */
135
+ readonly xLabel?: string;
136
+ }
137
+ /** Any widget's data, discriminated by `kind`. */
138
+ type WidgetData = TextWidgetData | KpiWidgetData | GaugeWidgetData | TableWidgetData | BarListWidgetData | AlertListWidgetData | GraphWidgetData;
139
+ /** The payload type for one kind, e.g. `WidgetDataOf<'KPI'>`. */
140
+ type WidgetDataOf<K extends WidgetKind> = Extract<WidgetData, {
141
+ kind: K;
142
+ }>;
143
+ /**
144
+ * A widget with nothing to show yet, with the reason as text ("No orders
145
+ * synced yet."). Any widget may return this instead of its kind's payload.
146
+ */
147
+ interface WidgetEmptyState {
148
+ readonly empty: true;
149
+ readonly text: string;
150
+ }
151
+ /** What a provider or API returns for a widget: its data, or an empty state. */
152
+ type WidgetPayload = WidgetData | WidgetEmptyState;
153
+ /** The default text for an empty widget. */
154
+ declare const DEFAULT_EMPTY_TEXT = "Nothing to show yet.";
155
+ /** Builds an empty state. */
156
+ declare function emptyWidget(text?: string): WidgetEmptyState;
157
+ declare function isWidgetEmptyState(payload: WidgetPayload): payload is WidgetEmptyState;
158
+ /**
159
+ * The empty text for a payload with nothing in it (a table with no rows, a
160
+ * list with no items, a graph with no points), or null when it has content.
161
+ * TEXT, KPI and GAUGE always have content.
162
+ */
163
+ declare function emptyTextFor(payload: WidgetPayload): string | null;
164
+
165
+ /**
166
+ * Number formatting via `Intl`, with an injectable locale (undefined uses
167
+ * the runtime's default), plus KPI period-over-period deltas.
168
+ */
169
+
170
+ type Locale = string | readonly string[] | undefined;
171
+ interface FormatOptions {
172
+ readonly locale?: Locale;
173
+ /** ISO 4217 code for `currency`. Default "USD". */
174
+ readonly currency?: string;
175
+ /** "1.2K" style, for axis ticks. */
176
+ readonly compact?: boolean;
177
+ }
178
+ declare const DEFAULT_CURRENCY = "USD";
179
+ /**
180
+ * Formats `value` as a count ("1,284"), money ("$12.50", major units) or a
181
+ * percent ("25%" from 0.25).
182
+ */
183
+ declare function formatValue(value: number, format: ValueFormat, options?: FormatOptions): string;
184
+ /** A plain count, e.g. "1,284". */
185
+ declare function formatCount(value: number, locale?: Locale): string;
186
+ /** `part` as a whole percentage of `total` (0 when total is 0 or less). */
187
+ declare function shareOf(part: number, total: number): number;
188
+ /**
189
+ * Change from `previous` to `current` in percent (unrounded), or null when
190
+ * there is nothing to compare: no previous value, or a previous of 0.
191
+ */
192
+ declare function percentChange(current: number, previous: number | null | undefined): number | null;
193
+ /**
194
+ * "+12%", "-8%", "±0%". Below 10% one decimal is kept ("+2.5%"), so small
195
+ * changes don't round away.
196
+ */
197
+ declare function formatPercentChange(change: number, locale?: Locale): string;
198
+ type DeltaDirection = 'up' | 'down' | 'flat';
199
+ type DeltaSentiment = 'good' | 'bad' | 'neutral';
200
+ interface KpiDelta {
201
+ /** Percent change, unrounded. */
202
+ readonly percent: number;
203
+ readonly direction: DeltaDirection;
204
+ /** Direction combined with whether higher is better. */
205
+ readonly sentiment: DeltaSentiment;
206
+ /** "+12%" */
207
+ readonly text: string;
208
+ /** "+12% vs previous period" */
209
+ readonly description: string;
210
+ }
211
+ interface KpiDeltaOptions {
212
+ /** Default true. */
213
+ readonly higherIsBetter?: boolean;
214
+ readonly locale?: Locale;
215
+ /** What the change is against. Default "previous period". */
216
+ readonly comparisonLabel?: string;
217
+ }
218
+ /** The default comparison wording in delta descriptions. */
219
+ declare const DEFAULT_COMPARISON_LABEL = "previous period";
220
+ /**
221
+ * The period-over-period change for a KPI, or null when it can't be worked
222
+ * out (no previous value, or a previous of 0).
223
+ */
224
+ declare function kpiDelta(current: number, previous: number | null | undefined, options?: KpiDeltaOptions): KpiDelta | null;
225
+
226
+ /**
227
+ * Options a widget author declares and a viewer (or the consumer) can choose:
228
+ * a default sort, "top N", a period, which columns show. The package never
229
+ * decides what an option means for your data. It validates the chosen values,
230
+ * hands them to your provider (`ProviderOptions.options`), keeps cached
231
+ * payloads for different choices apart, and can sort or trim a payload itself
232
+ * when the author says so (`apply: 'client'`).
233
+ *
234
+ * Definitions stay plain JSON: no functions, no regular expressions.
235
+ */
236
+
237
+ /** A chosen option value. Only a `columns` option holds more than a scalar. */
238
+ type OptionValue = string | number | boolean | readonly string[];
239
+ /** Resolved option values by option key. */
240
+ type OptionValues = Readonly<Record<string, OptionValue>>;
241
+ /** Fields every declared option has. */
242
+ interface OptionBase {
243
+ /** Unique within the widget; letters, digits, `_` and `-`, starting with a letter. */
244
+ readonly key: string;
245
+ /** What the viewer sees, in your own words (and language). */
246
+ readonly label: string;
247
+ }
248
+ /** A pick from a fixed list. */
249
+ interface ChoiceOption extends OptionBase {
250
+ readonly type: 'choice';
251
+ readonly choices: readonly {
252
+ readonly value: string;
253
+ readonly label: string;
254
+ }[];
255
+ readonly default: string;
256
+ }
257
+ interface NumberOption extends OptionBase {
258
+ readonly type: 'number';
259
+ readonly min: number;
260
+ readonly max: number;
261
+ /** Chosen values must be `min` plus a whole number of steps. */
262
+ readonly step?: number;
263
+ readonly default: number;
264
+ }
265
+ interface BooleanOption extends OptionBase {
266
+ readonly type: 'boolean';
267
+ readonly default: boolean;
268
+ }
269
+ /** Free text: a search term, a tag. Always plain text, never HTML. */
270
+ interface TextOption extends OptionBase {
271
+ readonly type: 'text';
272
+ /** At most 500. */
273
+ readonly maxLength: number;
274
+ readonly default: string;
275
+ }
276
+ /** The named ranges a `dateRange` option can use (see `resolveDateRange`). */
277
+ declare const DATE_RANGE_PRESETS: readonly ["today", "yesterday", "last7", "last30", "last90", "thisMonth", "lastMonth", "thisYear", "lastYear"];
278
+ type DateRangePreset = (typeof DATE_RANGE_PRESETS)[number];
279
+ /**
280
+ * A range of calendar days. The value is a preset key (`last30`) or an ISO
281
+ * interval `YYYY-MM-DD/YYYY-MM-DD`. Providers always receive the interval.
282
+ */
283
+ interface DateRangeOption extends OptionBase {
284
+ readonly type: 'dateRange';
285
+ /** The presets a viewer may pick. Default: every preset. */
286
+ readonly presets?: readonly {
287
+ readonly value: DateRangePreset;
288
+ readonly label: string;
289
+ }[];
290
+ readonly default: string;
291
+ }
292
+ /** A `#rrggbb` color. A color alone should never carry meaning. */
293
+ interface ColorOption extends OptionBase {
294
+ readonly type: 'color';
295
+ readonly default: string;
296
+ }
297
+ /**
298
+ * How rows are ordered. The value is `column:asc` or `column:desc`, where
299
+ * `column` is a column key: `c0`, `c1`, ... for a TABLE (by position) and
300
+ * `label` or `value` for a BAR_LIST.
301
+ */
302
+ interface SortOption extends OptionBase {
303
+ readonly type: 'sort';
304
+ readonly columns: readonly {
305
+ readonly key: string;
306
+ readonly label: string;
307
+ }[];
308
+ /** Absent: no sort until the viewer picks one. */
309
+ readonly default?: string;
310
+ /**
311
+ * `provider` (default): your provider returns the right rows, which a
312
+ * truncated table needs. `client`: the toolkit sorts the payload it got.
313
+ */
314
+ readonly apply?: 'provider' | 'client';
315
+ }
316
+ /** Which TABLE columns show, and in what order. The value is column keys. */
317
+ interface ColumnsOption extends OptionBase {
318
+ readonly type: 'columns';
319
+ readonly columns: readonly {
320
+ readonly key: string;
321
+ readonly label: string;
322
+ }[];
323
+ readonly default: readonly string[];
324
+ /** `provider` (default) passes the keys on; `client` trims the payload here. */
325
+ readonly apply?: 'provider' | 'client';
326
+ }
327
+ type WidgetOption = ChoiceOption | NumberOption | BooleanOption | TextOption | DateRangeOption | ColorOption | SortOption | ColumnsOption;
328
+ declare const WIDGET_OPTION_TYPES: readonly ["choice", "number", "boolean", "text", "dateRange", "color", "sort", "columns"];
329
+ /** The most options one widget may declare. */
330
+ declare const MAX_WIDGET_OPTIONS = 20;
331
+ /** The most characters in a `text` option. */
332
+ declare const MAX_TEXT_OPTION_LENGTH = 500;
333
+ /** Allowed option keys. */
334
+ declare const OPTION_KEY_PATTERN: RegExp;
335
+ /** What option resolution needs from the outside world. */
336
+ interface OptionContext {
337
+ /** "Now" in epoch ms, for date range presets. Default `Date.now()`. */
338
+ readonly now?: number;
339
+ /** An IANA time zone for "today". Default `UTC`. */
340
+ readonly timeZone?: string;
341
+ }
342
+ /** The value a column key refers to in a sort or columns option. */
343
+ declare function isColumnKeyFor(kind: string, key: string): boolean;
344
+ /** Whether the toolkit (not your provider) applies this option. */
345
+ declare function isClientApplied(option: WidgetOption): boolean;
346
+ /** Whether `value` is `YYYY-MM-DD/YYYY-MM-DD` with real days, start not after end. */
347
+ declare function isIsoInterval(value: string): boolean;
348
+ /**
349
+ * The ISO interval a date range value stands for, or undefined when it is
350
+ * neither a preset nor a valid interval. Presets, in calendar days of the
351
+ * context's time zone: `today`, `yesterday`, `last7`, `last30` and `last90`
352
+ * (ending today), `thisMonth` and `thisYear` (from the first day to today),
353
+ * `lastMonth` and `lastYear` (the whole of it).
354
+ */
355
+ declare function resolveDateRange(value: string, context?: OptionContext): string | undefined;
356
+ /** `column:asc|desc` as a detail sort, or undefined when malformed. */
357
+ declare function parseSortValue(value: string): DetailSort | undefined;
358
+ /**
359
+ * The sort the widget's `sort` option currently selects, or undefined when
360
+ * the widget has no sort option or no sort is chosen. Use it to seed table
361
+ * headers and the detail view.
362
+ */
363
+ declare function sortFromOptions(definition: {
364
+ readonly options?: readonly WidgetOption[];
365
+ }, values: OptionValues | undefined): DetailSort | undefined;
366
+ /**
367
+ * A chosen value made valid for `option`, or undefined when it isn't one the
368
+ * option allows. Text is trimmed, colors lower-cased, date ranges resolved to
369
+ * an interval.
370
+ */
371
+ declare function coerceOptionValue(option: WidgetOption, value: unknown, context?: OptionContext): OptionValue | undefined;
372
+ /** The value an option has when nothing valid was chosen. */
373
+ declare function defaultOptionValue(option: WidgetOption, context?: OptionContext): OptionValue | undefined;
374
+ /**
375
+ * The value of every option a definition declares: the chosen one when it is
376
+ * valid for that option, else the default. Keys that aren't declared and
377
+ * values that aren't valid are ignored (never an error), so a layout saved
378
+ * before an option changed still loads. A `sort` option with no default and
379
+ * no valid choice is left out. Empty when the definition declares none.
380
+ */
381
+ declare function resolveOptionValues(definition: {
382
+ readonly options?: readonly WidgetOption[];
383
+ }, chosen: Readonly<Record<string, unknown>> | undefined, context?: OptionContext): OptionValues;
384
+ /** Whether two sets of resolved values are the same. */
385
+ declare function sameOptionValues(a: OptionValues, b: OptionValues): boolean;
386
+ /**
387
+ * Text that tells apart the payloads different choices produce, for cache
388
+ * keys: the values that differ from the default, in key order, and only the
389
+ * options your provider sees (`apply: 'client'` ones don't change the data it
390
+ * returns). Empty when everything is on its default, so a widget on defaults
391
+ * keeps its plain key.
392
+ */
393
+ declare function optionsCacheSuffix(definition: {
394
+ readonly options?: readonly WidgetOption[];
395
+ }, values: OptionValues, context?: OptionContext): string;
396
+ /**
397
+ * Applies the options the author marked `apply: 'client'` to a payload: sorts
398
+ * the rows of a TABLE or the items of a BAR_LIST, and trims and reorders the
399
+ * columns of a TABLE. Other payloads, and options whose columns don't fit the
400
+ * payload, come back unchanged. Sorting happens before trimming, so a sort
401
+ * can use a column that is then hidden.
402
+ */
403
+ declare function applyClientOptions(definition: {
404
+ readonly options?: readonly WidgetOption[];
405
+ }, data: WidgetData, values: OptionValues): WidgetData;
406
+
407
+ /**
408
+ * Runs each widget's data provider and collects the outcome per widget, so
409
+ * one failing provider never breaks the rest of the dashboard. The package
410
+ * never fetches anything itself: providers are the consumer's functions
411
+ * (a Prisma query, an HTTP call, a computed value).
412
+ */
413
+
414
+ /** The minimum a resolve context carries; add whatever your providers need. */
415
+ interface WidgetContext {
416
+ /** The viewer's roles. Widgets with `roles` need at least one of them. */
417
+ readonly roles?: readonly string[];
418
+ }
419
+ /** What the resolver hands every provider, after the context and definition. */
420
+ interface ProviderOptions {
421
+ /**
422
+ * Aborted when the widget times out, is refreshed, or the dashboard is torn
423
+ * down. Pass it to `fetch` (or any cancellable call) to stop the work; a
424
+ * provider that ignores it is still abandoned, just not stopped.
425
+ */
426
+ readonly signal: AbortSignal;
427
+ /**
428
+ * The widget's declared options, resolved: what was chosen when it is valid,
429
+ * else the default. Empty when the definition declares none. Treat values
430
+ * that reach a database query (`text`, `sort`, `columns`) as untrusted input
431
+ * and check them again on the server.
432
+ */
433
+ readonly options: OptionValues;
434
+ }
435
+ /**
436
+ * Loads one widget's payload. May be sync or async; may throw. The third
437
+ * argument is optional to use: a provider that takes only `(context,
438
+ * definition)` keeps working.
439
+ */
440
+ type WidgetProvider<C = WidgetContext> = (context: C, definition: WidgetDefinition, options: ProviderOptions) => WidgetPayload | Promise<WidgetPayload>;
441
+ /** Providers keyed by widget key. */
442
+ type WidgetProviders<C = WidgetContext> = Readonly<Record<string, WidgetProvider<C>>>;
443
+ interface OkWidget {
444
+ readonly definition: WidgetDefinition;
445
+ readonly status: 'ok';
446
+ readonly data: WidgetData;
447
+ /** When the data was produced (epoch ms), set when a provider resolved it. */
448
+ readonly updatedAt?: number;
449
+ /** True while cached data is shown and a fresh load is still running. */
450
+ readonly stale?: boolean;
451
+ /** The widget's resolved option values; present when it declares options. */
452
+ readonly options?: OptionValues;
453
+ }
454
+ interface EmptyWidget {
455
+ readonly definition: WidgetDefinition;
456
+ readonly status: 'empty';
457
+ /** Why there is nothing to show, e.g. "No orders synced yet." */
458
+ readonly emptyText: string;
459
+ /** The widget's resolved option values; present when it declares options. */
460
+ readonly options?: OptionValues;
461
+ }
462
+ interface ErrorWidget {
463
+ readonly definition: WidgetDefinition;
464
+ readonly status: 'error';
465
+ /** Names the widget and what failed. */
466
+ readonly error: string;
467
+ /** The widget's resolved option values; present when it declares options. */
468
+ readonly options?: OptionValues;
469
+ }
470
+ /** One widget after its provider ran. */
471
+ type ResolvedWidget = OkWidget | EmptyWidget | ErrorWidget;
472
+ /** A widget whose data hasn't arrived yet (for client-side loading states). */
473
+ interface LoadingWidget {
474
+ readonly definition: WidgetDefinition;
475
+ readonly status: 'loading';
476
+ /** The widget's resolved option values; present when it declares options. */
477
+ readonly options?: OptionValues;
478
+ }
479
+ /** Anything a dashboard can render: resolved, or still loading. */
480
+ type DashboardWidget = ResolvedWidget | LoadingWidget;
481
+ type WidgetStatus = DashboardWidget['status'];
482
+ /** A payload the cache holds, with when it was stored (epoch ms). */
483
+ interface CachedPayload {
484
+ readonly payload: unknown;
485
+ readonly storedAt: number;
486
+ }
487
+ /**
488
+ * Consumer-supplied storage for widget payloads (memory, localStorage, a
489
+ * database). The package never stores anything itself. Payloads read back
490
+ * are validated again, so a corrupt entry cannot break a widget.
491
+ */
492
+ interface WidgetCache {
493
+ get(key: string): CachedPayload | undefined | Promise<CachedPayload | undefined>;
494
+ set(key: string, entry: CachedPayload): void | Promise<void>;
495
+ }
496
+ interface ResolveOptions extends OptionContext {
497
+ /**
498
+ * The values chosen for each widget's options, by widget key. Values a
499
+ * widget didn't declare, or that aren't valid for the option, are ignored
500
+ * and the option's default is used.
501
+ */
502
+ readonly optionValues?: Readonly<Record<string, Readonly<Record<string, unknown>> | undefined>>;
503
+ /**
504
+ * Validate each provider's payload with `validateWidgetData` (and check
505
+ * its kind matches the definition). Default true; turn off only for
506
+ * trusted, typed providers.
507
+ */
508
+ readonly validate?: boolean;
509
+ /**
510
+ * Give each provider this many milliseconds. A timeout aborts the
511
+ * provider's signal and yields an `error` widget naming the key.
512
+ */
513
+ readonly timeoutMs?: number;
514
+ /** Cancels loading; affected widgets resolve as `error` (canceled). */
515
+ readonly signal?: AbortSignal;
516
+ /** Where to store successful payloads (see `WidgetCache`). */
517
+ readonly cache?: WidgetCache;
518
+ /** Cache key for a widget. Default: the widget's `key`. */
519
+ readonly cacheKey?: (definition: WidgetDefinition) => string;
520
+ }
521
+ /**
522
+ * Turns a payload (typically JSON from an API) into a resolved widget:
523
+ * validated against the definition's kind, and marked empty when it has
524
+ * nothing to show.
525
+ */
526
+ declare function resolvePayload(definition: WidgetDefinition, payload: unknown, options?: ResolveOptions): ResolvedWidget;
527
+ /** A widget that failed, with the reason. */
528
+ declare function failedWidget(definition: WidgetDefinition, error: string): ErrorWidget;
529
+ /** Loading placeholders for definitions, before their data arrives. */
530
+ declare function loadingWidgets(definitions: readonly WidgetDefinition[]): LoadingWidget[];
531
+ /**
532
+ * The cache key for a widget under these options. With resolved option
533
+ * `values`, choices that differ from the defaults are appended, so "top 5 by
534
+ * units" and "top 10 by revenue" are cached apart, while a widget on its
535
+ * defaults keeps its plain key.
536
+ */
537
+ declare function cacheKeyFor(definition: WidgetDefinition, options: ResolveOptions, values?: OptionValues): string;
538
+ /** The resolved option values for a widget under these resolve options. */
539
+ declare function optionValuesFor(definition: WidgetDefinition, options: ResolveOptions): OptionValues;
540
+ /**
541
+ * Applies the options marked `apply: 'client'` to an `ok` widget's payload and
542
+ * records the resolved values on the widget (when it declares options). Other
543
+ * widgets only get the values. Returns the same object when nothing changes.
544
+ */
545
+ declare function finalizeWidget<W extends DashboardWidget>(widget: W, values: OptionValues): W;
546
+ /**
547
+ * Runs one widget's provider, capturing a throw, a timeout, a cancellation or
548
+ * a bad payload as an error widget. Never rejects.
549
+ */
550
+ declare function resolveWidget<C extends WidgetContext>(definition: WidgetDefinition, provider: WidgetProvider<C> | undefined, context: C, options?: ResolveOptions): Promise<ResolvedWidget>;
551
+ /**
552
+ * Like `resolveWidget`, but leaves the payload as the provider returned it:
553
+ * options marked `apply: 'client'` are not applied (see `finalizeWidget`). For
554
+ * loaders that keep the payload so a client-side choice can change without
555
+ * asking the provider again.
556
+ */
557
+ declare function resolveWidgetUnapplied<C extends WidgetContext>(definition: WidgetDefinition, provider: WidgetProvider<C> | undefined, context: C, options?: ResolveOptions): Promise<ResolvedWidget>;
558
+ /**
559
+ * The widgets a viewer can see: active, allowed for `roles`, in sort order.
560
+ */
561
+ declare function widgetsFor(definitions: readonly WidgetDefinition[], roles: readonly string[] | undefined): WidgetDefinition[];
562
+ /**
563
+ * Resolves every widget the context's roles may see, in parallel. Inactive
564
+ * and role-filtered widgets are skipped (and their providers never run).
565
+ * Results come back in sort order; a provider that throws, is missing or
566
+ * returns a bad payload yields an `error` widget and never rejects.
567
+ */
568
+ declare function resolveWidgets<C extends WidgetContext>(definitions: readonly WidgetDefinition[], providers: WidgetProviders<C>, context: C, options?: ResolveOptions): Promise<ResolvedWidget[]>;
569
+
570
+ /**
571
+ * A small Result type. Functions in this package return a Result rather than
572
+ * throwing, so the caller has to look at the outcome. `throw` is reserved for
573
+ * programmer error.
574
+ */
575
+ interface Ok<T> {
576
+ readonly ok: true;
577
+ readonly value: T;
578
+ }
579
+ interface Err {
580
+ readonly ok: false;
581
+ /** Specific enough to act on: names the field, the widget and the problem. */
582
+ readonly error: string;
583
+ }
584
+ type Result<T> = Ok<T> | Err;
585
+ declare function ok<T>(value: T): Ok<T>;
586
+ declare function err(error: string): Err;
587
+ /** Turns an unknown thrown value into a readable message. */
588
+ declare function describeUnknownError(cause: unknown): string;
589
+
590
+ /**
591
+ * Detail view, core part: the data and query types, sorting, filtering and
592
+ * paging, query (de)serialisation for URLs, and validation. Pure functions;
593
+ * nothing here renders, fetches or reads `location`. See
594
+ * `docs/design/detail-view.md`.
595
+ */
596
+
597
+ /** `client`: the provider returns every row; `server`: it honors the query. */
598
+ declare const DETAIL_MODES: readonly ["client", "server"];
599
+ type DetailMode = (typeof DETAIL_MODES)[number];
600
+ declare const DEFAULT_DETAIL_PAGE_SIZE = 25;
601
+ declare const MAX_DETAIL_PAGE_SIZE = 200;
602
+ /** The `detail` setting on a widget definition. `true` means all defaults. */
603
+ interface WidgetDetailOptions {
604
+ /** Heading of the detail view. Default: the widget's title. */
605
+ readonly title?: string;
606
+ /** Rows per page, 1 to 200. Default 25. */
607
+ readonly pageSize?: number;
608
+ /** Default `client`. */
609
+ readonly mode?: DetailMode;
610
+ }
611
+ /** `WidgetDetailOptions` with every default filled in. */
612
+ interface ResolvedDetailOptions {
613
+ readonly title: string;
614
+ readonly pageSize: number;
615
+ readonly mode: DetailMode;
616
+ }
617
+ /** The `tableControls` setting on a widget definition. `true` means both. */
618
+ interface WidgetTableControls {
619
+ /** A search box over every column. Default true. */
620
+ readonly search?: boolean;
621
+ /** Sortable column headers. Default true. */
622
+ readonly sort?: boolean;
623
+ }
624
+ /** `WidgetTableControls` with every default filled in. */
625
+ interface ResolvedTableControls {
626
+ readonly search: boolean;
627
+ readonly sort: boolean;
628
+ }
629
+ interface DetailColumn {
630
+ /** Stable id used by sort, filters and URLs. Unique within the data. */
631
+ readonly key: string;
632
+ readonly label: string;
633
+ /** Right-aligned, and sorted as numbers when cell values are numbers. */
634
+ readonly numeric?: boolean;
635
+ /** Default true. */
636
+ readonly sortable?: boolean;
637
+ /** Default false: gets its own filter input when true. */
638
+ readonly filterable?: boolean;
639
+ }
640
+ interface DetailCell {
641
+ /** What is shown. */
642
+ readonly text: string;
643
+ /** What sorts and filters. Default: `text`. */
644
+ readonly value?: string | number;
645
+ /** Renders the cell as a link (http, https, mailto or relative only). */
646
+ readonly href?: string;
647
+ }
648
+ interface DetailData {
649
+ readonly columns: readonly DetailColumn[];
650
+ /** One array of cells per row, each as long as `columns`. */
651
+ readonly rows: readonly (readonly DetailCell[])[];
652
+ /** Server mode: rows matching the query across all pages. */
653
+ readonly totalRows?: number;
654
+ }
655
+ type SortDirection = 'asc' | 'desc';
656
+ interface DetailSort {
657
+ readonly column: string;
658
+ readonly direction: SortDirection;
659
+ }
660
+ interface DetailQuery {
661
+ /** Free text, matched against every column. */
662
+ readonly search?: string;
663
+ /** Per-column text, keyed by column key. Blank entries are ignored. */
664
+ readonly filters?: Readonly<Record<string, string>>;
665
+ readonly sort?: DetailSort;
666
+ /** 1-based. */
667
+ readonly page: number;
668
+ readonly pageSize: number;
669
+ }
670
+ /** What a detail provider receives beyond the usual arguments. */
671
+ interface DetailProviderOptions extends ProviderOptions {
672
+ readonly query: DetailQuery;
673
+ }
674
+ /**
675
+ * Loads the full data for one widget's detail view. In client mode it may
676
+ * ignore `options.query`; in server mode it returns one page and `totalRows`.
677
+ */
678
+ type DetailProvider<C = WidgetContext> = (context: C, definition: WidgetDefinition, options: DetailProviderOptions) => DetailData | Promise<DetailData>;
679
+ /** Detail providers keyed by widget key. */
680
+ type DetailProviders<C = WidgetContext> = Readonly<Record<string, DetailProvider<C>>>;
681
+ interface DetailPage {
682
+ /** The rows of the requested page (after filtering and sorting). */
683
+ readonly rows: readonly (readonly DetailCell[])[];
684
+ /** Rows matching the query, across all pages. */
685
+ readonly totalRows: number;
686
+ /** The page shown: the requested one, clamped into range. */
687
+ readonly page: number;
688
+ readonly pageCount: number;
689
+ }
690
+ /** Reads `definition.detail` with defaults; undefined when there is none. */
691
+ declare function resolveDetailOptions(definition: Pick<WidgetDefinition, 'title' | 'detail'>): ResolvedDetailOptions | undefined;
692
+ /**
693
+ * Reads `definition.tableControls` with defaults; undefined when the widget
694
+ * has none (or both are turned off).
695
+ */
696
+ declare function resolveTableControls(definition: Pick<WidgetDefinition, 'tableControls'>): ResolvedTableControls | undefined;
697
+ /** The first page, unsorted and unfiltered. */
698
+ declare function defaultDetailQuery(pageSize?: number): DetailQuery;
699
+ /**
700
+ * Filters, sorts and pages `data` in memory (client mode). Matching is
701
+ * case- and accent-insensitive substring matching on each cell's `value`
702
+ * (else `text`). Sorting is stable, locale-aware (`locale`, default the
703
+ * runtime's) and numeric for number values. `page` is clamped into range.
704
+ * Fails, naming the column, when the query names a column that does not
705
+ * exist or cannot be sorted.
706
+ */
707
+ declare function queryRows(data: DetailData, query: DetailQuery, locale?: string): Result<DetailPage>;
708
+ /**
709
+ * The query as a URL query string without the leading `?`: `q` (search),
710
+ * `sort` (`column:asc|desc`), `page` and `f.<column>` filters. `pageSize` is
711
+ * not included: the widget definition owns it. Defaults are left out.
712
+ */
713
+ declare function serializeDetailQuery(query: DetailQuery): string;
714
+ /**
715
+ * Reads a query string (with or without the leading `?`) written by
716
+ * `serializeDetailQuery`. Unknown parameters are ignored, so it can share a
717
+ * URL with the rest of an app. A malformed value fails with a message naming
718
+ * the parameter. When `data` is given, column keys are checked against it.
719
+ */
720
+ declare function parseDetailQuery(queryString: string, pageSize?: number, data?: Pick<DetailData, 'columns'>): Result<DetailQuery>;
721
+ /**
722
+ * Checks data from a detail provider: unique non-empty column keys, labels,
723
+ * every row as long as `columns`, cell `text` strings, `value` a string or
724
+ * finite number, safe `href`s, and `totalRows` a non-negative integer.
725
+ * Reports every problem, naming the column or row and cell.
726
+ */
727
+ declare function validateDetailData(data: unknown, subject?: string): Result<DetailData>;
728
+ /**
729
+ * A TABLE's columns and rows as detail data (column keys `c0`, `c1`, ...),
730
+ * footer ignored, so the table can be searched and sorted with `queryRows`.
731
+ */
732
+ declare function tableDetailData(data: TableWidgetData): DetailData;
733
+ /**
734
+ * The detail data for a widget whose card payload is already complete, so a
735
+ * detail view needs no provider: a TABLE without a `footer` (a footer means
736
+ * rows were left out) and a BAR_LIST. Returns undefined for everything else
737
+ * (alert lists, gauges, KPIs, text and charts): those need a detail provider.
738
+ */
739
+ declare function deriveDetailData(data: WidgetData, locale?: Locale): DetailData | undefined;
740
+
741
+ /** A size hint for grid layouts; renderers may map it to column spans. */
742
+ declare const WIDGET_SIZES: readonly ["small", "medium", "large", "full"];
743
+ type WidgetSize = (typeof WIDGET_SIZES)[number];
744
+ /**
745
+ * How a widget fills free space in a grid layout. `height` stretches it to
746
+ * the height of its row; `width` widens it to take the columns left over in
747
+ * its row; `both` does both. Absent, a widget is only as big as its content
748
+ * and `defaultSize`.
749
+ */
750
+ declare const WIDGET_FILLS: readonly ["height", "width", "both"];
751
+ type WidgetFill = (typeof WIDGET_FILLS)[number];
752
+ /**
753
+ * What a widget is, independent of its data. Mirrors Maker Toolkit's
754
+ * `widget` table (`widget_key`, `title`, `description`, `view_type`,
755
+ * `roles`, `sort_order`, `active`).
756
+ */
757
+ interface WidgetDefinition {
758
+ /** Stable, unique id. Layouts and providers are keyed by it. */
759
+ readonly key: string;
760
+ readonly title: string;
761
+ readonly description?: string;
762
+ readonly kind: WidgetKind;
763
+ /** Lower comes first. Default 0; ties keep definition order. */
764
+ readonly sortOrder?: number;
765
+ /** Roles allowed to see it. Absent or empty means everyone. */
766
+ readonly roles?: readonly string[];
767
+ /** False hides it from every user. Default true. */
768
+ readonly active?: boolean;
769
+ readonly defaultSize?: WidgetSize;
770
+ /**
771
+ * Share of the row the widget takes, in twelfths: an integer from 2 to 12
772
+ * (6 is half the row, 12 the whole row). When any widget in a grid sets
773
+ * it, the grid becomes 12 columns wide and widgets without a `width` use
774
+ * the one their `defaultSize` implies (see `widthForSize`). Narrow grids
775
+ * give widgets more room automatically.
776
+ */
777
+ readonly width?: number;
778
+ /**
779
+ * Gives the widget a detail view: a full list with sorting, filtering and
780
+ * paging. `true` uses defaults. The data comes from a detail provider.
781
+ */
782
+ readonly detail?: boolean | WidgetDetailOptions;
783
+ /**
784
+ * TABLE widgets only: a search box and sortable column headers in the card
785
+ * itself, working on the rows the card holds. `true` turns on both. With a
786
+ * `footer` (rows were left out) they only see the rows shown; use `detail`
787
+ * for the full list.
788
+ */
789
+ readonly tableControls?: boolean | WidgetTableControls;
790
+ /** Fill free space in the row (height, width or both). Default: none. */
791
+ readonly fill?: WidgetFill;
792
+ /**
793
+ * Keeps viewers from rearranging this widget. `true` locks everything:
794
+ * move, hide and minimize. Use an object to lock only some; fields left out
795
+ * are not locked.
796
+ */
797
+ readonly locked?: boolean | WidgetLock;
798
+ /**
799
+ * The widget's home page in a paged dashboard: a page key or title. A
800
+ * widget no page lists yet (and a widget locked against moving) shows here;
801
+ * without it, on the first page. Ignored when the layout has no pages.
802
+ */
803
+ readonly page?: string;
804
+ /**
805
+ * Choices the author offers, with defaults: a default sort, "top N", a
806
+ * period, which columns show. The chosen values reach your provider as
807
+ * `ProviderOptions.options`. See `options.ts`.
808
+ */
809
+ readonly options?: readonly WidgetOption[];
810
+ }
811
+ /** What a viewer may not do to a locked widget. */
812
+ interface WidgetLock {
813
+ /** Can't be reordered, and holds its place (a pinned slot). */
814
+ readonly move?: boolean;
815
+ /** Can't be hidden. */
816
+ readonly hide?: boolean;
817
+ /** Can't be collapsed to its header. */
818
+ readonly minimize?: boolean;
819
+ }
820
+ /** `WidgetLock` with every default filled in. */
821
+ type ResolvedWidgetLock = Required<WidgetLock>;
822
+ /**
823
+ * Reads a widget's `locked` setting with defaults: `true` locks move, hide
824
+ * and minimize, an object locks the fields it sets to `true`, and anything
825
+ * else (absent, `false`) locks nothing.
826
+ */
827
+ declare function resolveWidgetLock(definition: Pick<WidgetDefinition, 'locked'>): ResolvedWidgetLock;
828
+ /**
829
+ * Identity helper that keeps the literal `kind` (so typed providers can be
830
+ * checked against it) and gives a definition its type without a cast.
831
+ */
832
+ declare function defineWidget<const D extends WidgetDefinition>(definition: D): D;
833
+ /** Definitions sorted by `sortOrder` (stable: ties keep their input order). */
834
+ declare function sortDefinitions<D extends WidgetDefinition>(definitions: readonly D[]): D[];
835
+ /** Whether a widget is active and visible to someone with these roles. */
836
+ declare function isWidgetVisibleTo(definition: WidgetDefinition, roles: readonly string[] | undefined): boolean;
837
+
838
+ /** The parts of a widget definition that decide its place in a grid. */
839
+ interface GridItem {
840
+ readonly key: string;
841
+ readonly defaultSize?: WidgetSize;
842
+ readonly fill?: WidgetFill;
843
+ readonly width?: number;
844
+ }
845
+ /** Columns in a grid that uses widget `width`. */
846
+ declare const WIDTH_COLUMNS = 12;
847
+ /** Smallest and largest `width`, in twelfths of a row. */
848
+ declare const MIN_WIDGET_WIDTH = 2;
849
+ declare const MAX_WIDGET_WIDTH = 12;
850
+ /** Whether `value` is a valid widget `width`: an integer from 2 to 12. */
851
+ declare function isWidgetWidth(value: unknown): value is number;
852
+ /** The width (in twelfths) a `defaultSize` stands for. Default `medium`. */
853
+ declare function widthForSize(size: WidgetSize | undefined): number;
854
+ /** The 12-column width of an item: its `width`, else from its size. */
855
+ declare function itemWidth(item: GridItem): number;
856
+ /** Whether a grid of `items` should use 12 columns (any item sets `width`). */
857
+ declare function usesWidthColumns(items: readonly GridItem[]): boolean;
858
+ /** Whether the widget should take the columns left over in its row. */
859
+ declare function fillsWidth(fill: WidgetFill | undefined): boolean;
860
+ /** Whether the widget should stretch to the height of its row. */
861
+ declare function fillsHeight(fill: WidgetFill | undefined): boolean;
862
+ /**
863
+ * Columns a size takes in a grid of `columns` tracks: `large` spans
864
+ * `largeSpan` (default 2), `full` spans every column, anything else one.
865
+ */
866
+ declare function baseColumnSpan(size: WidgetSize | undefined, columns: number, largeSpan?: number): number;
867
+ /**
868
+ * Places `items` in a grid of `columns` tracks the way a sparse grid
869
+ * auto-places them (left to right, wrapping to a new row when an item does
870
+ * not fit), then gives the columns left over in each row to the items that
871
+ * `fill` their width, shared evenly (earlier items get the remainder).
872
+ *
873
+ * Returns the column span for each width-filling item, keyed by `key`. Items
874
+ * that do not fill width keep their normal size and are not in the map.
875
+ */
876
+ declare function fillColumnSpans(items: readonly GridItem[], columns: number, largeSpan?: number): Map<string, number>;
877
+ /**
878
+ * Like `fillColumnSpans` for a 12-column grid: each item starts at its
879
+ * `width` (see `itemWidth`) and width-fillers share the leftover twelfths.
880
+ */
881
+ declare function fillWidthSpans(items: readonly GridItem[]): Map<string, number>;
882
+ /** One row of a placed grid: the widgets in it and their final column spans. */
883
+ interface PlacedRow {
884
+ readonly keys: readonly string[];
885
+ /** Column spans, in `keys` order, after width-fillers took the leftovers. */
886
+ readonly spans: readonly number[];
887
+ }
888
+ /**
889
+ * The rows `items` occupy in a 12-column grid, exactly as `fillWidthSpans`
890
+ * places them: left to right, a widget that doesn't fit in the rest of a row
891
+ * starts the next row whole (a widget is never split), and width-fillers then
892
+ * grow into the leftover columns. Growth never changes how many rows there
893
+ * are.
894
+ */
895
+ declare function placeRows(items: readonly GridItem[]): readonly PlacedRow[];
896
+ /**
897
+ * How many of `items`, in order, fit in `maxRows` rows of a 12-column grid.
898
+ * Placement is left to right, so this is the length of the longest prefix
899
+ * whose rows number at most `maxRows`.
900
+ */
901
+ declare function fitCount(items: readonly GridItem[], maxRows: number): number;
902
+
903
+ /**
904
+ * A viewer's dashboard customization: order, hidden and minimized widgets.
905
+ * Pure functions over a small JSON-serialisable object. Persist it however
906
+ * you like (a settings table, localStorage); `parseLayout` tolerates
907
+ * anything, so a corrupt saved value never breaks the dashboard.
908
+ *
909
+ * Generalised from Maker Toolkit's desktop `dashboard-layout.ts`.
910
+ */
911
+
912
+ /** What layout functions need from a widget definition. */
913
+ interface LayoutItem {
914
+ readonly key: string;
915
+ readonly sortOrder?: number;
916
+ /** See `WidgetDefinition.locked`. Pinned widgets hold their slot. */
917
+ readonly locked?: boolean | WidgetLock;
918
+ /** See `WidgetDefinition.page`: the widget's home page. */
919
+ readonly page?: string;
920
+ }
921
+ interface DashboardLayout {
922
+ /** Widget keys in display order. Keys not listed follow, by sortOrder. */
923
+ readonly order: readonly string[];
924
+ /** Widget keys removed from the dashboard (restorable). */
925
+ readonly hidden: readonly string[];
926
+ /** Widget keys collapsed to their header. */
927
+ readonly minimized: readonly string[];
928
+ /**
929
+ * Pages of widgets. Absent, the lists above are the one and only page. With
930
+ * pages, each page owns its own lists and the top-level lists are left
931
+ * empty. See `pages.ts`.
932
+ */
933
+ readonly pages?: readonly LayoutPage[];
934
+ }
935
+ /** One page of a paged layout: the same lists as a layout, plus a name. */
936
+ interface LayoutPage {
937
+ /** Unique within the layout and stable when the page is renamed or moved. */
938
+ readonly key: string;
939
+ /** Shown in the page bar. At most `MAX_PAGE_TITLE_LENGTH` characters. */
940
+ readonly title: string;
941
+ readonly order: readonly string[];
942
+ readonly hidden: readonly string[];
943
+ readonly minimized: readonly string[];
944
+ /** Rows this page holds, overriding the dashboard's default. */
945
+ readonly maxRows?: number;
946
+ }
947
+ /** Longest page title; longer saved titles are cut. */
948
+ declare const MAX_PAGE_TITLE_LENGTH = 30;
949
+ /**
950
+ * Most pages `parseLayout` keeps: a safety ceiling against malformed or
951
+ * hostile JSON, not a product limit.
952
+ */
953
+ declare const MAX_PARSED_PAGES = 50;
954
+ /** Most rows a page may be given with `maxRows`. */
955
+ declare const MAX_PAGE_ROWS = 20;
956
+ declare const EMPTY_LAYOUT: DashboardLayout;
957
+ /**
958
+ * Reads a saved layout: a JSON string or an already-parsed object. Missing,
959
+ * corrupt or wrongly typed values give the empty layout (or empty lists for
960
+ * the fields that are wrong); non-string and duplicate keys are dropped.
961
+ */
962
+ declare function parseLayout(raw: unknown): DashboardLayout;
963
+ /** The JSON string to persist. */
964
+ declare function serializeLayout(layout: DashboardLayout): string;
965
+ /**
966
+ * The widgets to show, in order: saved keys first (when still present),
967
+ * then widgets the layout doesn't know yet, by sortOrder. Hidden widgets
968
+ * are left out. Widgets locked against moving are pinned: each keeps its
969
+ * place from the definitions (never from `layout.order`) and the others
970
+ * rearrange around it.
971
+ */
972
+ declare function visibleWidgets<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout): T[];
973
+ /** Hidden widgets, by sortOrder, for a "restore" menu. */
974
+ declare function hiddenWidgets<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout): T[];
975
+ /**
976
+ * Moves `key` to `toIndex` within the current visible order. Returns the
977
+ * same layout object when the key isn't visible, the index is out of range,
978
+ * or the move isn't allowed: a pinned widget can't move, and nothing can be
979
+ * moved onto a pinned slot. Free widgets keep their relative order around
980
+ * pinned ones, so a move that crosses a pinned slot lands on the nearest
981
+ * free slot you named.
982
+ */
983
+ declare function moveWidget<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout, key: string, toIndex: number): DashboardLayout;
984
+ /**
985
+ * Moves `key` by `offset` places (negative is earlier). Pinned slots are
986
+ * stepped over, not counted, and the move is clamped at the first and last
987
+ * free slot.
988
+ */
989
+ declare function moveWidgetBy<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout, key: string, offset: number): DashboardLayout;
990
+ /** Moves `key` one place earlier. */
991
+ declare function moveWidgetUp<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout, key: string): DashboardLayout;
992
+ /** Moves `key` one place later. */
993
+ declare function moveWidgetDown<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout, key: string): DashboardLayout;
994
+ declare function isHidden(layout: DashboardLayout, key: string): boolean;
995
+ declare function isMinimized(layout: DashboardLayout, key: string): boolean;
996
+ /** Hides `key`. No-op (same object) if already hidden. */
997
+ declare function hideWidget(layout: DashboardLayout, key: string): DashboardLayout;
998
+ /** Shows `key` again, expanded. No-op (same object) if not hidden. */
999
+ declare function showWidget(layout: DashboardLayout, key: string): DashboardLayout;
1000
+ /** Hides or shows `key` (showing also clears its minimized state). */
1001
+ declare function toggleHidden(layout: DashboardLayout, key: string): DashboardLayout;
1002
+ /** Collapses `key` to its header. No-op (same object) if already minimized. */
1003
+ declare function minimizeWidget(layout: DashboardLayout, key: string): DashboardLayout;
1004
+ /** Expands `key`. No-op (same object) if not minimized. */
1005
+ declare function restoreWidget(layout: DashboardLayout, key: string): DashboardLayout;
1006
+ /** Minimizes or restores `key`. */
1007
+ declare function toggleMinimized(layout: DashboardLayout, key: string): DashboardLayout;
1008
+ /**
1009
+ * Drops keys for widgets that no longer exist. Only pass the full list of
1010
+ * definitions (not a role-filtered one), or a viewer's settings for widgets
1011
+ * they temporarily can't see are lost.
1012
+ */
1013
+ declare function pruneLayout<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout): DashboardLayout;
1014
+ /** Options for `enforceLocks`. */
1015
+ interface EnforceLocksOptions {
1016
+ /**
1017
+ * Skip enforcement. Set it server-side only for a user who may rearrange
1018
+ * locked widgets (an administrator editing the default layout), never from
1019
+ * a flag the client sends.
1020
+ */
1021
+ readonly overrideLocks?: boolean;
1022
+ }
1023
+ /**
1024
+ * Removes whatever a widget's lock forbids from a layout: a hide-locked key
1025
+ * leaves `hidden`, a minimize-locked key leaves `minimized`, and a
1026
+ * move-locked key leaves `order` (a pinned widget's place comes from the
1027
+ * definitions). Idempotent; returns the same object when nothing changes.
1028
+ * Run it on a layout a client sends before saving it.
1029
+ */
1030
+ declare function enforceLocks<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout, options?: EnforceLocksOptions): DashboardLayout;
1031
+
1032
+ /**
1033
+ * Pages of widgets: a dashboard split into pages, like the home screens of a
1034
+ * phone. Each page is a small layout (order, hidden, minimized) with a name
1035
+ * and a capacity of `maxRows` rows of 12 columns. Pure functions, in the
1036
+ * style of `layout.ts`: inputs are never mutated, a refusal is a `Result`
1037
+ * with a specific message, and a no-op returns the same object.
1038
+ *
1039
+ * A layout without `pages` has one implicit page (`DEFAULT_PAGE_KEY`), so
1040
+ * everything here also works on layouts saved before pages existed.
1041
+ */
1042
+
1043
+ /** The key of the implicit page of a layout that has no `pages`. */
1044
+ declare const DEFAULT_PAGE_KEY = "default";
1045
+ /** Rows a page holds unless it (or the dashboard) says otherwise. */
1046
+ declare const DEFAULT_PAGE_ROWS = 4;
1047
+ /** What the page functions need from a widget definition. */
1048
+ type PageItem = LayoutItem & GridItem & {
1049
+ /** Used in messages; the key stands in when absent. */
1050
+ readonly title?: string;
1051
+ };
1052
+ interface PageOptions {
1053
+ /** Rows a page holds when it has no `maxRows` of its own. Default 4. */
1054
+ readonly maxRows?: number;
1055
+ }
1056
+ /** Whether the layout has pages of its own (rather than one implicit page). */
1057
+ declare function hasPages(layout: DashboardLayout): boolean;
1058
+ /**
1059
+ * The layout's pages. A layout without `pages` has one implicit page called
1060
+ * "Page 1" made from its top-level lists.
1061
+ */
1062
+ declare function pageList(layout: DashboardLayout): readonly LayoutPage[];
1063
+ /**
1064
+ * One page as an ordinary layout, so every function in `layout.ts` (move,
1065
+ * hide, minimize, `visibleWidgets`) works on it unchanged. For a layout with
1066
+ * no pages this is the layout itself. Undefined for an unknown page.
1067
+ */
1068
+ declare function pageLayout(layout: DashboardLayout, pageKey: string): DashboardLayout | undefined;
1069
+ /**
1070
+ * Writes a page's layout (changed with the functions in `layout.ts`) back
1071
+ * into the whole layout. Returns the same object when nothing changed or the
1072
+ * page doesn't exist.
1073
+ */
1074
+ declare function withPageLayout(layout: DashboardLayout, pageKey: string, next: DashboardLayout): DashboardLayout;
1075
+ /** The key of the page a `page` reference names: a key, else a title. */
1076
+ declare function resolvePageKey(layout: DashboardLayout, reference: string | undefined): string | undefined;
1077
+ /**
1078
+ * Which page each widget shows on: the page that lists it (in its order or
1079
+ * hidden list), else its home page (`definition.page`, else the first page).
1080
+ * A widget locked against moving always shows on its home page, so a viewer's
1081
+ * saved layout can't move it. Definitions keep their input order.
1082
+ */
1083
+ declare function assignPages<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout): Map<string, T[]>;
1084
+ /** The key of the page a widget shows on; undefined if it isn't a definition. */
1085
+ declare function pageOf<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout, key: string): string | undefined;
1086
+ /** A page's visible widgets, in display order (minimized ones included). */
1087
+ declare function pageWidgets<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout, pageKey: string): T[];
1088
+ /** How full a page is. */
1089
+ interface PageRoom {
1090
+ readonly maxRows: number;
1091
+ readonly rowsUsed: number;
1092
+ readonly rowsFree: number;
1093
+ }
1094
+ /** Rows a page allows, uses and has left, as the grid will draw them. */
1095
+ declare function pageRoom<T extends PageItem>(definitions: readonly T[], layout: DashboardLayout, pageKey: string, options?: PageOptions): PageRoom | undefined;
1096
+ /** The first `page-N` key no page uses. */
1097
+ declare function nextPageKey(layout: DashboardLayout): string;
1098
+ interface AddPageOptions {
1099
+ /** The new page's title. Default: the next free "Page N". */
1100
+ readonly title?: string;
1101
+ /** The new page's key. Default: the next free `page-N`. */
1102
+ readonly key?: string;
1103
+ }
1104
+ /**
1105
+ * Adds an empty page after the last one. A layout without pages gains an
1106
+ * explicit first page holding everything it had.
1107
+ */
1108
+ declare function addPage(layout: DashboardLayout, options?: AddPageOptions): Result<DashboardLayout>;
1109
+ /** Renames a page. The same layout comes back when the title is unchanged. */
1110
+ declare function renamePage(layout: DashboardLayout, pageKey: string, title: string): Result<DashboardLayout>;
1111
+ /**
1112
+ * Moves a page `offset` places (negative is earlier), clamped at the ends.
1113
+ * Returns the same layout when nothing moves.
1114
+ */
1115
+ declare function movePage(layout: DashboardLayout, pageKey: string, offset: number): DashboardLayout;
1116
+ /**
1117
+ * Removes a page. Its widgets move to the end of the previous page (the next
1118
+ * one when it was first), keeping their hidden and minimized state. Refused
1119
+ * for the only page, and when the widgets wouldn't fit on the pages left.
1120
+ */
1121
+ declare function removePage<T extends PageItem>(definitions: readonly T[], layout: DashboardLayout, pageKey: string, options?: PageOptions): Result<DashboardLayout>;
1122
+ /**
1123
+ * Moves a widget to the end of another page, keeping its hidden and
1124
+ * minimized state. Refused for an unknown widget or page, for a widget locked
1125
+ * against moving, and when a visible widget wouldn't fit in the page's rows.
1126
+ */
1127
+ declare function moveWidgetToPage<T extends PageItem>(definitions: readonly T[], layout: DashboardLayout, widgetKey: string, targetKey: string, options?: PageOptions): Result<DashboardLayout>;
1128
+ interface NormalizeOptions extends PageOptions {
1129
+ /** Skip lock enforcement, for a user who may rearrange locked widgets. */
1130
+ readonly overrideLocks?: boolean;
1131
+ }
1132
+ /**
1133
+ * Repairs a layout so it can be trusted, for the place it is saved: unknown
1134
+ * widgets are dropped, locks are enforced, and in a paged layout each widget
1135
+ * lives on one page, titles are trimmed, unique and short, widgets no page
1136
+ * lists yet are placed (on their home page), and a page that holds too many
1137
+ * rows has its overflow moved, in order, to the next page with room or a new
1138
+ * page after it. Nothing is hidden or dropped to make room. Idempotent;
1139
+ * returns the same object when nothing changes.
1140
+ */
1141
+ declare function normalizeLayout<T extends PageItem>(definitions: readonly T[], layout: DashboardLayout, options?: NormalizeOptions): DashboardLayout;
1142
+
1143
+ /** Axis maths for charts: clean domains and tick values. Pure. */
1144
+ /** The smallest 1, 2 or 5 × 10^n that is at least `value` (1 for 0 or less). */
1145
+ declare function niceStep(value: number): number;
1146
+ interface NiceDomain {
1147
+ readonly min: number;
1148
+ readonly max: number;
1149
+ /** From min to max inclusive, evenly spaced on a clean step. */
1150
+ readonly ticks: readonly number[];
1151
+ }
1152
+ /**
1153
+ * A y-axis domain that always includes 0 (bars grow from a single
1154
+ * baseline), rounded out to a clean step, with about `tickCount` intervals.
1155
+ */
1156
+ declare function niceDomain(values: readonly number[], tickCount?: number): NiceDomain;
1157
+ /**
1158
+ * Show every `n`th x label so at most `maxLabels` appear (always at least 1).
1159
+ */
1160
+ declare function labelStride(count: number, maxLabels?: number): number;
1161
+
1162
+ /**
1163
+ * Storage adapters: the contract a developer implements to keep a layout in
1164
+ * localStorage, IndexedDB, a REST API or a database, plus I/O-free helpers
1165
+ * built on it (`createLayoutPersistence` and small adapter wrappers). The
1166
+ * package does no I/O itself: no timers, DOM, `fetch` or storage globals.
1167
+ * See docs/design/storage-adapters.md.
1168
+ */
1169
+
1170
+ type StoreErrorCode =
1171
+ /** Storage can't be reached or is disabled. */
1172
+ 'unavailable'
1173
+ /** Storage is full. */
1174
+ | 'quota'
1175
+ /** Not allowed to read or write. */
1176
+ | 'forbidden'
1177
+ /** The stored revision is newer than the one given. */
1178
+ | 'conflict'
1179
+ /** The adapter was given something it can't store. */
1180
+ | 'invalid'
1181
+ /** What is stored can't be read back. */
1182
+ | 'corrupt';
1183
+ type StoreResult<T> = {
1184
+ readonly ok: true;
1185
+ readonly value: T;
1186
+ } | {
1187
+ readonly ok: false;
1188
+ readonly code: StoreErrorCode;
1189
+ /** Names the key and the cause. */
1190
+ readonly error: string;
1191
+ };
1192
+ interface StoredValue {
1193
+ /** The serialized layout. */
1194
+ readonly value: string;
1195
+ /** Opaque version (an ETag, a row version) for conflict detection. */
1196
+ readonly revision?: string;
1197
+ }
1198
+ interface StorageAdapter {
1199
+ /** Resolves `{ ok: true, value: null }` when nothing is stored. */
1200
+ get(key: string, options: {
1201
+ readonly signal: AbortSignal;
1202
+ }): Promise<StoreResult<StoredValue | null>>;
1203
+ /**
1204
+ * With `ifRevision`, fail with `conflict` when the stored revision differs.
1205
+ * Adapters without revisions ignore it (last write wins).
1206
+ */
1207
+ set(key: string, value: string, options: {
1208
+ readonly signal: AbortSignal;
1209
+ readonly ifRevision?: string;
1210
+ }): Promise<StoreResult<StoredValue>>;
1211
+ remove?(key: string, options: {
1212
+ readonly signal: AbortSignal;
1213
+ }): Promise<StoreResult<null>>;
1214
+ /** Optional: report a change made elsewhere (another tab). Returns an unsubscribe. */
1215
+ subscribe?(key: string, onChange: (stored: StoredValue | null) => void): () => void;
1216
+ }
1217
+ /** UTF-8 byte length without `TextEncoder` (the core has no DOM or Node lib). */
1218
+ declare function utf8Length(text: string): number;
1219
+ /** An in-memory adapter with revisions and `subscribe`: tests, SSR, fallback. */
1220
+ declare function memoryAdapter(initial?: Readonly<Record<string, string>>): StorageAdapter;
1221
+ /** Namespaces every key, for several dashboards or apps in one store. */
1222
+ declare function withPrefix(adapter: StorageAdapter, prefix: string): StorageAdapter;
1223
+ /**
1224
+ * Reads from `primary`, falling back to `secondary` when it errs or holds
1225
+ * nothing. Writes go to `primary` first, then `secondary` (best effort); a
1226
+ * `conflict` from `primary` is returned as is and writes nothing else. When
1227
+ * `primary` can't be written but `secondary` can, the write counts as saved.
1228
+ */
1229
+ declare function withFallback(primary: StorageAdapter, secondary: StorageAdapter): StorageAdapter;
1230
+ /** Remembers each key's last read or write, so repeated reads skip the store. */
1231
+ declare function withReadCache(adapter: StorageAdapter): StorageAdapter;
1232
+ /** Rejects writes with `forbidden`: previewing someone else's layout. */
1233
+ declare function readOnly(adapter: StorageAdapter): StorageAdapter;
1234
+ interface RetryOptions {
1235
+ /** Total tries, including the first. Default 3. */
1236
+ readonly attempts?: number;
1237
+ /** First wait in ms; doubles each retry. Default 200. */
1238
+ readonly delayMs?: number;
1239
+ /** Waits `ms`. You pass it, so the core has no timers. */
1240
+ readonly sleep: (ms: number, signal: AbortSignal) => Promise<void>;
1241
+ }
1242
+ /** Retries `unavailable` errors (and nothing else) with doubling waits. */
1243
+ declare function withRetry(adapter: StorageAdapter, options: RetryOptions): StorageAdapter;
1244
+ interface EncodingOptions {
1245
+ /** Applied before storing: compression, or encryption with your own crypto. */
1246
+ readonly encode: (value: string) => string | Promise<string>;
1247
+ /** Reverses `encode` after reading. */
1248
+ readonly decode: (value: string) => string | Promise<string>;
1249
+ }
1250
+ /** Transforms values on the way in and out. Keys and revisions pass through. */
1251
+ declare function withEncoding(adapter: StorageAdapter, codec: EncodingOptions): StorageAdapter;
1252
+ interface StorageLogEvent {
1253
+ readonly operation: 'get' | 'set' | 'remove';
1254
+ readonly key: string;
1255
+ readonly ok: boolean;
1256
+ /** Present when `ok` is false. Values are never logged. */
1257
+ readonly code?: StoreErrorCode;
1258
+ }
1259
+ /** Reports each operation's key and outcome. Never the stored value. */
1260
+ declare function withLogging(adapter: StorageAdapter, log: (event: StorageLogEvent) => void): StorageAdapter;
1261
+ interface StoreScope {
1262
+ /** Which dashboard: 'sales'. */
1263
+ readonly dashboardKey: string;
1264
+ /** Who it belongs to; absent for shared defaults. A hint for building the key: a server takes the user from the session. */
1265
+ readonly userKey?: string;
1266
+ }
1267
+ /** Current envelope version written by `createLayoutPersistence`. */
1268
+ declare const LAYOUT_ENVELOPE_VERSION = 1;
1269
+ /** Default largest serialized layout (bytes). */
1270
+ declare const DEFAULT_MAX_LAYOUT_BYTES: number;
1271
+ type ConflictPolicy =
1272
+ /** Save anyway; the other device's change is lost. */
1273
+ 'overwrite'
1274
+ /** Drop the local save and return what is stored. */
1275
+ | 'keep-theirs'
1276
+ /** Return `outcome: 'conflict'` and let the UI choose (default). */
1277
+ | 'ask'
1278
+ /** Merge, then save the result once. */
1279
+ | ((mine: DashboardLayout, theirs: DashboardLayout) => DashboardLayout);
1280
+ interface LayoutPersistenceOptions {
1281
+ /** Default 64 KB. */
1282
+ readonly maxBytes?: number;
1283
+ /** For adapters with revisions. Default `'ask'`. */
1284
+ readonly onConflict?: ConflictPolicy;
1285
+ /** Cancels every operation. */
1286
+ readonly signal?: AbortSignal;
1287
+ /** Epoch ms for the envelope's `savedAt`. Default `Date.now`. */
1288
+ readonly now?: () => number;
1289
+ }
1290
+ interface LoadedLayout {
1291
+ /** Repaired against the definitions; null when nothing is stored. */
1292
+ readonly layout: DashboardLayout | null;
1293
+ readonly revision?: string;
1294
+ /** ISO time from the envelope; absent for a bare legacy layout. */
1295
+ readonly savedAt?: string;
1296
+ }
1297
+ interface SavedLayout {
1298
+ /**
1299
+ * `saved`: stored (or merged, then stored). `kept-theirs`: the stored layout
1300
+ * won and `layout` is it. `conflict`: nothing was stored and `layout` is
1301
+ * the stored one; decide, then save again with `force: true`.
1302
+ */
1303
+ readonly outcome: 'saved' | 'kept-theirs' | 'conflict';
1304
+ /** What is now stored (or, for `conflict`, what is stored by someone else). */
1305
+ readonly layout: DashboardLayout;
1306
+ readonly revision?: string;
1307
+ }
1308
+ interface SaveOptions {
1309
+ /** Skip conflict detection and overwrite. */
1310
+ readonly force?: boolean;
1311
+ }
1312
+ interface LayoutPersistence {
1313
+ /** The adapter key for a scope, for example `dwt:sales:user-42:layout`. */
1314
+ keyFor(scope: StoreScope, part?: 'layout' | 'backup'): string;
1315
+ load(scope: StoreScope, definitions: readonly PageItem[], options?: NormalizeOptions): Promise<StoreResult<LoadedLayout>>;
1316
+ save(scope: StoreScope, layout: DashboardLayout, options?: SaveOptions): Promise<StoreResult<SavedLayout>>;
1317
+ loadBackup(scope: StoreScope): Promise<StoreResult<DashboardLayout | null>>;
1318
+ saveBackup(scope: StoreScope, layout: DashboardLayout): Promise<StoreResult<null>>;
1319
+ /** Removes the layout and its backup. */
1320
+ reset(scope: StoreScope): Promise<StoreResult<null>>;
1321
+ /** Calls `onChange` when another tab or device changes the layout; needs `adapter.subscribe`. */
1322
+ watch(scope: StoreScope, definitions: readonly PageItem[], onChange: (loaded: LoadedLayout) => void, options?: NormalizeOptions): () => void;
1323
+ }
1324
+ /**
1325
+ * Persistence for layouts over any `StorageAdapter`: an envelope with a
1326
+ * version, tolerant reads repaired against today's definitions, a size limit,
1327
+ * revision tracking with a conflict policy, and saves serialized per key so
1328
+ * rapid edits never race (while one save is in flight, later ones collapse
1329
+ * into the newest).
1330
+ */
1331
+ declare function createLayoutPersistence(adapter: StorageAdapter, options?: LayoutPersistenceOptions): LayoutPersistence;
1332
+
1333
+ export { type DetailCell as $, type AlertListWidgetData as A, type BarListWidgetData as B, type CachedPayload as C, type DashboardWidget as D, DATE_RANGE_PRESETS as E, DEFAULT_COMPARISON_LABEL as F, type GraphWidgetData as G, DEFAULT_CURRENCY as H, DEFAULT_DETAIL_PAGE_SIZE as I, DEFAULT_EMPTY_TEXT as J, type KpiWidgetData as K, type Locale as L, DEFAULT_MAX_LAYOUT_BYTES as M, type NiceDomain as N, type OptionValues as O, type PageItem as P, DEFAULT_PAGE_KEY as Q, type ResolvedTableControls as R, type StoreScope as S, type TableWidgetData as T, DEFAULT_PAGE_ROWS as U, DETAIL_MODES as V, type WidgetSize as W, type DateRangeOption as X, type DateRangePreset as Y, type DeltaDirection as Z, type DeltaSentiment as _, type WidgetFill as a, WIDGET_SIZES as a$, type DetailColumn as a0, type DetailMode as a1, type DetailPage as a2, type DetailProvider as a3, type DetailProviders as a4, EMPTY_LAYOUT as a5, type EmptyWidget as a6, type EncodingOptions as a7, type EnforceLocksOptions as a8, type Err as a9, type OptionContext as aA, type OptionValue as aB, type PageOptions as aC, type PageRoom as aD, type PlacedRow as aE, type ProviderOptions as aF, type ResolvedDetailOptions as aG, type ResolvedWidget as aH, type ResolvedWidgetLock as aI, type RetryOptions as aJ, type SaveOptions as aK, type SavedLayout as aL, type SortDirection as aM, type SortOption as aN, type StorageAdapter as aO, type StorageLogEvent as aP, type StoreErrorCode as aQ, type StoreResult as aR, type StoredValue as aS, type TableCell as aT, type TableColumn as aU, type TextOption as aV, VALUE_FORMATS as aW, type ValueFormat as aX, WIDGET_FILLS as aY, WIDGET_KINDS as aZ, WIDGET_OPTION_TYPES as a_, type ErrorWidget as aa, type FormatOptions as ab, type GraphPoint as ac, type GraphSeries as ad, type GridItem as ae, type KpiDelta as af, type KpiDeltaOptions as ag, LAYOUT_ENVELOPE_VERSION as ah, type LayoutItem as ai, type LayoutPage as aj, type LayoutPersistenceOptions as ak, type LoadedLayout as al, type LoadingWidget as am, MAX_DETAIL_PAGE_SIZE as an, MAX_GRAPH_SERIES as ao, MAX_PAGE_ROWS as ap, MAX_PAGE_TITLE_LENGTH as aq, MAX_PARSED_PAGES as ar, MAX_TEXT_OPTION_LENGTH as as, MAX_WIDGET_OPTIONS as at, MAX_WIDGET_WIDTH as au, MIN_WIDGET_WIDTH as av, type NumberOption as aw, OPTION_KEY_PATTERN as ax, type Ok as ay, type OkWidget as az, type WidgetDefinition as b, normalizeLayout as b$, WIDTH_COLUMNS as b0, type WidgetCache as b1, type WidgetDataOf as b2, type WidgetDetailOptions as b3, type WidgetEmptyState as b4, type WidgetLock as b5, type WidgetOption as b6, type WidgetProvider as b7, type WidgetStatus as b8, type WidgetTableControls as b9, hasPages as bA, hiddenWidgets as bB, hideWidget as bC, isClientApplied as bD, isColumnKeyFor as bE, isHidden as bF, isIsoInterval as bG, isMinimized as bH, isWidgetEmptyState as bI, isWidgetKind as bJ, isWidgetVisibleTo as bK, isWidgetWidth as bL, itemWidth as bM, kpiDelta as bN, labelStride as bO, loadingWidgets as bP, memoryAdapter as bQ, minimizeWidget as bR, movePage as bS, moveWidget as bT, moveWidgetBy as bU, moveWidgetDown as bV, moveWidgetToPage as bW, moveWidgetUp as bX, nextPageKey as bY, niceDomain as bZ, niceStep as b_, addPage as ba, applyClientOptions as bb, assignPages as bc, baseColumnSpan as bd, cacheKeyFor as be, coerceOptionValue as bf, createLayoutPersistence as bg, defaultDetailQuery as bh, defaultOptionValue as bi, defineWidget as bj, deriveDetailData as bk, describeUnknownError as bl, emptyTextFor as bm, emptyWidget as bn, enforceLocks as bo, err as bp, failedWidget as bq, fillColumnSpans as br, fillWidthSpans as bs, fillsHeight as bt, fillsWidth as bu, finalizeWidget as bv, fitCount as bw, formatCount as bx, formatPercentChange as by, formatValue as bz, type DetailProviderOptions as c, ok as c0, optionValuesFor as c1, optionsCacheSuffix as c2, pageLayout as c3, pageList as c4, pageOf as c5, pageRoom as c6, pageWidgets as c7, parseDetailQuery as c8, parseLayout as c9, tableDetailData as cA, toggleHidden as cB, toggleMinimized as cC, usesWidthColumns as cD, utf8Length as cE, validateDetailData as cF, visibleWidgets as cG, widgetsFor as cH, widthForSize as cI, withEncoding as cJ, withFallback as cK, withLogging as cL, withPageLayout as cM, withPrefix as cN, withReadCache as cO, withRetry as cP, parseSortValue as ca, percentChange as cb, placeRows as cc, pruneLayout as cd, queryRows as ce, readOnly as cf, removePage as cg, renamePage as ch, resolveDateRange as ci, resolveDetailOptions as cj, resolveOptionValues as ck, resolvePageKey as cl, resolvePayload as cm, resolveTableControls as cn, resolveWidget as co, resolveWidgetLock as cp, resolveWidgetUnapplied as cq, resolveWidgets as cr, restoreWidget as cs, sameOptionValues as ct, serializeDetailQuery as cu, serializeLayout as cv, shareOf as cw, showWidget as cx, sortDefinitions as cy, sortFromOptions as cz, type DetailData as d, type DetailQuery as e, type DashboardLayout as f, type GaugeWidgetData as g, type DetailSort as h, type TextWidgetData as i, type WidgetData as j, type LayoutPersistence as k, type NormalizeOptions as l, type ResolveOptions as m, type WidgetContext as n, type WidgetProviders as o, type WidgetKind as p, type Result as q, type WidgetPayload as r, type AddPageOptions as s, type AlertListItem as t, type BarListItem as u, type BooleanOption as v, type ChoiceOption as w, type ColorOption as x, type ColumnsOption as y, type ConflictPolicy as z };