sg-widgets-core 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.
@@ -0,0 +1,2726 @@
1
+ import { $ as STOCK_SPRITE_PATH, $t as isOperator, A as hierarchyEntity, At as emptyFilter, B as intersectStatuses, Bt as DataType, C as SummaryType, Ct as FilterNode, D as UploadFile, Dt as WireCondition, E as ThreadRow, Et as TextSearchFilter, F as StatusIcon, Ft as referencedPaths, G as statusLabel, Gt as TIME_UNITS, H as relativeLuminance, Ht as OPERATORS, I as StatusOption, It as toApi3Hash, J as NATIVE_STATUSES, Jt as ValueShape, K as statusPaint, Kt as TimeUnit, L as StatusPaint, Lt as toFilterArray, M as normalizeHierarchyNode, Mt as group, N as Rgb, Nt as isBlankCondition, O as UploadResult, Ot as WireGroup, P as StatusGlyph, Pt as isEmptyFilter, Q as STOCK_ICON_KEYS, Qt as isNumericType, R as StatusRecord, Rt as walk, S as SummaryGrouping, St as FilterGroup, T as ThreadAuthor, Tt as Scalar, U as rgbToCss, Ut as OPERATORS_BY_TYPE, V as parseBgColor, Vt as NEGATING_OPERATORS, W as statusGlyph, Wt as Operator, X as STOCK_ICON_CELLS, Xt as isFilterable, Y as NativeStatus, Yt as isDataType, Z as STOCK_ICON_DATA_URLS, Zt as isLinkType, _ as SgClient, _t as statusFieldNameFor, a as EventLogOptions, at as stockIconSource, b as SummaryField, bt as EntityRef, c as HierarchyNode, ct as FieldSchema, d as RawHierarchyNode, dt as RawProperty, en as isTemporalType, et as SpriteCell, f as RestClient, ft as displayNameOf, g as SgApiError, gt as statusFieldFor, h as SearchResult, ht as normalizeFields, i as EventLogEntry, it as spriteStyle, j as normalizeEventLogEntry, jt as fromWire, k as eventLogFilters, kt as condition, l as HierarchyPath, lt as RawFieldSchema, m as SearchOptions, mt as normalizeField, n as EntityRow, nn as supportsOperator, nt as isNativeStatus, o as EventLogResult, ot as DISPLAY_NAME_FIELDS, p as RestClientOptions, pt as fieldSchemaOverride, q as usableStatuses, qt as VALUE_SHAPE, r as EntityTypeInfo, rt as nativeStatus, s as FollowingOptions, st as FIELD_SCHEMA_OVERRIDES, t as EVENT_LOG_FIELDS, tn as operatorsFor, tt as StockIconSource, u as HierarchyRef, ut as RawFieldsResponse, v as SummarizeOptions, vt as undeclaredField, w as TextSearchRow, wt as LogicalOperator, x as SummaryGroup, xt as FilterCondition, y as SummarizeResult, yt as ConditionValue, z as foregroundFor, zt as DATA_TYPES } from "./client-BhHppzTV.mjs";
2
+ //#region src/field-icons.d.ts
3
+ /** The catch-all glyph, used for a data type this build does not know. */
4
+ export declare const DEFAULT_FIELD_ICON = "file-text";
5
+ /** The lucide icon name for a field's data type. */
6
+ export declare function iconNameFor(dataType: string): string;
7
+ /** Every icon name this module can return, for a UI package's name-to-component map. */
8
+ export declare const FIELD_ICON_NAMES: readonly string[];
9
+ //#endregion
10
+ //#region src/filter-ux.d.ts
11
+ /** One label per raw operator, for a type that has no wording of its own. */
12
+ export declare const OPERATOR_LABELS: Readonly<Record<Operator, string>>;
13
+ /** The label a data type puts on an operator. */
14
+ export declare function operatorLabel(operator: Operator, dataType?: string): string;
15
+ /** Singular and plural wording for a relative-date unit. */
16
+ export declare const TIME_UNIT_LABELS: Readonly<Record<TimeUnit, [string, string]>>;
17
+ export declare function timeUnitLabel(unit: TimeUnit, count?: number): string;
18
+ /** How a row edits the value of a preset. */
19
+ export type PresetInput = 'none' | 'scalar' | 'list' | 'range' | 'relative' | 'string';
20
+ /**
21
+ * One entry of the operator menu.
22
+ *
23
+ * `operator` and, for a pinned preset, `value` are what serialisation sends;
24
+ * everything else is wording. A preset whose `id` equals its `operator` is the
25
+ * plain operator with no UX layer on it.
26
+ */
27
+ export interface OperatorPreset {
28
+ id: string;
29
+ label: string;
30
+ operator: Operator;
31
+ input: PresetInput;
32
+ /** The value the preset pins. Present only when `input` is `none`. */
33
+ value?: ConditionValue;
34
+ }
35
+ /** A named run of the operator menu. */
36
+ export interface OperatorGroup {
37
+ label: string;
38
+ presets: OperatorPreset[];
39
+ }
40
+ /**
41
+ * The value "is empty" sends on a type, or `undefined` when the type has no
42
+ * empty test at all.
43
+ *
44
+ * `null` is the spelling on every filterable type but two: uuid takes `""` and
45
+ * 400s on null, and a checkbox is two-state, never null, and 400s on both
46
+ * (field_types/uuid, field_types/checkbox).
47
+ */
48
+ export declare function emptyValueFor(dataType: string): '' | null | undefined;
49
+ export declare function supportsEmpty(dataType: string): boolean;
50
+ /**
51
+ * The operators a field takes: its data type's vocabulary, narrowed by the
52
+ * field's own `operators` where the API evaluates fewer of them.
53
+ */
54
+ export declare function fieldOperators(field?: Pick<FieldSchema, 'dataType' | 'operators'> | null): readonly Operator[];
55
+ /**
56
+ * The operator menu for a data type, grouped. Every entry names the raw operator
57
+ * it serialises to; an unfilterable type gets an empty menu. `operators` narrows
58
+ * the menu to what the field's own site answers.
59
+ */
60
+ export declare function operatorMenu(dataType: string, operators?: readonly Operator[]): OperatorGroup[];
61
+ /**
62
+ * Every menu entry for a data type, in menu order. Calendar operators are
63
+ * replaced by their named offsets and never offered raw: an integer offset with
64
+ * no wording around it is not a control anyone can read.
65
+ */
66
+ export declare function presetsFor(dataType: string, only?: readonly Operator[]): OperatorPreset[];
67
+ export declare function presetById(dataType: string, id: string, operators?: readonly Operator[]): OperatorPreset | undefined;
68
+ /** The menu entry a condition currently sits on. */
69
+ export declare function presetIdOf(condition: FilterCondition, dataType: string): string;
70
+ /**
71
+ * Move a condition onto another menu entry. The value survives when the two
72
+ * entries edit the same shape and is reset to the type's default otherwise, so
73
+ * switching "is" to "is any of" keeps what was typed only where it fits.
74
+ */
75
+ export declare function applyPreset(condition: FilterCondition, preset: OperatorPreset, dataType: string): FilterCondition;
76
+ /** True when the condition sits on a preset that pins its value, so no editor is drawn. */
77
+ export declare function isPinned(condition: FilterCondition, dataType: string): boolean;
78
+ /**
79
+ * The value a row starts on. Blank wherever a person has to supply the value, so
80
+ * a half-built row serialises to nothing; filled where the type has only one
81
+ * sensible starting point (a checkbox is two-state and never null, and a
82
+ * relative window with no length is not a window).
83
+ */
84
+ export declare function defaultValueFor(dataType: string, operator: Operator): ConditionValue;
85
+ /** A blank condition on a field, ready for its value. */
86
+ export declare function defaultCondition(path: string, dataType: string, operators?: readonly Operator[]): FilterCondition;
87
+ /** A condition read as three segments: what is compared, how, and against what. */
88
+ export interface ConditionParts {
89
+ field: string;
90
+ operator: string;
91
+ /** Empty when the operator pins its own value, as `is empty` and the calendar presets do. */
92
+ value: string;
93
+ }
94
+ /**
95
+ * The three segments of a condition's summary. A pill draws them apart and a
96
+ * sentence joins them; both read the same words.
97
+ */
98
+ export declare function conditionParts(condition: FilterCondition, field?: FieldSchema | null): ConditionParts;
99
+ /** A condition's values, as much of them as a pill has room to name. */
100
+ export interface ConditionValues {
101
+ /** The values the pill names, in order. */
102
+ shown: string[];
103
+ /** Values past `shown`, which the pill reads as `+n`. */
104
+ overflow: number;
105
+ /** Every value, comma-joined, for the `title` a truncated pill carries. */
106
+ title: string;
107
+ /** The shown values as one line, for a pill that spells its values rather than drawing them. */
108
+ text: string;
109
+ /** The scalar behind each shown value, for a pill that draws its values rather than spelling them. */
110
+ values: Scalar[];
111
+ }
112
+ /**
113
+ * A condition's values as a pill reads them: the first `max`, at least one, a count of
114
+ * the rest, and the whole list for the title. A condition on any other shape than a
115
+ * list has one value and no overflow, so a pill draws it whole.
116
+ */
117
+ export declare function conditionValues(condition: FilterCondition, field?: FieldSchema | null, max?: number): ConditionValues;
118
+ /**
119
+ * One line naming what a condition matches, such as
120
+ * `Status is any of Approved, Final`. Without a schema the field's dotted path
121
+ * stands in for its label and codes stand in for their display values.
122
+ */
123
+ export declare function describeCondition(condition: FilterCondition, field?: FieldSchema | null): string;
124
+ export type ConditionIssueCode = 'no-field' | 'unknown-field' | 'unfilterable' | 'unknown-operator' | 'wrong-shape' | 'blank-value';
125
+ export interface ConditionIssue {
126
+ code: ConditionIssueCode;
127
+ message: string;
128
+ }
129
+ /**
130
+ * Everything wrong with one row, in the order a person would fix it. An empty
131
+ * array means the row serialises to a filter the API accepts.
132
+ *
133
+ * `field` is the schema of the path's last segment. Pass `null` when the path
134
+ * resolved to nothing and omit it when no schema is loaded, which suppresses
135
+ * every check that needs one.
136
+ */
137
+ export declare function validateCondition(condition: FilterCondition, field?: FieldSchema | null): ConditionIssue[];
138
+ /** The value editor a data type needs, once the operator has chosen the shape. */
139
+ export type ValueEditorKind = 'none' | 'text' | 'number' | 'date' | 'date_time' | 'checkbox' | 'options' | 'entity' | 'color' | 'url';
140
+ /**
141
+ * Which editor a row draws. The operator picks the arity (one value, a list, a
142
+ * range, a relative window) and the data type picks the editor inside it.
143
+ *
144
+ * A `url` field takes no filter at all, so no field list offers one; the kind is
145
+ * here for a tree that carries a path the schema no longer filters on
146
+ * (field_types/url).
147
+ */
148
+ export declare function valueEditorFor(dataType: string, operator: Operator): ValueEditorKind;
149
+ /** Whether a value editor holds one value or many. */
150
+ /** The arity a row draws: none for a pinned preset, else the operator's own. */
151
+ export declare function conditionArity(condition: FilterCondition, dataType: string): 'none' | 'one' | 'many' | 'two' | 'relative';
152
+ export declare function valueArity(operator: Operator): 'none' | 'one' | 'many' | 'two' | 'relative';
153
+ /**
154
+ * The values a list-shaped condition holds, as a row edits them. `in` and `not_in`
155
+ * take a JSON array, so anything else on the condition reads as no values yet.
156
+ */
157
+ export declare function conditionList(value: ConditionValue): Scalar[];
158
+ /** The list with one value replaced. An index outside it leaves the list alone. */
159
+ export declare function withListValue(value: ConditionValue, index: number, next: Scalar): Scalar[];
160
+ /** The list with one value dropped. */
161
+ export declare function withoutListValue(value: ConditionValue, index: number): Scalar[];
162
+ /**
163
+ * The list with one more entry on the end, blank. A blank entry serialises to
164
+ * nothing: a row whose value is still unfilled is dropped rather than sent.
165
+ */
166
+ export declare function withAddedListValue(value: ConditionValue): Scalar[];
167
+ /** A relative-date window, as a filter row edits it. */
168
+ export interface RelativeDate {
169
+ count: number;
170
+ unit: TimeUnit;
171
+ direction: 'last' | 'next';
172
+ /** Negated windows also match rows where the field is unset (017_filter_operators). */
173
+ negated?: boolean;
174
+ }
175
+ export declare function relativeOperator(relative: Pick<RelativeDate, 'direction' | 'negated'>): Operator;
176
+ export declare function relativeValue(relative: Pick<RelativeDate, 'count' | 'unit'>): [number, TimeUnit];
177
+ /** Read a condition back as a relative window, or `null` when it is not one. */
178
+ export declare function relativeFrom(operator: Operator, value: ConditionValue): RelativeDate | null;
179
+ /** The count and the unit a relative row shows, with the default window for an unfilled one. */
180
+ export declare function relativeWindow(value: ConditionValue): {
181
+ count: number | null;
182
+ unit: TimeUnit;
183
+ };
184
+ /** The window with one half replaced. A missing count goes as 1, the smallest the API takes. */
185
+ export declare function withRelativeWindow(value: ConditionValue, next: Partial<{
186
+ count: number | null;
187
+ unit: TimeUnit;
188
+ }>): [number, TimeUnit];
189
+ /**
190
+ * The time units as a field, so a relative row picks its unit with the same list
191
+ * control a `list` field uses. Mandatory: a window always has a unit.
192
+ */
193
+ export declare function timeUnitField(displayName?: string): Pick<FieldSchema, 'displayName' | 'mandatory' | 'validValues' | 'displayValues'>;
194
+ /**
195
+ * Where a node sits in the tree: the index it holds in each group from the root
196
+ * down. `[]` is the root itself.
197
+ */
198
+ export type NodePath = readonly number[];
199
+ export declare function nodeAt(root: FilterNode, path: NodePath): FilterNode | undefined;
200
+ /** A copy of the tree with the node at `path` replaced. The root cannot be replaced this way. */
201
+ export declare function replaceAt(root: FilterGroup, path: NodePath, node: FilterNode): FilterGroup;
202
+ /** A copy of the tree with the node at `path` gone. */
203
+ export declare function removeAt(root: FilterGroup, path: NodePath): FilterGroup;
204
+ /** A copy of the tree with `node` appended to the group at `path`. */
205
+ export declare function appendAt(root: FilterGroup, path: NodePath, node: FilterNode): FilterGroup;
206
+ /** A copy of the tree with the node at `path` moved `delta` places among its siblings. */
207
+ export declare function moveAt(root: FilterGroup, path: NodePath, delta: number): FilterGroup;
208
+ /** Conditions in the tree, nested groups included. */
209
+ export declare function countConditions(node: FilterNode): number;
210
+ /** Conditions that would survive serialisation. */
211
+ export declare function countActiveConditions(node: FilterNode): number;
212
+ /** The first condition on a field path, with where it sits. */
213
+ export declare function findCondition(root: FilterNode, path: string, at?: NodePath): {
214
+ at: NodePath;
215
+ condition: FilterCondition;
216
+ } | undefined;
217
+ /**
218
+ * A copy of the tree with every condition on one of `paths` gone. A group the
219
+ * pruning emptied goes with it, so the `or` of `is` conditions a facet writes
220
+ * leaves nothing behind.
221
+ */
222
+ export declare function withoutPaths(root: FilterGroup, paths: readonly string[]): FilterGroup;
223
+ /**
224
+ * How a facet spells a set of ticked values on a field.
225
+ *
226
+ * A field the API evaluates `in` on takes one condition holding the list. A field
227
+ * it does not takes one condition per value: `is` joined by `or`, `is_not` joined
228
+ * by `and`, which is the same set of rows.
229
+ */
230
+ export interface FacetShape {
231
+ /** The operator a ticked set writes. */
232
+ any: Operator;
233
+ /** The operator a negated set writes. */
234
+ none: Operator;
235
+ /** True where a set of several values is one condition per value. */
236
+ spread: boolean;
237
+ }
238
+ export declare function facetShape(field?: Pick<FieldSchema, 'dataType' | 'operators'> | null): FacetShape;
239
+ /** The node a facet contributes, read back as one checklist. */
240
+ export interface FacetCondition {
241
+ /** Where the node sits in the tree. */
242
+ at: NodePath;
243
+ /** The condition, or the group of one-value conditions, the facet wrote. */
244
+ node: FilterNode;
245
+ /**
246
+ * The condition a pill reads. A group of one-value conditions stands in as one
247
+ * list-shaped condition, so a pill draws every facet the same way. Never serialised.
248
+ */
249
+ summary: FilterCondition;
250
+ /** The operator the checklist writes, `in` or `is` and their negations. */
251
+ operator: Operator;
252
+ /** The values ticked. Empty where the node is not a checklist. */
253
+ values: Scalar[];
254
+ /** True where the node is a checklist this facet can edit, false where the editor wrote it. */
255
+ checklist: boolean;
256
+ }
257
+ /**
258
+ * The node a facet contributes on `path`, wherever it sits: the condition it
259
+ * wrote, or the group of one-value conditions it wrote on a field the API
260
+ * evaluates no `in` on.
261
+ */
262
+ export declare function findFacet(root: FilterNode, path: string, field?: Pick<FieldSchema, 'dataType' | 'operators'> | null, at?: NodePath): FacetCondition | undefined;
263
+ /**
264
+ * Set the condition a facet contributes: replaced where one exists, appended to
265
+ * the root where none does, removed when nothing is ticked. `operator` is the
266
+ * list operator the facet holds, spelled out as one `is` per value on a field the
267
+ * API evaluates no `in` on.
268
+ */
269
+ export declare function setFacet(root: FilterGroup, path: string, values: readonly Scalar[], operator?: Operator, field?: Pick<FieldSchema, 'dataType' | 'operators'> | null): FilterGroup;
270
+ /** A facet's condition moved onto another of its menu entries. */
271
+ export declare function setFacetPreset(root: FilterGroup, path: string, preset: OperatorPreset, dataType: string, field?: Pick<FieldSchema, 'dataType' | 'operators'> | null): FilterGroup;
272
+ /** One value a facet offers, with how many rows hold it. */
273
+ export interface FacetValue {
274
+ /** Stable identity for the UI. An entity is `Type:id`; everything else is its own string. */
275
+ key: string;
276
+ label: string;
277
+ /** The value an `in` condition sends. */
278
+ value: Scalar;
279
+ count: number;
280
+ }
281
+ /** What one facet lists, and where its counts came from. */
282
+ export interface FacetList {
283
+ values: FacetValue[];
284
+ /**
285
+ * The rows the counts were tallied from, where they came from a page of rows rather
286
+ * than the site's groups. Absent where every count is the site's own.
287
+ */
288
+ sampled?: number;
289
+ }
290
+ /** The groups of a `_summarize` call grouped on `field` under `filters`. */
291
+ export type FacetCounts = (field: string, filters: WireGroup | null) => Promise<SummaryGroup[]>;
292
+ /** What a facet reads: the site's groups, and one page of rows for a field it cannot group. */
293
+ export interface FacetReads {
294
+ counts?: FacetCounts | undefined;
295
+ /** One page of rows carrying `fields`, under `filters`. */
296
+ sample: (fields: readonly string[], filters: WireGroup | null) => Promise<readonly EntityRow[]>;
297
+ /**
298
+ * Fields the site refused to group, as `Type.field`, held by the caller across reads
299
+ * so the site is asked once per field. Filled here.
300
+ */
301
+ refused?: Set<string>;
302
+ }
303
+ /**
304
+ * The values a facet offers, tallied from a page of rows.
305
+ *
306
+ * The site vocabulary of a `list` or `status_list` field comes from the schema so
307
+ * a value nobody holds still appears at zero; every other type can only be
308
+ * discovered from the rows that were read, so its list is as complete as the page
309
+ * size allowed.
310
+ */
311
+ export declare function facetValues(rows: readonly EntityRow[], field: FieldSchema): FacetValue[];
312
+ /**
313
+ * The values a facet offers, from the groups of a `_summarize` call grouped on its
314
+ * field. A group is keyed on `group_value`, which on an entity field is the reference
315
+ * itself, so two people sharing a display name stay two rows, and `group_name` is the
316
+ * label. The `''` group, the rows with nothing in the field, is left out: the empty
317
+ * tests belong to the dialog (020_summarize). A multi_entity group's value is the
318
+ * row's set of links as an array of references, and it counts towards each of them;
319
+ * the corpus has not measured that grouping.
320
+ */
321
+ export declare function facetValuesFromGroups(groups: readonly SummaryGroup[], field: FieldSchema): FacetValue[];
322
+ /** The `counts` reader over a client: one `_summarize` call grouped on the field. */
323
+ export declare function facetCounts(client: Pick<SgClient, 'summarize'>, entityType: string): FacetCounts;
324
+ /**
325
+ * The filter each facet counts against: the base filter and the whole tree, less the
326
+ * facet's own conditions, so a facet keeps every value it could switch to while the
327
+ * others show what remains. Keyed by facet name; a facet nobody ticked shares the
328
+ * whole tree with its neighbours.
329
+ */
330
+ export declare function facetScopes(value: FilterGroup, base: FilterGroup | null, facets: readonly string[]): Record<string, WireGroup | null>;
331
+ /**
332
+ * True where the site refused to group the field: a 4xx. The measured refusal is
333
+ * 400 `Grouping is not allowed for field <Type>.<field>.` (field_types/image,
334
+ * field_types/summary); the status decides, never the wording.
335
+ */
336
+ export declare function isGroupingRefusal(error: unknown): boolean;
337
+ /**
338
+ * What every facet lists, each counted under its own scope.
339
+ *
340
+ * With `counts`, a field's values come from the site's groups, and a field the site
341
+ * refuses to group is tallied from one page of rows instead, with the schema's
342
+ * vocabulary at zero and `sampled` saying how many rows; the refusal is kept in
343
+ * `refused` so the next read tallies that field without asking. Without `counts`
344
+ * every field is tallied that way. Facets sharing a scope share one page. Any other
345
+ * failure fails the read.
346
+ */
347
+ export declare function facetLists(fields: readonly FieldSchema[], scopes: Record<string, WireGroup | null>, reads: FacetReads): Promise<Record<string, FacetList>>;
348
+ /**
349
+ * The menu a facet pill's operator segment offers: the operators that take the
350
+ * checklist's list of values, and the empty tests, which take none. A field the
351
+ * API evaluates no `in` on still offers both list entries, since the facet spells
352
+ * a list out as one condition per value.
353
+ */
354
+ export declare function facetPresets(dataType: string, field?: Pick<FieldSchema, 'dataType' | 'operators'> | null): OperatorPreset[];
355
+ export declare function isSortable(dataType: string): boolean;
356
+ /** Fields a sort picker may offer, sorted by display name. */
357
+ export declare function sortableFields(fields: Record<string, FieldSchema>, options?: {
358
+ hidePaths?: readonly string[];
359
+ }): FieldSchema[];
360
+ export interface SortKey {
361
+ /** Field name or dotted path. */
362
+ field: string;
363
+ direction: 'asc' | 'desc';
364
+ }
365
+ /**
366
+ * The `sort` string `_search` takes: field names comma-joined, each descending
367
+ * one prefixed with `-`. A leading `+` and a trailing ` desc` are both 400s, and
368
+ * id ascending is the implicit tiebreak (026_result_order).
369
+ */
370
+ export declare function toSortString(keys: readonly SortKey[]): string;
371
+ export declare function fromSortString(sort: string | null | undefined): SortKey[];
372
+ /** A path pattern hides the exact path and everything under it. */
373
+ export declare function isHiddenPath(path: string, hidePaths: readonly string[] | undefined): boolean;
374
+ /** Fields a filter row may choose, sorted by display name. */
375
+ export declare function filterableFields(fields: Record<string, FieldSchema>, options?: {
376
+ hidePaths?: readonly string[];
377
+ dataTypes?: readonly DataType[];
378
+ }): FieldSchema[];
379
+ //#endregion
380
+ //#region src/render.d.ts
381
+ /**
382
+ * The renderings a value can need. Deliberately small: several data types share a
383
+ * rendering, and the exact number formatting is chosen from the `dataType` itself.
384
+ */
385
+ export type RenderKind = 'text' | 'number' | 'date' | 'datetime' | 'entity' | 'multi_entity' | 'status' | 'image' | 'checkbox' | 'url' | 'color' | 'list' | 'empty';
386
+ /** The rendering a data type needs. Unknown types fall back to text. */
387
+ export declare function renderKindFor(dataType: string): RenderKind;
388
+ export interface FieldTextOptions {
389
+ /** The site's `hours_per_day`; a duration then renders in days (field_types/duration). */
390
+ hoursPerDay?: number;
391
+ locale?: string;
392
+ /** IANA zone a `date_time` is shown in. Defaults to the runtime's, which is what "local" means to a viewer. */
393
+ timeZone?: string;
394
+ /** Frames a second, for a timecode. Given, a timecode gains its frame digits (field_types/timecode). */
395
+ frameRate?: number;
396
+ /** Decimals kept on a float or a currency. */
397
+ decimals?: number;
398
+ currencySymbol?: string;
399
+ /** The status field's `display_values`, the only source of a status label (field_types/status_list). */
400
+ displayValues?: Record<string, string>;
401
+ /** Between the names of a multi_entity value. */
402
+ separator?: string;
403
+ }
404
+ /**
405
+ * Any value as one line of text.
406
+ *
407
+ * The renderings a widget usually draws with a component of its own -- a status
408
+ * badge, a thumbnail, a chip -- come back as their plain label, so a compact
409
+ * surface can show them as text and a caller can copy or export them.
410
+ */
411
+ export declare function fieldText(value: unknown, dataType: string, options?: FieldTextOptions): string;
412
+ /**
413
+ * True when there is nothing to show. `0`, `false` and `"0"` are values, not
414
+ * emptiness: a `number` holding 0 is `is_not None` on the server too
415
+ * (field_types/number, percent, duration).
416
+ */
417
+ export declare function isEmptyValue(value: unknown): boolean;
418
+ export interface DurationFormatOptions {
419
+ /**
420
+ * The site's `hours_per_day` from `GET /preferences`. Given, the duration is
421
+ * rendered in days, which is what a site with `duration_units: "days"` expects.
422
+ * The field schema names no unit; only the site does (field_types/duration).
423
+ */
424
+ hoursPerDay?: number;
425
+ }
426
+ /** A stored duration is a whole number of minutes (field_types/duration). */
427
+ export declare function formatDuration(minutes: number | null | undefined, options?: DurationFormatOptions): string;
428
+ /**
429
+ * A percent is a bare integer on a 0-100 scale, and nothing is clamped: -1 and
430
+ * 1000 both store at 200, so render what is there (field_types/percent).
431
+ */
432
+ export declare function formatPercent(value: number | string | null | undefined): string;
433
+ export interface TimecodeFormatOptions {
434
+ /**
435
+ * Frames a second, for the `HH:MM:SS:FF` form. No schema property and no
436
+ * preference names the rate, so it comes from the app; solving it out of a
437
+ * `_summarize` grouping gives 23.976 on the probed site
438
+ * (field_types/timecode).
439
+ */
440
+ frameRate?: number;
441
+ }
442
+ /**
443
+ * A timecode is milliseconds in a signed 32-bit integer. Nothing wraps at 24
444
+ * hours and negatives are stored, so hours are not clamped either
445
+ * (field_types/timecode). Without a frame rate the frame digits are left off;
446
+ * with one they are the sub-second remainder rounded to the nearest frame, which
447
+ * is how the server renders it.
448
+ */
449
+ export declare function formatTimecode(ms: number | null | undefined, options?: TimecodeFormatOptions): string;
450
+ export interface FloatFormatOptions {
451
+ /** Decimals to keep at most; trailing zeros are dropped. The store itself rounds to 6 (field_types/float). */
452
+ maxDecimals?: number;
453
+ /** Exactly this many decimals, zeros kept. Wins over `maxDecimals`. */
454
+ decimals?: number;
455
+ }
456
+ /**
457
+ * A float reads back as a JSON *string* rounded to 6 decimals ("25.0"), never a
458
+ * JSON number, so it is formatted from the string to avoid a needless round trip
459
+ * through binary (field_types/float).
460
+ */
461
+ /**
462
+ * A currency amount with its symbol. There is no corpus card for `currency`; the
463
+ * read shape is treated as a decimal number and the symbol is the caller's.
464
+ */
465
+ export declare function formatCurrency(value: number | string | null | undefined, options?: {
466
+ symbol?: string;
467
+ decimals?: number;
468
+ locale?: string;
469
+ }): string;
470
+ export declare function formatFloat(value: number | string | null | undefined, options?: FloatFormatOptions): string;
471
+ export interface DateFormatOptions {
472
+ locale?: string | string[];
473
+ /** IANA zone. Defaults to the runtime's, which is what "local" means to a viewer. */
474
+ timeZone?: string;
475
+ }
476
+ /**
477
+ * A `date` is exactly "YYYY-MM-DD": no time, no zone (field_types/date). It is
478
+ * therefore formatted in UTC whatever the viewer's zone, because shifting a
479
+ * zoneless day into a local zone moves it to the wrong day.
480
+ */
481
+ export declare function formatDate(value: string | null | undefined, options?: DateFormatOptions): string;
482
+ /**
483
+ * A `date_time` is always UTC "YYYY-MM-DDTHH:MM:SSZ" at second resolution
484
+ * (field_types/date_time). It is shown in the viewer's zone; keep the raw string
485
+ * as a `title` so the stored instant stays readable.
486
+ */
487
+ export declare function formatDateTime(value: string | null | undefined, options?: DateFormatOptions): string;
488
+ /** The `upload`/`web` and `local` shapes of a `url` field (field_types/url). */
489
+ export interface UrlValue {
490
+ url?: string | null;
491
+ name?: string | null;
492
+ content_type?: string | null;
493
+ link_type?: 'upload' | 'web' | 'local' | string;
494
+ local_path_mac?: string | null;
495
+ local_path_linux?: string | null;
496
+ local_path_windows?: string | null;
497
+ relative_path?: string | null;
498
+ type?: string;
499
+ id?: number;
500
+ }
501
+ /**
502
+ * The last path segment of a URL or path, percent-decoded, with the query
503
+ * dropped. A presigned attachment URL carries the signature in the query and the
504
+ * content hash in the path, so the query is never part of a file name
505
+ * (field_types/url, field_types/image).
506
+ */
507
+ export declare function fileNameFromUrl(url: string | null | undefined): string;
508
+ export type LocalPlatform = 'mac' | 'linux' | 'windows';
509
+ /** The platform the current browser reports, for choosing which local path to open. */
510
+ export declare function currentPlatform(): LocalPlatform;
511
+ /** `file:` URL for a local path: a Windows drive path gains a third slash, and every segment is encoded. */
512
+ export declare function fileHref(path: string): string;
513
+ export interface UrlLinkInfo {
514
+ /** Where to open, or null when nothing can be opened. A local link opens through `file:`. */
515
+ href: string | null;
516
+ label: string;
517
+ /** Present on a `local` link: the path per platform, and the one for the current platform. */
518
+ local?: {
519
+ mac: string | null;
520
+ linux: string | null;
521
+ windows: string | null;
522
+ path: string | null;
523
+ };
524
+ }
525
+ /**
526
+ * A displayable link out of a `url` value. Three shapes exist and no fourth:
527
+ * `upload`/`web` (has `url` and `name`), `local` (paths, no `url`), and a bare
528
+ * string (field_types/url). A local link resolves to a `file:` href for the
529
+ * current platform; browsers refuse to follow one from an http page, so an app
530
+ * that opens paths another way rewrites it with its own scheme.
531
+ */
532
+ export declare function urlLink(value: unknown, platform?: LocalPlatform): UrlLinkInfo | null;
533
+ /** The prefix Flow PT serves while a thumbnail is still transcoding (field_types/image). */
534
+ export declare const THUMBNAIL_PENDING_PATH = "/images/status/transient/";
535
+ /**
536
+ * An `image` value is the only state marker there is: test the transient prefix,
537
+ * never truthiness (field_types/image).
538
+ */
539
+ export declare function imageState(value: string | null | undefined): 'none' | 'pending' | 'ready';
540
+ /**
541
+ * `Task.color` holds this token instead of a colour, meaning "use the colour of
542
+ * my Pipeline Step". Resolve it with a dotted `step.Step.color` read in the same
543
+ * call and keep a client default (field_types/color).
544
+ */
545
+ export declare const COLOR_SENTINEL = "pipeline_step";
546
+ /**
547
+ * Initials for an avatar fallback, from a person's display name. Takes the first
548
+ * letter of the first and last word, so "Ada Lovelace" is AL and "Anna van der
549
+ * Meer" is AM. A single word gives one letter; a `login` such as `j.doe` is
550
+ * split on its punctuation too.
551
+ */
552
+ /**
553
+ * A stable index in `[0, count)` for a name, so the same person always gets the same
554
+ * tint. FNV-1a over the code points; the name alone, so a login and a display name
555
+ * of one person may differ.
556
+ */
557
+ export declare function nameColorIndex(name: string | null | undefined, count?: number): number;
558
+ /** Eight hues far enough apart to tell neighbours apart, skipping the muddy yellows. */
559
+ export declare const NAME_HUES: readonly [15, 45, 95, 150, 195, 240, 285, 330];
560
+ /** A stable hue for a name, for tinting initials independently of the theme. */
561
+ export declare function nameHue(name: string | null | undefined): number;
562
+ export declare function initialsOf(name: string | null | undefined, max?: number): string;
563
+ //#endregion
564
+ //#region src/state.d.ts
565
+ /**
566
+ * The empty, loading and error states of `docs/design-rules.md` rule 5.
567
+ *
568
+ * Every data widget takes the same three labels and draws the same block. The
569
+ * defaults are here so the two registries cannot drift and a studio that wants
570
+ * its own wording sets one prop per widget. A translation seam belongs at this
571
+ * boundary; until a non-English studio asks for one, these are the strings.
572
+ */
573
+ /** What a data widget is showing: one of the three state blocks, or its rows. */
574
+ export type WidgetState = 'error' | 'loading' | 'empty' | 'rows';
575
+ /** What a data widget calls its empty, loading and error states. */
576
+ export interface StateLabels {
577
+ /** Shown when the read or the query returned nothing. */
578
+ emptyLabel?: string;
579
+ /** The accessible name of the skeletons a read stands behind. */
580
+ loadingLabel?: string;
581
+ /** Shown in place of what the failed read said. */
582
+ errorLabel?: string;
583
+ }
584
+ /** A query matched nothing. */
585
+ export declare const NO_MATCH_LABEL = "No match";
586
+ /** A read returned nothing. */
587
+ export declare const NO_ROWS_LABEL = "No rows";
588
+ /** A list of what the caller has picked holds nothing. */
589
+ export declare const NOTHING_CHOSEN_LABEL = "Nothing chosen";
590
+ /** A read is in flight. */
591
+ export declare const LOADING_LABEL = "Loading…";
592
+ /** A read failed and said nothing a reader can use. */
593
+ export declare const ERROR_LABEL = "Something went wrong";
594
+ /**
595
+ * The line a state block shows.
596
+ *
597
+ * An error falls back to what the read said and then to a fixed line, so a
598
+ * failure is never a blank block; a caller's `errorLabel` replaces both.
599
+ */
600
+ export declare function stateLine(state: WidgetState, labels: StateLabels, message?: string | null): string;
601
+ /**
602
+ * What a failed read said, as a line. An `Error` gives its message; anything else
603
+ * a promise rejected with is rendered as itself, so a thrown string still reads.
604
+ */
605
+ export declare function errorText(error: unknown): string;
606
+ //#endregion
607
+ //#region src/search.d.ts
608
+ /** The words the API matches on: whitespace-separated, empties dropped. */
609
+ export declare function searchWords(text: string): string[];
610
+ /** One stretch of a label, either inside a matched word or outside every one. */
611
+ export interface MatchRun {
612
+ text: string;
613
+ match: boolean;
614
+ }
615
+ /**
616
+ * Split a label into alternating plain and matched runs, one run per stretch.
617
+ * Matching is case-insensitive and every word is highlighted wherever it occurs,
618
+ * because that is what the server matched on. Overlapping words merge into one
619
+ * run, so a run never nests and the runs always rebuild the label exactly.
620
+ *
621
+ * The result is text, never markup: a widget draws each run as an element and
622
+ * never sets HTML from a row. It is the one splitting in this package, and
623
+ * `highlightRuns` is the name the pickers were written against.
624
+ */
625
+ export declare function matchRuns(label: string, query: string): MatchRun[];
626
+ /**
627
+ * True when every word of the query appears in the text, the server's own rule.
628
+ * It is also the rule a list filters its own rows with, over the row's label and
629
+ * code joined into one text.
630
+ */
631
+ export declare function matchesEveryWord(text: string, query: string): boolean;
632
+ /** One search result, with what a row needs to draw itself. */
633
+ export interface SearchHit {
634
+ /** The row, `name` filled from the search answer. */
635
+ ref: EntityRef;
636
+ /** Status code, never a label. */
637
+ status: string | null;
638
+ /** The row `_text_search` also matched the words against, when the row links to one. */
639
+ link: {
640
+ type: string;
641
+ name: string;
642
+ } | null;
643
+ /** `image`, when the type has one. Presigned and short-lived (field_types/image). */
644
+ image: string | null;
645
+ /** The row's project, `name` being its `cached_display_name`. Null on a site-wide type. */
646
+ project: EntityRef | null;
647
+ /**
648
+ * Attributes and relationships of the second read, in one map, so a row anatomy
649
+ * reads any field a caller asked for. A relationship is unwrapped to its `data`.
650
+ */
651
+ values: Record<string, unknown>;
652
+ }
653
+ /** A hit with only what `_text_search` itself answers. */
654
+ export declare function toSearchHit(row: TextSearchRow): SearchHit;
655
+ /**
656
+ * Fill in what `_text_search` does not answer: one `search` per type over the ids
657
+ * just returned. An unknown field name is dropped at 200, so asking every type for
658
+ * `image`, `project` and whatever a row anatomy names is safe even where the type
659
+ * carries none of it (probe 003).
660
+ */
661
+ export declare function hydrate(client: SgClient, rows: TextSearchRow[], options?: {
662
+ /** Fields on top of the identity ones, for a caller's own sub-label or secondary. */
663
+ fields?: readonly string[] | undefined;
664
+ /** Field holding the label. The display-name chain answers it when absent. */
665
+ labelField?: string | undefined;
666
+ }): Promise<SearchHit[]>;
667
+ /** Enough of `SchemaService` to answer whether a type carries a field. */
668
+ export interface FieldLookup {
669
+ field(entityType: string, name: string): Promise<{
670
+ name: string;
671
+ } | undefined>;
672
+ }
673
+ /**
674
+ * Add a project condition to every searched type that has a `project` field.
675
+ * A type without one 400s on the path rather than returning nothing: Project has
676
+ * no `project` field at all (entity_types/Project) and neither does Step
677
+ * (entity_types/Step), so the schema decides which types can be scoped.
678
+ */
679
+ export declare function scopeToProject(schema: FieldLookup, entityTypes: Record<string, WireCondition[] | null | undefined>, projectId: number): Promise<Record<string, WireCondition[]>>;
680
+ /**
681
+ * The pause a search widget leaves before it asks. Long enough that a typist does
682
+ * not fire a request a letter, short enough to feel live.
683
+ */
684
+ export declare const SEARCH_DEBOUNCE_MS = 250;
685
+ /** The page `_text_search` answers at its cap, which is also its default (probe 053). */
686
+ export declare const SEARCH_PAGE_SIZE = 25;
687
+ /**
688
+ * The rows a hierarchy search asks for. Each hit costs one path lookup on top of the
689
+ * search itself, so it asks for fewer than the endpoint allows.
690
+ */
691
+ export declare const HIERARCHY_LEAF_LIMIT = 10;
692
+ /**
693
+ * True when a further page may be there. A read carries no total and `links.next` is
694
+ * emitted forever, so a full page is the only sign of another one (006_pagination).
695
+ */
696
+ export declare function hasMorePage(count: number, size: number): boolean;
697
+ /** The project a tree root path names, or null on the site root. */
698
+ export declare function projectOfPath(path: string): number | null;
699
+ /**
700
+ * What a search does with the query it now holds: empty the list, ask at once, or
701
+ * ask once the pause has elapsed.
702
+ *
703
+ * A widget that browses rather than matching reads on an empty query too, which is
704
+ * what `readsEmpty` says; nothing is debounced there, because no one is typing.
705
+ */
706
+ export type QueryPlan = 'clear' | 'now' | 'debounce';
707
+ export declare function queryPlan(query: string, readsEmpty: boolean): QueryPlan;
708
+ export interface SearchViewState {
709
+ /** What the failed read said, or null. */
710
+ error: string | null;
711
+ loading: boolean;
712
+ /** Rows on show. */
713
+ count: number;
714
+ /** An empty list is the empty line only once something has been asked for. */
715
+ asked: boolean;
716
+ }
717
+ /**
718
+ * What a search list draws. The skeletons stand for a first page only: a page on the
719
+ * way under rows already on screen leaves those rows where they are.
720
+ */
721
+ export declare function searchView(state: SearchViewState): WidgetState;
722
+ /**
723
+ * The ticket an answer has to still hold to be written.
724
+ *
725
+ * A search cancels by taking the next ticket: whatever is in flight then holds a
726
+ * stale one and is dropped rather than landing over the query that replaced it.
727
+ */
728
+ export interface RequestGate {
729
+ /** Take the next ticket. Every earlier one is stale from here on. */
730
+ next: () => number;
731
+ /** Drop whatever is in flight without starting anything. */
732
+ cancel: () => void;
733
+ /** True while `ticket` is the one that may write. */
734
+ holds: (ticket: number) => boolean;
735
+ }
736
+ export declare function requestGate(): RequestGate;
737
+ /**
738
+ * How long a press stays answerable for what it set off. A press that replaces the
739
+ * rows under it does so while the event it started is still unfolding, and each
740
+ * framework flushes that on its own schedule, so the mark outlives the press by a
741
+ * little and no longer.
742
+ */
743
+ export declare const PRESS_WINDOW_MS = 400;
744
+ /**
745
+ * The press a replacement is the consequence of.
746
+ *
747
+ * A press inside a list that replaces the rows under it leaves the caret on a row
748
+ * that is gone. `mark` is the press; `takes` answers true once, to whatever the
749
+ * press led to inside the window.
750
+ */
751
+ export interface PressGate {
752
+ /** A press landed inside the list. */
753
+ mark: () => void;
754
+ /** True when a press is still answerable, and spends it. */
755
+ takes: () => boolean;
756
+ }
757
+ export declare function pressGate(windowMs?: number): PressGate;
758
+ /** The types to search, as the map `_text_search` takes: bare names carry no filter. */
759
+ export declare function searchTypeMap(types: string[] | Record<string, WireCondition[] | null>): Record<string, WireCondition[] | null>;
760
+ /**
761
+ * A list of what was picked before, newest first: the new entry leads, the one it
762
+ * repeats is dropped, and the list is cut to `limit`.
763
+ */
764
+ export declare function prependRecent<T>(recents: readonly T[], picked: T, limit: number, keyOf: (item: T) => string): T[];
765
+ /**
766
+ * The rows a tree path runs through, root first.
767
+ *
768
+ * A path is read left to right. A capitalised segment names the type the level
769
+ * below is drawn from; `<Type>/<id>` and `<field>/<Type>/<id>` are a row of that
770
+ * type, and a bare `id/<n>` is a row of the type the last folder named. The path
771
+ * runs through field names such as `sg_sequence`, because the tree follows the
772
+ * site's own navigation configuration rather than a fixed hierarchy
773
+ * (post_hierarchy_search).
774
+ */
775
+ export declare function pathRefs(path: string | string[]): EntityRef[];
776
+ /** The breadcrumb a hierarchy row shows: the path rendered for a person, then the row. */
777
+ export declare function breadcrumb(path: HierarchyPath): string[];
778
+ //#endregion
779
+ //#region src/query.d.ts
780
+ export interface QueryCacheOptions {
781
+ /**
782
+ * How long a resolved value stays fresh, in milliseconds. Default 30000.
783
+ * `Infinity` keeps values until `invalidate()`; `0` disables caching and
784
+ * leaves only the in-flight deduping.
785
+ */
786
+ ttlMs?: number;
787
+ }
788
+ export interface QueryCache extends SgClient {
789
+ /**
790
+ * Forget cached values. With no argument, all of them. With a prefix, every
791
+ * key that starts with it; a key is `<method>:<json args>`, so `'fields'`
792
+ * drops every schema read and `'fields:["Shot"'` drops only Shot's.
793
+ */
794
+ invalidate(prefix?: string): void;
795
+ }
796
+ export declare function createQueryCache(client: SgClient, options?: QueryCacheOptions): QueryCache;
797
+ //#endregion
798
+ //#region src/edit.d.ts
799
+ /** A parsed value, or the reason the input was refused. */
800
+ export type ParseResult<T> = {
801
+ value: T;
802
+ } | {
803
+ error: string;
804
+ };
805
+ export declare function isParseError<T>(result: ParseResult<T>): result is {
806
+ error: string;
807
+ };
808
+ /**
809
+ * The column behind `number`, `percent`, `duration` and `timecode` is a signed
810
+ * 32-bit integer; one past either end is a 400 (field_types/number, percent,
811
+ * timecode).
812
+ */
813
+ export declare const INT32_MIN = -2147483648;
814
+ export declare const INT32_MAX = 2147483647;
815
+ /** Decimals a `float` keeps. Rounding happens on write, at 200 and without warning (field_types/float). */
816
+ export declare const FLOAT_PRECISION = 6;
817
+ /**
818
+ * Free text. Both ends are stripped on write and an empty string is stored as
819
+ * null, so there is no "set but blank" state to preserve (field_types/text).
820
+ */
821
+ export declare function parseTextInput(raw: string): ParseResult<string | null>;
822
+ export interface IntegerParseOptions {
823
+ min?: number;
824
+ max?: number;
825
+ }
826
+ /**
827
+ * A whole number for `number`, `percent` and `timecode`. A decimal is refused
828
+ * rather than rounded, matching the API: `number` and `percent` 400 on a Float
829
+ * and `timecode` takes `[Integer, NilClass]` alone (field_types/number, percent,
830
+ * timecode). Empty input clears the field.
831
+ */
832
+ export declare function parseInteger(raw: string, options?: IntegerParseOptions): ParseResult<number | null>;
833
+ export interface FloatParseOptions {
834
+ /** Decimals to keep. Defaults to the six the store itself keeps (field_types/float). */
835
+ precision?: number;
836
+ }
837
+ /**
838
+ * A decimal for `float`. The store rounds to six decimals on write, so the parse
839
+ * rounds too and the value a caller emits is the value that comes back
840
+ * (field_types/float). Empty input clears the field.
841
+ */
842
+ export declare function parseFloatInput(raw: string, options?: FloatParseOptions): ParseResult<number | null>;
843
+ /**
844
+ * The wire form of a float. An Integer is refused on write and inside a filter,
845
+ * so a whole value has to carry a decimal point; JSON has no way to write one, so
846
+ * the value goes as the numeric string the API also accepts (field_types/float).
847
+ */
848
+ export declare function toApiFloat(value: number | null): string | null;
849
+ export interface DurationParseOptions {
850
+ /** The site's `hours_per_day` from `GET /preferences`. Without it the `d` unit is refused. */
851
+ hoursPerDay?: number;
852
+ }
853
+ /**
854
+ * A duration, in whole minutes.
855
+ *
856
+ * The stored integer is minutes and no schema names the unit, so the accepted
857
+ * spellings are the client's own: a bare number is minutes, `1h 30m` and `1.5h`
858
+ * are hours, and `1:30` is hours and minutes. `2d` needs `hours_per_day`, which
859
+ * only the site's preferences hold, and is refused without it. The API itself
860
+ * takes only something that parses as a number, so `"1h 30m"` never leaves this
861
+ * function (field_types/duration).
862
+ */
863
+ export declare function parseDurationInput(raw: string, options?: DurationParseOptions): ParseResult<number | null>;
864
+ export interface TimecodeParseOptions {
865
+ /** Frames a second, without which the frame digits of a typed timecode are refused. */
866
+ frameRate?: number;
867
+ }
868
+ /**
869
+ * A timecode, in milliseconds.
870
+ *
871
+ * The stored integer is milliseconds: `1000` renders as one whole second in the
872
+ * server's own `_summarize` grouping. The field rejects every timecode string,
873
+ * including the drop-frame spelling, so the conversion is the client's
874
+ * (field_types/timecode). Empty input clears the field.
875
+ */
876
+ export declare function parseTimecodeInput(raw: string, options?: TimecodeParseOptions): ParseResult<number | null>;
877
+ /** The bounds and the amount one step moves, for one numeric data type. */
878
+ export interface NumberSteps {
879
+ step: number;
880
+ min?: number;
881
+ max?: number;
882
+ }
883
+ export interface NumberStepsOptions {
884
+ /** Frames per second. A timecode steps one frame when it is known, one second when it is not. */
885
+ frameRate?: number;
886
+ }
887
+ /**
888
+ * The step and the bounds a numeric data type takes when the caller names none.
889
+ *
890
+ * A percent runs 0 to 100 because that is the scale it is read on; the column
891
+ * itself clamps nothing, so a caller with out-of-scale data widens the bounds
892
+ * (field_types/percent). A duration steps a quarter of an hour, a timecode one
893
+ * frame, and a float a tenth.
894
+ */
895
+ export declare function numberSteps(dataType: string, options?: NumberStepsOptions): NumberSteps;
896
+ export interface StepOptions {
897
+ step: number;
898
+ /** Steps taken at once: ten with Shift, a hundred with Page Up and Page Down. */
899
+ multiplier?: number;
900
+ min?: number;
901
+ max?: number;
902
+ }
903
+ /**
904
+ * Where a step lands: rounded to the decimals the step and the starting value
905
+ * carry, then clamped. The rounding is what keeps a tenth-sized step off
906
+ * `0.30000000000000004`.
907
+ */
908
+ export declare function settleStep(from: number | null, to: number, options: StepOptions): number;
909
+ /** The value one step away. An empty field lands on zero first, then steps. */
910
+ export declare function stepNumber(from: number | null, direction: 1 | -1, options: StepOptions): number;
911
+ export interface NumberInputFormat {
912
+ locale?: string;
913
+ /** Exactly this many decimals, zeros kept. */
914
+ decimals?: number;
915
+ }
916
+ /** A number written the way the locale writes it, for putting a stored value back in an input. */
917
+ export declare function formatNumberInput(value: number, options?: NumberInputFormat): string;
918
+ /**
919
+ * The digits behind a locale-formatted number: spacing and group marks dropped,
920
+ * the decimal mark turned into a point, so the parsers above read back what the
921
+ * formatter wrote. A comma is a group mark in every locale that does not spell
922
+ * its decimal with one.
923
+ */
924
+ export declare function unformatNumberInput(raw: string, locale?: string): string;
925
+ /**
926
+ * What the six numeric data types need beyond the type's own name: the site
927
+ * preferences a duration and a timecode read in, the decimals a float keeps, the
928
+ * bounds an integer is held to, and the locale a plain number is written in.
929
+ */
930
+ export interface NumberShape {
931
+ /** The site's `hours_per_day` from `GET /preferences`, for the `d` unit (field_types/duration). */
932
+ hoursPerDay?: number;
933
+ /** Frames per second, for the `HH:MM:SS:FF` form and the one-frame step (field_types/timecode). */
934
+ frameRate?: number;
935
+ /** Decimals kept on a float. The store itself keeps six (field_types/float). */
936
+ precision?: number;
937
+ min?: number;
938
+ max?: number;
939
+ /** Locale the value is written in. Defaults to the runtime's. */
940
+ locale?: string;
941
+ }
942
+ /** The stored value as the string an input shows. Each type round-trips through its own parse. */
943
+ export declare function numberDraft(value: unknown, dataType: string, shape?: NumberShape): string;
944
+ /** What the numeric parses read: the locale's group and decimal marks are undone first. */
945
+ export declare function numberDigits(raw: string, dataType: string, shape?: NumberShape): string;
946
+ /** One numeric input, through the parse its data type calls for. */
947
+ export declare function parseNumberInput(raw: string, dataType: string, shape?: NumberShape): ParseResult<number | null>;
948
+ /**
949
+ * The wire value. A `float` goes as a decimal string because an Integer is
950
+ * refused on write and JSON cannot spell `2.0`; every other type is a bare
951
+ * number (field_types/float, number, percent, duration, timecode).
952
+ */
953
+ export declare function numberWire(parsed: number | null, dataType: string): number | string | null;
954
+ /** The stored value as the number the steppers and the spinbutton role work on. */
955
+ export declare function storedNumber(value: number | string | null | undefined): number | null;
956
+ /**
957
+ * A calendar date as `YYYY-MM-DD`, with no time and no zone. The API validates
958
+ * the date rather than only parsing it, and refuses every timestamp
959
+ * (field_types/date). Empty input clears the field.
960
+ */
961
+ export declare function toApiDate(raw: string): ParseResult<string | null>;
962
+ /** A calendar day, as the three numbers a calendar widget is built from. */
963
+ export interface IsoDayParts {
964
+ year: number;
965
+ month: number;
966
+ day: number;
967
+ }
968
+ /**
969
+ * `YYYY-MM-DD` as its three numbers, or null when the string is not one. The shape
970
+ * a calendar takes differs per framework, so only the split is shared.
971
+ */
972
+ export declare function isoDayParts(value: string | null | undefined): IsoDayParts | null;
973
+ /** The three numbers back as `YYYY-MM-DD`, zero-padded. */
974
+ export declare function isoDay(parts: IsoDayParts): string;
975
+ export interface DateTimeOptions {
976
+ /** IANA zone the typed wall-clock time is read in. Defaults to the runtime's. */
977
+ timeZone?: string;
978
+ }
979
+ /**
980
+ * A local wall-clock date and time as the UTC `YYYY-MM-DDTHH:MM:SSZ` the field
981
+ * stores. A written offset is normalised away and a zoneless string is taken as
982
+ * UTC, so the conversion has to happen here or the instant is wrong
983
+ * (field_types/date_time). Both parts empty clears the field; a date with no time
984
+ * is midnight local.
985
+ */
986
+ export declare function toApiDateTime(date: string, time: string, options?: DateTimeOptions): ParseResult<string | null>;
987
+ export interface LocalDateTime {
988
+ /** `YYYY-MM-DD` in the given zone. */
989
+ date: string;
990
+ /** `HH:MM` in the given zone. */
991
+ time: string;
992
+ /** `HH:MM:SS` in the given zone, for a value whose seconds matter. */
993
+ timeWithSeconds: string;
994
+ }
995
+ /**
996
+ * The stored UTC instant as local wall-clock parts, for putting back in a date
997
+ * and a time input. The store is second-resolution UTC with a literal `Z`
998
+ * (field_types/date_time).
999
+ */
1000
+ export declare function fromApiDateTime(value: string | null | undefined, options?: DateTimeOptions): LocalDateTime | null;
1001
+ /** The zone a wall-clock input is read in, named for the line under a date-time field. */
1002
+ export declare function timeZoneName(timeZone?: string): string;
1003
+ /**
1004
+ * A value is legal only if it is in `valid_values`, byte for byte: a write is
1005
+ * case-sensitive and a trailing space is a 400, while a filter on the same string
1006
+ * matches (field_types/list).
1007
+ */
1008
+ export declare function isValidListValue(validValues: readonly string[] | undefined, value: string): boolean;
1009
+ /** The web-link write shape: an object holding `url`, optionally `name` (field_types/url). */
1010
+ export interface UrlWriteValue {
1011
+ url: string;
1012
+ name?: string;
1013
+ }
1014
+ /**
1015
+ * A web link for a `url` field. The only accepted input is an object holding
1016
+ * `url`: a bare string 400s, `{}` 400s, and the url itself is validated, a raw
1017
+ * space being the one character measured to fail. With no `name` the field reads
1018
+ * back the whole url as its name (field_types/url).
1019
+ */
1020
+ export declare function parseUrlInput(url: string, name?: string): ParseResult<UrlWriteValue | null>;
1021
+ /**
1022
+ * A colour as the decimal `r,g,b` the field stores, with no spaces and no `#`.
1023
+ * Hex is rejected on write, so it is converted here; spaces inside the triple are
1024
+ * rejected too, so they are stripped (field_types/color). The pipeline-step token
1025
+ * passes through.
1026
+ */
1027
+ export declare function parseColorInput(raw: string): ParseResult<string | null>;
1028
+ /** A stored `r,g,b` as `#rrggbb`, for a colour input. Null for the sentinel and for anything else. */
1029
+ export declare function colorToHex(value: string | null | undefined): string | null;
1030
+ /** The editor a data type needs. `none` means the type has no editor here. */
1031
+ export type EditorKind = 'text' | 'number' | 'checkbox' | 'date' | 'date_time' | 'list' | 'url' | 'color' | 'status_list' | 'entity' | 'multi_entity' | 'none';
1032
+ /**
1033
+ * The editor for a data type. The read-only types and the ones REST cannot write
1034
+ * have no editor at all (field_types/calculated, summary, pivot_column).
1035
+ */
1036
+ export declare function editorKindFor(dataType: string): EditorKind;
1037
+ /**
1038
+ * True when the editor for a data type reads the API, so the caller has to hand it
1039
+ * a context. A status picker reads the schema and the Status table; an entity
1040
+ * picker searches.
1041
+ */
1042
+ export declare function editorNeedsContext(dataType: string): boolean;
1043
+ /** True when a data type can be edited by one of these widgets. */
1044
+ export declare function isEditableType(dataType: string): boolean;
1045
+ /** Where a cell's editor opens: in the cell, or in a popover anchored to it. */
1046
+ export type EditorPlacement = 'inline' | 'popover';
1047
+ /**
1048
+ * Where the editor for a data type opens when the caller names no placement.
1049
+ * Every editor takes the room a popover has; a checkbox is one press and stays in
1050
+ * the cell.
1051
+ */
1052
+ export declare function editorPlacementFor(dataType: string): EditorPlacement;
1053
+ //#endregion
1054
+ //#region src/schema-service.d.ts
1055
+ export interface SchemaServiceOptions {
1056
+ /** How long a schema read stays fresh. Ignored when a `QueryCache` is passed in. Default one hour. */
1057
+ ttlMs?: number;
1058
+ }
1059
+ /** One step of a resolved dotted path. */
1060
+ export interface PathSegment {
1061
+ /** Type this segment was read on. */
1062
+ entityType: string;
1063
+ /** Programmatic field name. */
1064
+ name: string;
1065
+ displayName: string;
1066
+ dataType: string;
1067
+ /** For a link that the path continues through: the type the next segment reads on. */
1068
+ through?: string;
1069
+ field: FieldSchema;
1070
+ }
1071
+ export interface SchemaService {
1072
+ /** Enabled types with their display names. */
1073
+ entityTypes(): Promise<EntityTypeInfo[]>;
1074
+ /** Every field of a type, at site scope. */
1075
+ fields(entityType: string): Promise<Record<string, FieldSchema>>;
1076
+ field(entityType: string, name: string): Promise<FieldSchema | undefined>;
1077
+ /** Codes the project hides on a list or status field. */
1078
+ hiddenValues(entityType: string, field: string, projectId: number): Promise<string[]>;
1079
+ /**
1080
+ * Statuses a picker may offer. Without `projectId` the site vocabulary, which hides nothing.
1081
+ * `field` names a list or status field other than the type's own status field.
1082
+ */
1083
+ statusOptions(entityType: string, projectId?: number, field?: string): Promise<StatusOption[]>;
1084
+ /** Statuses usable in every one of the projects. */
1085
+ statusOptionsForProjects(entityType: string, projectIds: number[], field?: string): Promise<StatusOption[]>;
1086
+ /** The type's status field, or its conventional name when the type has none. */
1087
+ statusField(entityType: string): Promise<FieldSchema | string>;
1088
+ /** Walk `entity.Shot.code` into its segments. Throws naming the whole path when a segment does not resolve. */
1089
+ resolvePath(rootType: string, dottedPath: string): Promise<PathSegment[]>;
1090
+ invalidate(): void;
1091
+ }
1092
+ export declare function createSchemaService(client: SgClient | QueryCache, options?: SchemaServiceOptions): SchemaService;
1093
+ //#endregion
1094
+ //#region src/collection.d.ts
1095
+ /** One sort key. Serialised as `path` or `-path` (026_result_order). */
1096
+ export interface SortSpec {
1097
+ path: string;
1098
+ descending: boolean;
1099
+ }
1100
+ /** A filter as the caller holds it: the editor's tree, the wire group, or nothing. */
1101
+ export type SourceFilters = FilterNode | WireGroup | null;
1102
+ export type SourceStatus = 'idle' | 'loading' | 'loadingMore' | 'ready' | 'error';
1103
+ /** `pages` shows one page at a time; `infinite` appends page after page. */
1104
+ export type SourceMode = 'pages' | 'infinite';
1105
+ export interface EntitySourceOptions {
1106
+ client: SgClient;
1107
+ entityType: string;
1108
+ /** Paths to read, plain or dotted. `id` is always read. */
1109
+ fields: string[];
1110
+ filters?: SourceFilters;
1111
+ sort?: SortSpec[];
1112
+ /** Rows per request. Default 50. */
1113
+ pageSize?: number;
1114
+ /** Default `infinite`. */
1115
+ mode?: SourceMode;
1116
+ /** The page `pages` mode opens on. Default 1. */
1117
+ page?: number;
1118
+ }
1119
+ /** Everything a view renders. A new object on every change, so identity is the signal. */
1120
+ export interface EntitySourceState {
1121
+ rows: EntityRow[];
1122
+ status: SourceStatus;
1123
+ error: Error | null;
1124
+ /** True when another page exists. */
1125
+ hasMore: boolean;
1126
+ /** The total the last `count()` answered. Null until one is asked for, and null when the site did not answer it. */
1127
+ total: number | null;
1128
+ filters: WireGroup | null;
1129
+ sort: SortSpec[];
1130
+ mode: SourceMode;
1131
+ /** 1-based page number of the rows on screen. 1 in infinite mode. */
1132
+ page: number;
1133
+ pageSize: number;
1134
+ }
1135
+ export interface EntitySource {
1136
+ readonly entityType: string;
1137
+ readonly fields: readonly string[];
1138
+ readonly rows: EntityRow[];
1139
+ readonly status: SourceStatus;
1140
+ readonly error: Error | null;
1141
+ readonly hasMore: boolean;
1142
+ readonly filters: WireGroup | null;
1143
+ readonly sort: SortSpec[];
1144
+ readonly mode: SourceMode;
1145
+ readonly page: number;
1146
+ readonly pageSize: number;
1147
+ readonly total: number | null;
1148
+ /** Read the first page, discarding anything already loaded, and count the set in `pages` mode. */
1149
+ load(): Promise<void>;
1150
+ /**
1151
+ * Append the next page. A no-op in `pages` mode, while another read is in flight,
1152
+ * or when there is no more. Calling it again is what retries a page that failed.
1153
+ */
1154
+ loadMore(): Promise<void>;
1155
+ /** Read every page already shown again, keeping the row count. */
1156
+ refresh(): Promise<void>;
1157
+ setFilters(filters: SourceFilters): Promise<void>;
1158
+ setSort(sort: SortSpec[]): Promise<void>;
1159
+ /** Show one page of the set. `pages` mode only. */
1160
+ setPage(page: number): Promise<void>;
1161
+ /** Walk the set a page at a time, or append page after page. Opens at the first page. */
1162
+ setMode(mode: SourceMode): Promise<void>;
1163
+ /** Change the rows per page and open at the first one. */
1164
+ setPageSize(size: number): Promise<void>;
1165
+ /** Total rows the filter matches, through `_summarize` (020_summarize). Null when the site did not answer the key. */
1166
+ count(): Promise<number | null>;
1167
+ /** Write the named fields of one row and put the re-read row back in place. */
1168
+ updateRow(ref: EntityRef, patch: Record<string, unknown>): Promise<EntityRow>;
1169
+ /**
1170
+ * Read these rows again with the source's own projection and swap them in place.
1171
+ * A row the site no longer answers (retired, or moved out of reach) leaves the list.
1172
+ * The filter is not applied, as on `updateRow`: a row that moved out of the filter
1173
+ * still comes back so the view can show what changed. Ids not on screen are ignored.
1174
+ * Neither status nor order changes, so nothing dims and nothing jumps.
1175
+ */
1176
+ rereadRows(ids: readonly number[]): Promise<void>;
1177
+ /** The current state object, stable between changes. */
1178
+ snapshot(): EntitySourceState;
1179
+ subscribe(listener: () => void): () => void;
1180
+ }
1181
+ /** `Type:id`, the key a list keys rows on. */
1182
+ export declare function rowKey(row: EntityRow | EntityRef): string;
1183
+ /**
1184
+ * The value at a path on a row. A dotted path comes back flat under its literal
1185
+ * key in `attributes`; a plain entity field comes back under `relationships`
1186
+ * (003_query, endpoints/post_entity_type_search).
1187
+ */
1188
+ export declare function cellValue(row: EntityRow, path: string): unknown;
1189
+ /** Sort keys as the API's `sort` body value: comma separated, `-` per key (026_result_order). */
1190
+ export declare function serializeSort(sort: readonly SortSpec[]): string | undefined;
1191
+ /** A filter in the one shape a read carries, whichever spelling the caller holds. */
1192
+ export declare function toWireGroup(filters: SourceFilters): WireGroup | null;
1193
+ export declare function createEntitySource(options: EntitySourceOptions): EntitySource;
1194
+ /** The numbers a collection's footer draws, in either mode. */
1195
+ export interface PageRange {
1196
+ mode: SourceMode;
1197
+ page: number;
1198
+ pageSize: number;
1199
+ /** 1-based index of the first row on screen. 0 when there are none. */
1200
+ from: number;
1201
+ /** 1-based index of the last row on screen. 0 when there are none. */
1202
+ to: number;
1203
+ total: number | null;
1204
+ /** Pages the total implies. Null when nothing counted the set. */
1205
+ pageCount: number | null;
1206
+ hasPrevious: boolean;
1207
+ hasNext: boolean;
1208
+ /** `1 to 25 of 320`, and `1 to 25` when nothing counted the set. */
1209
+ rangeLabel: string;
1210
+ /** `25 loaded`, and `25 of 320 loaded` once the set is counted. */
1211
+ loadedLabel: string;
1212
+ }
1213
+ /**
1214
+ * The footer's numbers for one state.
1215
+ *
1216
+ * A read answers no total of its own, so a range reads "of N" only once
1217
+ * `_summarize` has counted the set. In `pages` mode a next page exists because
1218
+ * that count says so, or because the page that came back was full; in
1219
+ * `infinite` mode the source has already walked the set, so its own answer
1220
+ * stands (006_pagination, 020_summarize).
1221
+ */
1222
+ export declare function describePaging(state: EntitySourceState): PageRange;
1223
+ /** A column as a caller writes it: a path, and anything the schema should not decide. */
1224
+ export interface ColumnSpec {
1225
+ /** Plain or dotted path, e.g. `entity.Shot.code`. */
1226
+ path: string;
1227
+ header?: string;
1228
+ dataType?: string;
1229
+ /** Starting width in pixels. */
1230
+ width?: number;
1231
+ editable?: boolean;
1232
+ align?: 'left' | 'right';
1233
+ field?: FieldSchema | null;
1234
+ /** Override the sortability the data type implies. */
1235
+ sortable?: boolean;
1236
+ /** Override where this column's editor opens. */
1237
+ editorPlacement?: EditorPlacement;
1238
+ }
1239
+ /** A column with every question answered, which is what a collection widget takes. */
1240
+ export interface CollectionColumn {
1241
+ path: string;
1242
+ header: string;
1243
+ dataType: string;
1244
+ width?: number;
1245
+ /** True when a cell may open an editor. A projection is never writable. */
1246
+ editable: boolean;
1247
+ align: 'left' | 'right';
1248
+ /** False for a type the server sorts as a silent no-op or a 400 (026_result_order). */
1249
+ sortable: boolean;
1250
+ /** The schema of the field the path lands on, for a status label out of `display_values`. */
1251
+ field: FieldSchema | null;
1252
+ /** Where this column's editor opens. Absent leaves it to the data type. */
1253
+ editorPlacement?: EditorPlacement;
1254
+ }
1255
+ /**
1256
+ * Fill in headers, data types and editability from the schema.
1257
+ *
1258
+ * The header of a dotted path is the display name of the field it lands on. A
1259
+ * projection is never writable: a write names one field of one row
1260
+ * (put_entity_type_id), so only a plain path can open an editor.
1261
+ */
1262
+ export declare function resolveColumns(schema: SchemaService, entityType: string, columns: ReadonlyArray<string | ColumnSpec>): Promise<CollectionColumn[]>;
1263
+ /**
1264
+ * A column from a bare path, for a row-anatomy prop that takes either.
1265
+ *
1266
+ * Nothing but the path is known, so the value renders as text and the column is
1267
+ * neither sortable nor editable. A caller that wants the field's own type passes
1268
+ * the resolved column from `resolveColumns` instead.
1269
+ */
1270
+ export declare function toColumn(spec: string | CollectionColumn): CollectionColumn;
1271
+ /**
1272
+ * Contiguous runs of rows sharing a value at `path`.
1273
+ *
1274
+ * Grouping a paged read is only honest over an order the server produced, so a
1275
+ * caller sorts on the same path and this walks the runs. A group whose rows
1276
+ * continue on the next page grows when that page arrives.
1277
+ */
1278
+ export interface RowGroup {
1279
+ /** The raw value the run shares. */
1280
+ value: unknown;
1281
+ rows: EntityRow[];
1282
+ }
1283
+ /**
1284
+ * The value a row groups under, derived rather than read from a column.
1285
+ *
1286
+ * For a group a column cannot name: a multi-entity field no site sorts on, or a
1287
+ * value that comes from one field on one type and another on another. The order
1288
+ * the runs are walked in is the caller's, so a caller passing this sorts the
1289
+ * source itself.
1290
+ */
1291
+ export type GroupKeyFn = (row: EntityRow) => unknown;
1292
+ /** What the rows are grouped on: a path they are sorted by, or a key derived from each row. */
1293
+ export type GroupBy = string | GroupKeyFn;
1294
+ export declare function groupRows(rows: readonly EntityRow[], by: GroupBy): RowGroup[];
1295
+ /**
1296
+ * The text a group key reads as when there is no column to render it by.
1297
+ *
1298
+ * A row reads as its display name, anything else as its own text, and a key that
1299
+ * is nothing at all as the empty string.
1300
+ */
1301
+ export declare function groupKeyText(value: unknown): string;
1302
+ //#endregion
1303
+ //#region src/status-service.d.ts
1304
+ export interface StatusService {
1305
+ /** Every Status on the site. Fetched once. */
1306
+ all(): Promise<StatusRecord[]>;
1307
+ /** The table indexed by code. The first row wins when a code repeats. */
1308
+ byCode(): Promise<ReadonlyMap<string, StatusRecord>>;
1309
+ /** One status, or `undefined` for a code with no Status behind it, such as a plain `list` value. */
1310
+ record(code: string): Promise<StatusRecord | undefined>;
1311
+ invalidate(): void;
1312
+ }
1313
+ export declare function createStatusService(client: SgClient | QueryCache): StatusService;
1314
+ //#endregion
1315
+ //#region src/row.d.ts
1316
+ /**
1317
+ * A field named by a row-anatomy prop: a bare path, or a column already resolved
1318
+ * from the schema so the value renders by its data type. One type everywhere.
1319
+ */
1320
+ export type FieldSpec = string | CollectionColumn;
1321
+ /** The path a field spec names. Empty when nothing is named. */
1322
+ export declare function pathOf(spec: FieldSpec | null | undefined): string;
1323
+ /** The six props of rule 9, as a widget holds them. */
1324
+ export interface RowAnatomy {
1325
+ /** `false`, or the field name holding the image URL. */
1326
+ thumbnail?: string | false | undefined;
1327
+ /** The field shown as the main label. The display-name chain answers it when absent. */
1328
+ labelField?: string | undefined;
1329
+ /** The muted line under the label. */
1330
+ subLabelField?: FieldSpec | null | undefined;
1331
+ /** The right-aligned column, rendered by data type. */
1332
+ secondaryField?: FieldSpec | null | undefined;
1333
+ /** Show programmatic names beside display names. */
1334
+ showCode?: boolean | undefined;
1335
+ /** Extra fields to request, so a caller's own sub-label or secondary can read them. */
1336
+ fields?: string[] | undefined;
1337
+ }
1338
+ /** The field a row's thumbnail is read from, or null when there is none. */
1339
+ export declare function thumbnailField(anatomy: RowAnatomy): string | null;
1340
+ /**
1341
+ * Every field a row anatomy has to read, `base` first and duplicates dropped.
1342
+ *
1343
+ * An unknown field name is a silent 200 with the key absent (003_query), so asking
1344
+ * for a path a type does not carry costs nothing.
1345
+ */
1346
+ export declare function rowFields(anatomy: RowAnatomy, base?: readonly string[]): string[];
1347
+ /** The picture a row shows, or null when the field is off or holds no URL. */
1348
+ export declare function rowThumbnail(values: Record<string, unknown>, anatomy: RowAnatomy): string | null;
1349
+ /** The site's Status rows by code, as a widget holds them (probe 010). */
1350
+ export type StatusTable = Readonly<Record<string, StatusRecord>>;
1351
+ /** What the sub-label reads its value by. Both parts are optional: without them the value stands as written. */
1352
+ export interface SubLabelSource {
1353
+ /** The data type of the field the sub-label names. */
1354
+ dataType?: string | undefined;
1355
+ /** The status table, so a status code reads the name the site gives it. */
1356
+ statuses?: StatusTable | null | undefined;
1357
+ }
1358
+ /**
1359
+ * The muted line under the label, as text. Empty when nothing names it or the value is blank.
1360
+ *
1361
+ * The value reads by its data type, as the secondary column does: a status is stored as a
1362
+ * bare code and the name a reader knows it by lives on the site's Status row, keyed by code
1363
+ * and site-wide (probe 010, field_types/status_list); an entity keeps its name, a list of
1364
+ * them joins their names, and a date stays the string the API sent (field_types/date). A
1365
+ * caller with no schema hands in no type and gets the value as written.
1366
+ */
1367
+ export declare function rowSubLabel(values: Record<string, unknown>, anatomy: RowAnatomy, source?: SubLabelSource): string;
1368
+ /**
1369
+ * The data type the sub-label reads by: the resolved column's own, the schema's when
1370
+ * one was read, and nothing otherwise, which leaves the value as written.
1371
+ */
1372
+ export declare function subLabelType(anatomy: RowAnatomy, fieldType?: string | undefined): string;
1373
+ /** The schemas a row's sub-label and secondary draw with, and the status table when either needs one. */
1374
+ export interface RowFieldPlan {
1375
+ subLabel: FieldSchema | null;
1376
+ secondary: FieldSchema | null;
1377
+ /** `Status` rows by code, read once and only when a status is on show (probe 010). */
1378
+ statuses: StatusTable | null;
1379
+ }
1380
+ /**
1381
+ * The two fields a row resolves, in one pass and with one status read.
1382
+ *
1383
+ * A resolved column already carries its type, so only a bare path costs a schema
1384
+ * read, and that read is the service's cached one.
1385
+ */
1386
+ export declare function resolveRowFields(schema: SchemaService, statusTable: StatusService, type: string, anatomy: RowAnatomy): Promise<RowFieldPlan>;
1387
+ /** The programmatic name beside the label, when it says something the label does not. */
1388
+ export declare function rowCode(values: Record<string, unknown>, label: string, showCode?: boolean): string;
1389
+ /**
1390
+ * The raw value the secondary column draws. `id` is on the row itself rather than
1391
+ * among the attributes a read returns, so it is answered from the reference.
1392
+ */
1393
+ export declare function rowSecondary(row: {
1394
+ id: number;
1395
+ values: Record<string, unknown>;
1396
+ }, anatomy: RowAnatomy): unknown;
1397
+ /**
1398
+ * The data type the secondary renders as: the resolved column's own, the schema's
1399
+ * when one was read, and `number` for `id`, which is a code rather than a name.
1400
+ */
1401
+ export declare function secondaryType(anatomy: RowAnatomy, fieldType?: string | undefined): string;
1402
+ //#endregion
1403
+ //#region src/picker.d.ts
1404
+ /** One searched row, flattened for display. */
1405
+ export interface PickerRow {
1406
+ type: string;
1407
+ id: number;
1408
+ /** The label, from `labelField` or the display-name chain, never empty. */
1409
+ name: string;
1410
+ /**
1411
+ * Attributes and relationships in one map. A dotted path is a literal key with
1412
+ * dots in it, not a nested object (003_query), and a relationship is unwrapped
1413
+ * to its `data` so a caller reads `{type, id, name}` rather than an envelope.
1414
+ */
1415
+ values: Record<string, unknown>;
1416
+ }
1417
+ /**
1418
+ * The key a picker holds a row under, everywhere. A numeric id alone collides:
1419
+ * a Shot and an Asset on one site share ids freely, and a picker may search both.
1420
+ */
1421
+ export declare function entityKey(ref: {
1422
+ type: string;
1423
+ id: number;
1424
+ }): string;
1425
+ /** `Type 123`, the label a row falls back to when nothing names it. */
1426
+ export declare function placeholderName(ref: {
1427
+ type: string;
1428
+ id: number;
1429
+ }): string;
1430
+ /** True when a reference carries no usable name and needs a read to become a row. */
1431
+ export declare function isBareRef(ref: EntityRef): boolean;
1432
+ export declare function toRef(row: PickerRow): EntityRef;
1433
+ /** Flatten a `_search` row. `labelField` wins over the display-name chain when set. */
1434
+ export declare function flattenRow(row: EntityRow, labelField?: string): PickerRow;
1435
+ /** One field a query is matched against, and how. */
1436
+ export interface SearchField {
1437
+ path: string;
1438
+ /** Default `contains`. `starts_with` keeps a shared tail, such as an email domain, out of the match. */
1439
+ operator?: 'contains' | 'starts_with' | 'ends_with' | 'is';
1440
+ }
1441
+ /** A field name, or a field name with the operator it is matched with. */
1442
+ export type SearchFieldSpec = string | SearchField;
1443
+ /**
1444
+ * The filter a typed query becomes: every word must match, each anywhere in the
1445
+ * field, and a word may sit in any one of `fields`. So "pub an" finds
1446
+ * "Published Anna" and a login search finds a person by either half of a name.
1447
+ *
1448
+ * An empty query, or no fields, gives an empty group, which matches every row:
1449
+ * `"conditions": []` is 200 and unscoped (030_complex_filters). `contains` and
1450
+ * `starts_with` both work on text fields and through dotted paths, so a field may
1451
+ * be `entity.Shot.code` (017_filter_operators).
1452
+ */
1453
+ export declare function nameSearchFilter(query: string, fields: readonly SearchFieldSpec[]): FilterGroup;
1454
+ /**
1455
+ * The extra fields a person search matches, for the query as typed.
1456
+ *
1457
+ * The email is always searched, because it is what a person is known by on a site,
1458
+ * but only on its local part until the query holds an `@`: every address shares one
1459
+ * domain, so `contains` on "le" matches `example.studio` and with it the whole site.
1460
+ * A login never holds whitespace, so it is dropped once the query does. The
1461
+ * display-name chain is searched either way.
1462
+ */
1463
+ export declare function userSearchFields(query: string): SearchFieldSpec[];
1464
+ /** Accept either the editor tree or the wire shape wherever a caller supplies a filter. */
1465
+ export declare function asFilterGroup(filter: FilterGroup | WireGroup | null | undefined): FilterGroup | null;
1466
+ /**
1467
+ * Drop every condition whose root field the type does not have, and every group
1468
+ * left empty by that. A picker across several types shares one pre-filter, and a
1469
+ * filter naming a field the type lacks is a 400 rather than a silent no-op
1470
+ * (017_filter_operators), so the condition is dropped on the types it cannot
1471
+ * apply to. Dropping widens a result set; it never narrows one wrongly.
1472
+ */
1473
+ export declare function pruneFilterToFields(node: FilterNode, fieldNames: ReadonlySet<string>): FilterNode | null;
1474
+ /** One `and` of the parts that are not empty. Blank parts are dropped, not sent. */
1475
+ export declare function mergeFilters(...parts: Array<FilterNode | null | undefined>): FilterGroup;
1476
+ /** What a caller narrows a person search with. */
1477
+ export interface UserPickerOptions {
1478
+ /** Search script accounts alongside people. */
1479
+ includeApiUsers?: boolean;
1480
+ /** Offer people whose status is `dis`. */
1481
+ includeInactive?: boolean;
1482
+ }
1483
+ /** Login and email are searched and shown, so they have to be read (entity_types/HumanUser). */
1484
+ export declare const USER_PICKER_FIELDS: string[];
1485
+ /** People and script accounts, in that order. */
1486
+ export declare function userPickerTypes(includeApiUsers: boolean): string[];
1487
+ /**
1488
+ * The active condition, unless inactive people are wanted. `sg_status_list` on
1489
+ * HumanUser is two codes, `act` and `dis`, and `act` is the default
1490
+ * (entity_types/HumanUser). ApiUser has no status field, so a picker drops the
1491
+ * condition on that type rather than sending a filter that would 400.
1492
+ */
1493
+ export declare function userPickerFilters(includeInactive: boolean, extra: FilterGroup | WireGroup | null | undefined): FilterGroup;
1494
+ /** `API user` for a script account, the email for a person, nothing without one. */
1495
+ export declare function userPickerSubLabel(row: PickerRow): string;
1496
+ /** The caller's own search fields, on top of the ones a person is searched by. */
1497
+ export declare function userPickerSearchFields(extra: SearchFieldSpec[] | ((query: string) => SearchFieldSpec[])): (query: string) => SearchFieldSpec[];
1498
+ /** What a caller narrows a project search with. */
1499
+ export interface ProjectPickerOptions {
1500
+ /** Offer projects whose `archived` checkbox is set. */
1501
+ includeArchived?: boolean;
1502
+ }
1503
+ /** Project's status field is `sg_status`, a plain list, not `sg_status_list`. */
1504
+ export declare const PROJECT_PICKER_FIELDS: string[];
1505
+ /**
1506
+ * Archived projects are hidden unless asked for. `sg_status` is not a liveness
1507
+ * filter and is null on most projects; `archived`, `is_template` and `is_demo`
1508
+ * are the discriminators (018_project_listing).
1509
+ */
1510
+ export declare function projectPickerFilters(includeArchived: boolean, extra: FilterGroup | WireGroup | null | undefined): FilterGroup;
1511
+ /**
1512
+ * Clause 8 of the picker contract: the clear control follows `clearable`, and a
1513
+ * mandatory field never offers one. A field whose schema the widget has not read is
1514
+ * not mandatory as far as it knows, so the caller's answer stands.
1515
+ */
1516
+ export declare function clearableForField(clearable: boolean | undefined, field: {
1517
+ mandatory?: boolean;
1518
+ } | null | undefined): boolean;
1519
+ /**
1520
+ * Search results first, then any selected row not among them. Keyboard picking
1521
+ * lands on fresh matches, and a selected row never leaves the list, so it stays
1522
+ * deselectable with an empty query.
1523
+ */
1524
+ export declare function withSelectedPinned(rows: readonly PickerRow[], selected: readonly EntityRef[], known: ReadonlyMap<string, PickerRow>): PickerRow[];
1525
+ /** What a multi picker's control shows for the selection. */
1526
+ export type PickerSummary = 'chips' | 'ellipsis' | 'count';
1527
+ /** Chips `ellipsis` draws before the rest becomes `+n`, when nothing has measured the row. */
1528
+ export declare const ELLIPSIS_CHIPS = 3;
1529
+ /** How many chips a row shows, and how many it hides behind `+n`. */
1530
+ export interface ChipFit {
1531
+ visible: number;
1532
+ hidden: number;
1533
+ }
1534
+ /** A measured chip row: each chip's width, the room it has, and the room held back. */
1535
+ export interface ChipRow {
1536
+ /** Chip widths in order, each carrying the gap that follows it. */
1537
+ widths: readonly number[];
1538
+ /** The room the chips may occupy. */
1539
+ available: number;
1540
+ /** Room held back for the `+n` pill, spent only when something is hidden. */
1541
+ reserve: number;
1542
+ }
1543
+ /**
1544
+ * How many whole chips fit `available`, taken greedily from the first.
1545
+ *
1546
+ * A chip is never cut: one that does not fit whole is hidden, and so is every chip
1547
+ * after it. `reserve` is the room the `+n` pill needs, so it is subtracted only
1548
+ * once the row overflows and the pill is drawn. Each width carries its own trailing
1549
+ * gap, so `k` chips cost the sum of the first `k` widths whatever follows them.
1550
+ */
1551
+ export declare function fitChips(widths: readonly number[], available: number, reserve: number): ChipFit;
1552
+ export interface SelectionSummary<T> {
1553
+ /** The items drawn as chips, in order. Empty under `count`. */
1554
+ shown: T[];
1555
+ /** Items past `shown`. Drawn as `+n` beside the chips. */
1556
+ overflow: number;
1557
+ /** Every label, comma-joined, for the control's `title`. */
1558
+ title: string;
1559
+ /** `3 selected`, which is the whole control under `count`. */
1560
+ countLabel: string;
1561
+ /** True while the chips must stay on one line rather than wrap. */
1562
+ oneLine: boolean;
1563
+ }
1564
+ /**
1565
+ * What a multi picker draws for its selection.
1566
+ *
1567
+ * `chips` draws every chip and wraps. `ellipsis`, the default, keeps one line: as
1568
+ * many whole chips as the measured row fits, then `+n`, with the whole list in the
1569
+ * title. `count` draws neither and reads `3 selected`. `max` bounds the chips in
1570
+ * either chip mode; `0` means every chip. An `ellipsis` row nothing has measured yet
1571
+ * falls back to three chips.
1572
+ */
1573
+ export declare function summariseSelection<T>(items: readonly T[], labelOf: (item: T) => string, options?: {
1574
+ summary?: PickerSummary | undefined;
1575
+ max?: number | undefined;
1576
+ /** The measured row. `ellipsis` fits whole chips to it; the other modes ignore it. */
1577
+ fit?: ChipRow | undefined;
1578
+ }): SelectionSummary<T>;
1579
+ export interface EntitySearchOptions {
1580
+ /** A cached client. Every read goes through it, so repeated queries cost one request. */
1581
+ client: SgClient;
1582
+ /** Shared schema service. One is built over the client when absent. */
1583
+ schema?: SchemaService;
1584
+ entityTypes: string[];
1585
+ /** Field holding the row label. Defaults to the display-name chain. */
1586
+ labelField?: string | undefined;
1587
+ /**
1588
+ * Extra fields the query is matched against, on top of the display-name chain.
1589
+ * A function is called with the query, so a field is searched only when the query
1590
+ * suits it, and a field may name the operator it is matched with.
1591
+ */
1592
+ searchFields?: SearchFieldSpec[] | ((query: string) => SearchFieldSpec[]) | undefined;
1593
+ /** Field shown right-aligned, rendered by its data type. Nothing is shown without it. */
1594
+ secondaryField?: FieldSpec | null | undefined;
1595
+ /** Field shown under the label. Defaults to the entity type when several are searched. */
1596
+ subLabelField?: FieldSpec | null | undefined;
1597
+ /** Field holding the thumbnail URL. `false` hides thumbnails. */
1598
+ thumbnail?: string | false | undefined;
1599
+ /** Extra fields to request, so a caller's own sub-label or secondary can be read. */
1600
+ fields?: string[] | undefined;
1601
+ /** Caller pre-filter, merged into every request with `and`. */
1602
+ filters?: FilterGroup | WireGroup | null | undefined;
1603
+ /** Sugar for a project condition. Skipped on a type that has no project link. */
1604
+ projectId?: number | undefined;
1605
+ /** Rows to keep out of the results. Pushed into the server filter, per type. */
1606
+ exclude?: EntityRef[] | undefined;
1607
+ minQueryLength?: number | undefined;
1608
+ pageSize?: number | undefined;
1609
+ debounceMs?: number | undefined;
1610
+ onError?: ((error: Error) => void) | undefined;
1611
+ }
1612
+ export interface EntitySearchState {
1613
+ /** The query the current rows answer. Highlighting reads this, not the input. */
1614
+ query: string;
1615
+ loading: boolean;
1616
+ error: Error | null;
1617
+ rows: PickerRow[];
1618
+ /** True when another page exists. From a full page, never from `links.next` (006_pagination). */
1619
+ hasMore: boolean;
1620
+ /** True while the query is shorter than the minimum, so the rows answer no query. */
1621
+ tooShort: boolean;
1622
+ }
1623
+ export interface EntitySearch {
1624
+ readonly state: EntitySearchState;
1625
+ /** Rows seen or hydrated so far, by `Type:id`. */
1626
+ readonly known: ReadonlyMap<string, PickerRow>;
1627
+ subscribe(listener: (state: EntitySearchState) => void): () => void;
1628
+ /** Set the typed query. Debounced; a run already in flight is abandoned. */
1629
+ setQuery(query: string): void;
1630
+ /** Append the next page of the current query. */
1631
+ loadMore(): void;
1632
+ /** Read the rows behind bare references, one batched request per type. */
1633
+ hydrate(refs: readonly EntityRef[]): void;
1634
+ /** Remember a row so a selection can be shown before it is searched again. */
1635
+ remember(rows: readonly PickerRow[]): void;
1636
+ /** Apply changed props. Resets the results when the request would change. */
1637
+ update(options: Partial<EntitySearchOptions>): void;
1638
+ dispose(): void;
1639
+ }
1640
+ export declare function createEntitySearch(options: EntitySearchOptions): EntitySearch;
1641
+ //#endregion
1642
+ //#region src/picker-keys.d.ts
1643
+ /**
1644
+ * The keyboard model every picker shares.
1645
+ *
1646
+ * A picker is a token field: chips, a caret and a list. The chips take the caret one at
1647
+ * a time, and Backspace, the arrows and Escape mean the same thing in all of them, so
1648
+ * the rules live here and the two frameworks cannot drift.
1649
+ */
1650
+ /** What a keydown asks the control to do. */
1651
+ export type PickerKeyIntent =
1652
+ /** The key is not the control's business. */
1653
+ {
1654
+ kind: 'nothing';
1655
+ } |
1656
+ /** Close the popup, clear the query and give the caret back to the input. */
1657
+ {
1658
+ kind: 'dismiss';
1659
+ } |
1660
+ /** Put the caret on the chip at `index`, or back in the input when `null`. */
1661
+ {
1662
+ kind: 'focus';
1663
+ index: number | null;
1664
+ } |
1665
+ /** Remove the chip at `index`, then take `then`: a chip, or the input when `null`. */
1666
+ {
1667
+ kind: 'remove';
1668
+ index: number;
1669
+ then: number | null;
1670
+ } |
1671
+ /** Give the caret back to the input and write `key` into the query. */
1672
+ {
1673
+ kind: 'type';
1674
+ key: string;
1675
+ } |
1676
+ /** Give the caret back to the input and show the list. */
1677
+ {
1678
+ kind: 'open';
1679
+ } |
1680
+ /** Keep the list's highlighted row in view. */
1681
+ {
1682
+ kind: 'follow';
1683
+ };
1684
+ export interface PickerKeyState {
1685
+ /** The popup is showing. */
1686
+ open: boolean;
1687
+ /** Text in the search box. */
1688
+ query: string;
1689
+ /** Chips in the control. A single picker counts its value as one. */
1690
+ count: number;
1691
+ /** Index of the chip holding the caret, or `null` while the input holds it. */
1692
+ focused: number | null;
1693
+ /** The control takes edits. A disabled or readonly one takes none. */
1694
+ editable: boolean;
1695
+ /** Several keys may be chosen at once. */
1696
+ multiple: boolean;
1697
+ }
1698
+ /**
1699
+ * What a keydown means to a picker.
1700
+ *
1701
+ * From the input, Backspace and `ArrowLeft` in an empty query reach the chips of a multi
1702
+ * picker rather than the text, and a single picker clears its one value in a press. From
1703
+ * a chip, the arrows walk the row, Backspace and Delete take the chip and leave the caret
1704
+ * on its neighbour, `ArrowDown` shows the list, and anything a person would type gives
1705
+ * the caret back to the input. Escape is the control's business only while the popup
1706
+ * shows; a closed picker leaves the key to whatever encloses it.
1707
+ */
1708
+ export declare function pickerKeyIntent(key: string, state: PickerKeyState): PickerKeyIntent;
1709
+ /** What a keydown asks a search box to do. */
1710
+ export type SearchKeyIntent =
1711
+ /** The key is not the search box's business. */
1712
+ {
1713
+ kind: 'nothing';
1714
+ } |
1715
+ /** Clear the query and the rows it answered, and keep the caret where it is. */
1716
+ {
1717
+ kind: 'clear';
1718
+ };
1719
+ export interface SearchKeyState {
1720
+ /** Text in the search box. */
1721
+ query: string;
1722
+ }
1723
+ /**
1724
+ * What a keydown means to a search box. Escape is the search box's business only while
1725
+ * the query has text; an empty one leaves the key to the shell, as a closed picker does.
1726
+ */
1727
+ export declare function searchKeyIntent(key: string, state: SearchKeyState): SearchKeyIntent;
1728
+ /**
1729
+ * Put the caret on one chip of a row.
1730
+ *
1731
+ * Every chip is taken out of the tab order, so the row is walked with the arrows and Tab
1732
+ * still leaves the control. A chip the measured row hides takes no caret, and the input
1733
+ * keeps it; the model still counts the chip as the one a Backspace removes.
1734
+ */
1735
+ export declare function focusChip(row: Element | null | undefined, index: number | null): boolean;
1736
+ /**
1737
+ * Keep the list's highlighted row in view.
1738
+ *
1739
+ * `nearest` moves the scroller by the least it can and leaves the page under a
1740
+ * fixed popup where it is. Both primitives mark the row with `data-highlighted`.
1741
+ */
1742
+ export declare function scrollHighlightedIntoView(list: Element | null | undefined): boolean;
1743
+ /**
1744
+ * Keep the highlighted row in view for as long as the list is drawn.
1745
+ *
1746
+ * The primitive moves the highlight itself, and a load-more page appends rows under it,
1747
+ * so the list follows the mark rather than the key that moved it. Returns the teardown.
1748
+ */
1749
+ export declare function watchHighlight(list: Element | null | undefined): () => void;
1750
+ //#endregion
1751
+ //#region src/proxy-client.d.ts
1752
+ export interface ProxyClientOptions {
1753
+ /** Where the endpoints live, e.g. `/sg` or `https://app.example.com/api/sg`. */
1754
+ basePath: string;
1755
+ fetch?: typeof fetch;
1756
+ /** Extra request headers. Called per request, so a caller can rotate a CSRF or auth header. */
1757
+ headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
1758
+ }
1759
+ export declare class ProxyClient implements SgClient {
1760
+ private readonly options;
1761
+ private readonly base;
1762
+ private readonly fetchFn;
1763
+ constructor(options: ProxyClientOptions);
1764
+ private post;
1765
+ entityTypes(): Promise<EntityTypeInfo[]>;
1766
+ fields(entityType: string, projectId?: number): Promise<Record<string, FieldSchema>>;
1767
+ fieldWithProject(entityType: string, field: string, projectId: number): Promise<FieldSchema>;
1768
+ search(entityType: string, options: SearchOptions): Promise<SearchResult>;
1769
+ textSearch(text: string, entityTypes: Record<string, TextSearchFilter>, page?: {
1770
+ size?: number;
1771
+ number?: number;
1772
+ }): Promise<TextSearchRow[]>;
1773
+ statuses(): Promise<StatusRecord[]>;
1774
+ create(entityType: string, body: Record<string, unknown>): Promise<EntityRow>;
1775
+ /** The bytes cross the protocol base64-encoded; the handler runs the three calls. */
1776
+ upload(entityType: string, id: number, file: UploadFile): Promise<UploadResult>;
1777
+ update(entityType: string, id: number, patch: Record<string, unknown>): Promise<EntityRow>;
1778
+ hierarchySearch(rootPath: string, entity: EntityRef): Promise<HierarchyPath[]>;
1779
+ hierarchyExpand(path: string): Promise<HierarchyNode>;
1780
+ summarize(entityType: string, options?: SummarizeOptions): Promise<SummarizeResult>;
1781
+ threadContents(noteId: number, entityFields?: Record<string, string[]>): Promise<ThreadRow[]>;
1782
+ eventLog(options?: EventLogOptions): Promise<EventLogResult>;
1783
+ following(userId: number, options?: FollowingOptions): Promise<EntityRef[]>;
1784
+ }
1785
+ //#endregion
1786
+ //#region src/proxy-handler.d.ts
1787
+ /** The methods the protocol carries, one POST each. */
1788
+ export declare const PROXY_METHODS: readonly ['entityTypes', 'fields', 'fieldWithProject', 'search', 'textSearch', 'statuses', 'create', 'update', 'upload', 'hierarchyExpand', 'hierarchySearch', 'summarize', 'threadContents', 'eventLog', 'following'];
1789
+ export type ProxyMethod = (typeof PROXY_METHODS)[number];
1790
+ /** An error as it crosses the wire. `ProxyClient` rebuilds an `SgApiError` from it. */
1791
+ export interface ProxyError {
1792
+ status: number;
1793
+ message: string;
1794
+ body: unknown;
1795
+ }
1796
+ export type ProxyBody = {
1797
+ data: unknown;
1798
+ } | {
1799
+ error: ProxyError;
1800
+ };
1801
+ export interface ProxyResult {
1802
+ /** HTTP status the wrapper writes. */
1803
+ status: number;
1804
+ body: ProxyBody;
1805
+ }
1806
+ export type ProxyHandler = (method: string, body: unknown) => Promise<ProxyResult>;
1807
+ export declare function isProxyMethod(value: string): value is ProxyMethod;
1808
+ /**
1809
+ * Bytes as base64. The protocol is one JSON object per call, so an upload's bytes
1810
+ * cross it encoded and are decoded here.
1811
+ */
1812
+ export declare function bytesToBase64(bytes: Uint8Array): string;
1813
+ export declare function base64ToBytes(value: string): Uint8Array;
1814
+ /** Wraps a client so a host app can expose it over its own HTTP endpoints. */
1815
+ export declare function createProxyHandler(client: SgClient): ProxyHandler;
1816
+ //#endregion
1817
+ //#region src/session-auth.d.ts
1818
+ export interface SessionTokenAuthOptions {
1819
+ /** Site root, e.g. `https://studio.shotgrid.autodesk.com`. */
1820
+ siteUrl: string;
1821
+ /** The launcher's `sessionToken`. A credential for the person who approved: store it where a key would be stored. */
1822
+ sessionToken: string;
1823
+ fetch?: typeof fetch;
1824
+ }
1825
+ /**
1826
+ * A `token()` for `RestClientOptions`. It mints a bearer, holds it until shortly
1827
+ * before it expires, refreshes with the refresh token, and re-mints from the
1828
+ * session token when the refresh is refused.
1829
+ */
1830
+ export declare function createSessionTokenAuth(options: SessionTokenAuthOptions): () => Promise<string>;
1831
+ /** Why the launcher stopped. */
1832
+ export type AppSessionFailure = 'not_found' | 'timeout';
1833
+ export declare class AppSessionError extends Error {
1834
+ readonly reason: AppSessionFailure;
1835
+ constructor(reason: AppSessionFailure, message: string);
1836
+ }
1837
+ export interface RequestAppSessionOptions {
1838
+ siteUrl: string;
1839
+ /** The name shown to the person approving. */
1840
+ appName: string;
1841
+ /** Any string identifying this machine. The poll does not check it. */
1842
+ machineId: string;
1843
+ fetch?: typeof fetch;
1844
+ }
1845
+ export interface AppSessionRequest {
1846
+ id: string;
1847
+ /** The approval page. Show it to the person; opening a browser is the caller's business. */
1848
+ url: string;
1849
+ }
1850
+ /** Step one: ask the site for an approval page. Takes no token and no cookie. */
1851
+ export declare function requestAppSession(options: RequestAppSessionOptions): Promise<AppSessionRequest>;
1852
+ export interface PollAppSessionOptions {
1853
+ siteUrl: string;
1854
+ /** `sessionRequestId` from `requestAppSession`. */
1855
+ id: string;
1856
+ fetch?: typeof fetch;
1857
+ /** Gap between polls. Default 2000. */
1858
+ intervalMs?: number;
1859
+ /** Give up after this long. Default 300000, the life of a pending request. */
1860
+ timeoutMs?: number;
1861
+ }
1862
+ export interface AppSession {
1863
+ sessionToken: string;
1864
+ /** On an Autodesk Identity site this is the person's email address. Treat it as identifying. */
1865
+ userLogin: string;
1866
+ }
1867
+ /**
1868
+ * Step two: poll until the person approves. The token is handed out once, so poll
1869
+ * from one place and keep the response that holds it. A 404 is forgotten, denied
1870
+ * or a mistyped id alike, so the only answer is to request a new session.
1871
+ */
1872
+ export declare function pollAppSession(options: PollAppSessionOptions): Promise<AppSession>;
1873
+ //#endregion
1874
+ //#region src/context.d.ts
1875
+ /**
1876
+ * What the site decides about display. None of it is on a field: the unit behind
1877
+ * a duration is `hours_per_day` from `GET /preferences` (field_types/duration),
1878
+ * and nothing at all names the frame rate behind a timecode
1879
+ * (field_types/timecode), so the app supplies that one.
1880
+ */
1881
+ export interface SitePreferences {
1882
+ /** The site's `hours_per_day`; a duration then renders in days. */
1883
+ hoursPerDay?: number;
1884
+ locale?: string;
1885
+ /** IANA zone a `date_time` is shown in. Defaults to the runtime's. */
1886
+ timeZone?: string;
1887
+ /** Frames a second, for a timecode. Given, a timecode gains its frame digits. */
1888
+ frameRate?: number;
1889
+ }
1890
+ export interface SgContextOptions extends SitePreferences {
1891
+ client: SgClient;
1892
+ /** How long a row read stays fresh. Schema and statuses keep their own hour-long cache. Default 30000. */
1893
+ ttlMs?: number;
1894
+ /** The web app this data comes from, so widgets can link a row to its page. */
1895
+ siteUrl?: string;
1896
+ }
1897
+ export interface SgContext {
1898
+ /** The cached client. Widgets read rows through this, not through the one passed in. */
1899
+ client: QueryCache;
1900
+ schema: SchemaService;
1901
+ statuses: StatusService;
1902
+ /** The web app this data comes from, without its trailing slash. Empty when the app named none. */
1903
+ siteUrl: string;
1904
+ /** What the site decides about display. Empty when the app named nothing. */
1905
+ preferences: SitePreferences;
1906
+ /** Drop everything cached. Call it after a write. */
1907
+ invalidate(): void;
1908
+ }
1909
+ export declare function createSgContext(options: SgContextOptions): SgContext;
1910
+ /**
1911
+ * The context for a bare client, built once. A widget given a client and no
1912
+ * context calls this, and every widget on the page handed that client shares its
1913
+ * caches. The options are read on the first call for a client; a later call
1914
+ * returns what that one built.
1915
+ */
1916
+ export declare function contextFromClient(client: SgClient, options?: Omit<SgContextOptions, 'client'>): SgContext;
1917
+ /** The site preferences a formatter takes, ready to spread into `fieldText`. */
1918
+ export declare function preferencesOf(context?: {
1919
+ preferences?: SitePreferences;
1920
+ } | null): FieldTextOptions;
1921
+ //#endregion
1922
+ //#region src/presentation.d.ts
1923
+ /** Trailing slashes off, so a site url composes with an absolute path. */
1924
+ export declare function normalizeSiteUrl(siteUrl: string | null | undefined): string;
1925
+ /**
1926
+ * The web app's page for one row.
1927
+ *
1928
+ * `Project.landing_page_url` is the only detail address the API hands out, and it
1929
+ * is the site-relative path `/detail/Project/<id>` with the site url left to the
1930
+ * caller (entity_types/Project). Every other type takes the same shape by a
1931
+ * convention the API does not document: on the test site `/detail/<Type>/<id>`
1932
+ * answers 302 to `/user/login` carrying itself as `return_path`, so the route
1933
+ * resolves before authentication decides.
1934
+ *
1935
+ * Answers null when there is no site to link into or no row to link to.
1936
+ */
1937
+ export declare function entityDetailUrl(siteUrl: string | null | undefined, ref: EntityRef | null | undefined): string | null;
1938
+ /** The character between the hops of a path label. */
1939
+ export declare const PATH_SEPARATOR = " › ";
1940
+ export interface PathLabelOptions {
1941
+ /**
1942
+ * Name the type a hop travels through when the hop's field links several types.
1943
+ * Default true.
1944
+ */
1945
+ typeWhenAmbiguous?: boolean;
1946
+ /** Display names by schema type name, from `entityTypes()`. */
1947
+ typeLabels?: Record<string, string>;
1948
+ separator?: string;
1949
+ }
1950
+ /**
1951
+ * The label of a resolved path.
1952
+ *
1953
+ * A hop names its field, and the type only when the field's `valid_types` holds
1954
+ * more than one: `sg_task.Task.sg_status_list` reads `Task › Status`, while
1955
+ * `note_links.Shot.sg_sequence` reads `Link › Shot › Sequence` because the same
1956
+ * field could have gone to an Asset. A projection resolves a dotted path against
1957
+ * that `valid_types` list (probe 059), so the ambiguity the label spells out is
1958
+ * the one the read itself has to settle.
1959
+ */
1960
+ export declare function pathLabel(segments: readonly PathSegment[], options?: PathLabelOptions): string;
1961
+ //#endregion
1962
+ //#region src/pickers.d.ts
1963
+ export interface EntityTypeRestrictions {
1964
+ /** Codes a caller offers. Empty or absent means every type. */
1965
+ allow?: string[];
1966
+ /** Codes a caller withholds, applied after `allow`. */
1967
+ deny?: string[];
1968
+ }
1969
+ /**
1970
+ * The types a picker may offer, allow first and deny second.
1971
+ *
1972
+ * Applied to the derived list rather than to the read, so a caller narrowing the
1973
+ * set sees the list change with no second call. `/schema` is 12KB and holds every
1974
+ * enabled type, custom slots included (probe 002).
1975
+ */
1976
+ export declare function filterEntityTypes(types: readonly EntityTypeInfo[], restrictions?: EntityTypeRestrictions): EntityTypeInfo[];
1977
+ /** What an entity-type picker draws: the rows on offer and the label a code takes. */
1978
+ export interface EntityTypeOptions {
1979
+ /** Types on offer, `allow` first and `deny` second. */
1980
+ types: EntityTypeInfo[];
1981
+ /** Those the query matches, on display name or code. */
1982
+ shown: EntityTypeInfo[];
1983
+ /** A code's display name, or the code itself where the site offers no such type. */
1984
+ labelOf: (code: string) => string;
1985
+ }
1986
+ /**
1987
+ * The options both entity-type pickers derive from one schema read.
1988
+ *
1989
+ * The vocabulary is one read of 12KB (probe 002), so the query narrows the derived
1990
+ * list in the browser rather than asking for it again. `loaded` is `null` while the
1991
+ * read is in flight, which offers nothing and labels a code as itself.
1992
+ */
1993
+ export declare function entityTypeOptions(loaded: readonly EntityTypeInfo[] | null, options?: EntityTypeRestrictions & {
1994
+ query?: string;
1995
+ }): EntityTypeOptions;
1996
+ /** One traversal step: the link field followed, and the type it landed on. */
1997
+ export interface FieldHop {
1998
+ /** Field code on the type the hop leaves. */
1999
+ name: string;
2000
+ displayName: string;
2001
+ /** Type the hop lands on. */
2002
+ through: string;
2003
+ }
2004
+ /** `entity.Shot.code`: every hop names the type it travels through (field_types/entity). */
2005
+ export declare function fieldPathOf(hops: readonly FieldHop[], name: string): string;
2006
+ /** The type each hop landed on, root first. A type already here is not descended into again. */
2007
+ export declare function pathTypes(rootType: string, hops: readonly FieldHop[]): string[];
2008
+ /** The type a picker is reading fields from after these hops. */
2009
+ export declare function currentType(rootType: string, hops: readonly FieldHop[]): string;
2010
+ /** A resolved path as a person reads it. */
2011
+ export declare function friendlyFieldPath(segments: readonly PathSegment[], separator?: string): string;
2012
+ /** A column a data source computes, offered alongside the real fields. */
2013
+ export interface ExtraField {
2014
+ /** The value emitted when it is chosen. */
2015
+ name: string;
2016
+ displayName?: string;
2017
+ }
2018
+ export interface FieldPickerRestrictions {
2019
+ /** Allow descending through entity fields. Off by default. */
2020
+ deepLinks?: boolean;
2021
+ /** How many hops a path may take. Default 2. */
2022
+ maxDepth?: number;
2023
+ /** Data types a field must have to be selected. Traversal ignores this. */
2024
+ dataTypes?: string | string[];
2025
+ /** A field is selectable only if it declares link targets and one of them is here. Traversal ignores this. */
2026
+ validTypes?: string[];
2027
+ /** Full dotted paths to drop, so a root field and the same name behind a hop are separate. */
2028
+ exclude?: string[];
2029
+ /** Dotted prefixes to drop, along with everything beneath them. */
2030
+ hidePaths?: string[];
2031
+ /** Drop types the API refuses in a filter (017_filter_operators). */
2032
+ filterableOnly?: boolean;
2033
+ /** Synthetic entries offered at the root only. They bypass `dataTypes` and `validTypes`. */
2034
+ extraFields?: ExtraField[];
2035
+ /** Caller's own visibility test, receiving the schema and the candidate's full path. */
2036
+ filter?: (field: FieldSchema, path: string) => boolean;
2037
+ }
2038
+ export interface FieldOption {
2039
+ /** Full dotted path from the root type. This is the emitted value. */
2040
+ path: string;
2041
+ /** Field code on the type it lives on. */
2042
+ name: string;
2043
+ displayName: string;
2044
+ dataType: string;
2045
+ /** The row may be chosen as the value. */
2046
+ selectable: boolean;
2047
+ /** The row descends. */
2048
+ traversable: boolean;
2049
+ /** Types a traversable row may descend into, minus the ones already on the path. */
2050
+ targets: string[];
2051
+ /** A synthetic entry rather than a schema field. */
2052
+ computed: boolean;
2053
+ }
2054
+ export interface FieldOptionsInput extends FieldPickerRestrictions {
2055
+ rootType: string;
2056
+ /** Hops already taken. Empty at the root. */
2057
+ hops?: readonly FieldHop[];
2058
+ }
2059
+ /**
2060
+ * The types a field may be descended into from here.
2061
+ *
2062
+ * Only a single `entity` field: a dotted path through a `multi_entity` field reads
2063
+ * back nothing, 200 with the key absent from `attributes` (probe 016). A type
2064
+ * already on the path is dropped, so `project.Project.users.HumanUser.projects`
2065
+ * cannot loop.
2066
+ */
2067
+ export declare function traversalTargets(field: Pick<FieldSchema, 'dataType' | 'validTypes'>, input: {
2068
+ rootType: string;
2069
+ hops?: readonly FieldHop[];
2070
+ deepLinks?: boolean;
2071
+ maxDepth?: number;
2072
+ }): string[];
2073
+ /**
2074
+ * The rows a field picker shows for one type at one depth.
2075
+ *
2076
+ * `dataTypes` and `validTypes` bind what may be *selected*; a link field stays on
2077
+ * the list so a restricted picker can still reach a nested field of the wanted
2078
+ * type. Every other restriction hides the row outright and is measured against
2079
+ * the full dotted path, so excluding a root field leaves the same name behind a
2080
+ * hop alone. Computed entries come first, then the schema fields by display name.
2081
+ */
2082
+ export declare function deriveFieldOptions(fields: Record<string, FieldSchema>, input: FieldOptionsInput): FieldOption[];
2083
+ /** Rows matching the search box. */
2084
+ export declare function searchFieldOptions(options: readonly FieldOption[], query: string): FieldOption[];
2085
+ /** The list with `path` appended, or dropped when it is already there. */
2086
+ export declare function toggleFieldPath(paths: readonly string[], path: string): string[];
2087
+ /** The list with the entry at `from` moved to `to`. An index off either end leaves the order alone. */
2088
+ export declare function moveFieldPath(paths: readonly string[], from: number, to: number): string[];
2089
+ /** The sub-label of a row whose path the schema does not hold. */
2090
+ export declare const UNRESOLVED_PATH_LABEL = "not in the schema";
2091
+ /** One row of a fixed list of paths: what a caller offered, read through the schema. */
2092
+ export interface FieldPathOption {
2093
+ /** The path as the caller wrote it. This is the emitted value. */
2094
+ path: string;
2095
+ /** The resolved path, display names joined, or the raw path where the schema has no such path. */
2096
+ label: string;
2097
+ /** Code of the leaf field, empty where the path does not resolve. */
2098
+ name: string;
2099
+ /** Data type of the leaf field, empty where the path does not resolve. */
2100
+ dataType: string;
2101
+ /** The muted line under the label: the leaf's data type, or that the schema has no such path. */
2102
+ subLabel: string;
2103
+ /** The schema resolved the path. */
2104
+ resolved: boolean;
2105
+ }
2106
+ /**
2107
+ * A caller's fixed list of paths as flat rows, in the order given.
2108
+ *
2109
+ * Each path is resolved through every type it travels and labelled as the picker
2110
+ * labels a chosen value. A path the schema does not hold keeps its place and is
2111
+ * marked, so a list of columns never comes back shorter than it went in.
2112
+ */
2113
+ export declare function resolveFieldPathOptions(schema: Pick<SchemaService, 'resolvePath'>, rootType: string, paths: readonly string[], options?: PathLabelOptions): Promise<FieldPathOption[]>;
2114
+ /** Rows matching the search box, read on what the row shows and on the path behind it. */
2115
+ export declare function searchFieldPathOptions(options: readonly FieldPathOption[], query: string): FieldPathOption[];
2116
+ //#endregion
2117
+ //#region src/sortable.d.ts
2118
+ /**
2119
+ * Ordered lists that reorder by pointer and by keyboard.
2120
+ *
2121
+ * The model answers what a move does to an order and what a screen reader should
2122
+ * be told about it. The controller binds one list element: it turns pointer
2123
+ * events into a drag, moves the rows out of the way with a transform, runs the
2124
+ * keyboard drag mode, and calls back with the new order. A framework binding
2125
+ * wires the two together and renders the live region; it decides nothing here.
2126
+ */
2127
+ export type SortableDirection = 'up' | 'down';
2128
+ export type SortableOrientation = 'vertical' | 'horizontal';
2129
+ /** The edges of an item, as `getBoundingClientRect` gives them. */
2130
+ export interface SortableRect {
2131
+ top: number;
2132
+ bottom: number;
2133
+ left: number;
2134
+ right: number;
2135
+ }
2136
+ export interface SortablePoint {
2137
+ x: number;
2138
+ y: number;
2139
+ }
2140
+ /** The attribute carrying an item's id. It is what the controller walks the list by. */
2141
+ export declare const SORTABLE_ID_ATTRIBUTE = "data-sortable-id";
2142
+ /** The attribute marking the element a drag starts from. */
2143
+ export declare const SORTABLE_HANDLE_ATTRIBUTE = "data-sortable-handle";
2144
+ /** The live-region copy, one line per gesture step. Indexes are counted from zero. */
2145
+ export declare const sortableAnnouncements: {
2146
+ pickup: (label: string, index: number, count: number) => string;
2147
+ move: (label: string, index: number, count: number) => string;
2148
+ drop: (label: string) => string;
2149
+ cancel: () => string;
2150
+ };
2151
+ export interface SortableModelOptions {
2152
+ /** The name announced for an id. The id itself by default. */
2153
+ label?: (id: string) => string;
2154
+ }
2155
+ export interface SortableModel {
2156
+ readonly ids: readonly string[];
2157
+ indexOf(id: string): number;
2158
+ labelOf(id: string): string;
2159
+ /** The order with the entry at `from` moved to `to`. An index off either end leaves it alone. */
2160
+ moveTo(from: number, to: number): string[];
2161
+ /** The order with `id` moved one place towards the start or the end. */
2162
+ keyboardMove(id: string, direction: SortableDirection): string[];
2163
+ pickedUp(id: string): string;
2164
+ movedTo(id: string, index: number): string;
2165
+ dropped(id: string): string;
2166
+ cancelled(): string;
2167
+ }
2168
+ /** A frozen view of one order: every move answers with a new array. */
2169
+ export declare function createSortable(ids: readonly string[], options?: SortableModelOptions): SortableModel;
2170
+ /**
2171
+ * The index an item dragged from `from` lands on.
2172
+ *
2173
+ * `rects` are the items in list order, measured before the drag; `point` is the
2174
+ * dragged item's own midpoint, moved by the pointer. A neighbour's place is
2175
+ * claimed once its midpoint is crossed, so the far edge of a tall row does not
2176
+ * take a place the item is only overlapping.
2177
+ */
2178
+ export declare function sortableDropIndex(rects: readonly SortableRect[], from: number, point: SortablePoint, orientation?: SortableOrientation): number;
2179
+ /** How far one item sits from the next, so the rows a drag passes shift by a whole row. */
2180
+ export declare function sortableStride(rects: readonly SortableRect[], from: number, orientation?: SortableOrientation): number;
2181
+ export interface SortableScrollOptions {
2182
+ /** How near an edge the pointer has to be before the container scrolls. */
2183
+ threshold?: number;
2184
+ /** Pixels per frame at the edge itself. */
2185
+ speed?: number;
2186
+ orientation?: SortableOrientation;
2187
+ }
2188
+ /**
2189
+ * Pixels to scroll the container this frame, negative towards the start.
2190
+ *
2191
+ * Zero until the pointer is inside `threshold` of an edge, then proportional to
2192
+ * how far past it, up to `speed` at the edge and beyond.
2193
+ */
2194
+ export declare function sortableScrollStep(bounds: SortableRect, point: SortablePoint, options?: SortableScrollOptions): number;
2195
+ /**
2196
+ * Where every item of the list sits, by id, relative to the container.
2197
+ *
2198
+ * A measurement is compared with one taken later. Between the two the page may have
2199
+ * scrolled, or the container itself may have moved: a list inside a popover is
2200
+ * measured at the viewport's corner before the popover is placed. Container
2201
+ * coordinates read neither as a move of every row.
2202
+ */
2203
+ export declare function measureSortable(container: HTMLElement): Map<string, SortableRect>;
2204
+ export interface SortableFlipOptions {
2205
+ duration?: number;
2206
+ }
2207
+ /**
2208
+ * Move every item from where it was to where it is now, transform only.
2209
+ *
2210
+ * Called after the order has changed and the list has been redrawn. Returns the
2211
+ * fresh measurement to hold for the next change. Under reduced motion the items
2212
+ * are left where they are.
2213
+ */
2214
+ export declare function playSortableFlip(container: HTMLElement, previous: Map<string, SortableRect> | null, options?: SortableFlipOptions): Map<string, SortableRect>;
2215
+ export interface SortableControllerOptions {
2216
+ /** The element holding the items. */
2217
+ container: HTMLElement;
2218
+ /** The ids in their current order. Read at the start of every gesture and on every move. */
2219
+ order: () => readonly string[];
2220
+ /** The new order and the id that moved. */
2221
+ onOrderChange: (order: string[], id: string) => void;
2222
+ /** The name announced for an id. */
2223
+ label?: (id: string) => string;
2224
+ /** One line for the live region. */
2225
+ onAnnounce?: (message: string) => void;
2226
+ /** The id under a live gesture, or null when it ends. */
2227
+ onDraggingChange?: (id: string | null) => void;
2228
+ /** How far the pointer travels before a drag starts. */
2229
+ activationDistance?: number;
2230
+ /** How long the rows a drag passes take to shift. */
2231
+ duration?: number;
2232
+ orientation?: SortableOrientation;
2233
+ /** A true blocks every gesture. */
2234
+ disabled?: () => boolean;
2235
+ }
2236
+ export interface SortableController {
2237
+ /** The id under a live gesture, pointer or keyboard. */
2238
+ readonly active: string | null;
2239
+ /**
2240
+ * Set when a pointer drop has just committed a new order. The rows are already
2241
+ * where the drag left them, so a binding clears this and re-measures instead of
2242
+ * animating the change a second time.
2243
+ */
2244
+ settled: boolean;
2245
+ destroy(): void;
2246
+ }
2247
+ /**
2248
+ * Pointer and keyboard reordering over one list element.
2249
+ *
2250
+ * A drag starts on an element carrying the handle attribute, once the pointer has
2251
+ * travelled `activationDistance`; the dragged row follows the pointer and the rows
2252
+ * it passes shift by one row with a transform, so the gap left behind is the drop
2253
+ * slot. Escape cancels. Space or Enter on a handle enters keyboard drag mode,
2254
+ * where the arrow keys move the row a place at a time, Space, Enter or losing
2255
+ * focus drops it, and Escape puts it back.
2256
+ */
2257
+ export declare function createSortableController(options: SortableControllerOptions): SortableController;
2258
+ //#endregion
2259
+ //#region src/collection-state.d.ts
2260
+ /** How a collection keys a row. Without one, `Type:id`. */
2261
+ export type RowIdFn = (row: EntityRow) => string;
2262
+ /** True for a row the keyboard skips and the selection refuses. */
2263
+ export type RowDisabledFn = (row: EntityRow) => boolean;
2264
+ /** A row's id: the caller's, or `Type:id`. */
2265
+ export declare function rowIdOf(row: EntityRow, getRowId?: RowIdFn | null): string;
2266
+ /** True when the caller says the row is disabled. */
2267
+ export declare function rowIsDisabled(row: EntityRow, isRowDisabled?: RowDisabledFn | null): boolean;
2268
+ /**
2269
+ * Where a cursor lands moving `step` from `from`, skipping disabled entries.
2270
+ *
2271
+ * The walk stops at the ends rather than wrapping, and answers `from` when every
2272
+ * entry that way is disabled, so a key press on the last enabled row does nothing.
2273
+ */
2274
+ export declare function nextEnabledIndex(count: number, from: number, step: number, disabled: (index: number) => boolean): number;
2275
+ /** The first enabled entry at or after `from`, walking `step`. -1 when there is none. */
2276
+ export declare function firstEnabledIndex(count: number, from: number, step: number, disabled: (index: number) => boolean): number;
2277
+ /** The ids the rows of `refs` are keyed under. A ref no loaded row stands for is dropped. */
2278
+ export declare function idsForRefs(rows: readonly EntityRow[], refs: readonly EntityRef[], getRowId?: RowIdFn | null): string[];
2279
+ /** The rows the ids stand for, in row order. */
2280
+ export declare function refsForIds(rows: readonly EntityRow[], ids: readonly string[], getRowId?: RowIdFn | null): EntityRef[];
2281
+ /** `ids` with `id` added or removed. `on` forces the direction. */
2282
+ export declare function toggleId(ids: readonly string[], id: string, on?: boolean): string[];
2283
+ /** True when two id lists hold the same ids, whatever their order. */
2284
+ export declare function sameIds(a: readonly string[], b: readonly string[]): boolean;
2285
+ /** True when two selections name the same rows, whatever their order. */
2286
+ export declare function sameRefs(a: readonly EntityRef[], b: readonly EntityRef[]): boolean;
2287
+ /** True when two sort lists name the same keys in the same order. */
2288
+ export declare function sameSort(a: readonly SortSpec[], b: readonly SortSpec[]): boolean;
2289
+ /**
2290
+ * True when two filters reach the server as the same group.
2291
+ *
2292
+ * The editor's tree and the wire hash are two spellings of one filter, so a `filters`
2293
+ * prop holding the tree and a source holding the hash agree, and neither overwrites
2294
+ * the other.
2295
+ */
2296
+ export declare function sameFilters(a: SourceFilters, b: SourceFilters): boolean;
2297
+ /** A sort picker's keys as a source's sort. */
2298
+ export declare function toSortSpecs(keys: readonly SortKey[]): SortSpec[];
2299
+ /** A source's sort as a sort picker's keys. */
2300
+ export declare function toSortKeys(sort: readonly SortSpec[]): SortKey[];
2301
+ /** A selection with `ref` added or removed. */
2302
+ export declare function toggleRef(selection: readonly EntityRef[], ref: EntityRef): EntityRef[];
2303
+ /** What a header checkbox reads: every selectable row, or some of them. */
2304
+ export interface SelectionState {
2305
+ all: boolean;
2306
+ some: boolean;
2307
+ }
2308
+ /**
2309
+ * The tri-state of a select-all control over the rows that are loaded.
2310
+ *
2311
+ * A disabled row refuses its own box, so it is left out of both readings and a page
2312
+ * of nothing but disabled rows is neither all nor some.
2313
+ */
2314
+ export declare function selectionState(rows: readonly EntityRow[], selection: readonly EntityRef[], isRowDisabled?: RowDisabledFn | null): SelectionState;
2315
+ /** Every selectable loaded row, as a selection. */
2316
+ export declare function selectableRefs(rows: readonly EntityRow[], isRowDisabled?: RowDisabledFn | null): EntityRef[];
2317
+ /**
2318
+ * Which groups are shut.
2319
+ *
2320
+ * A mode with exceptions rather than a set of keys, because the keys a list holds
2321
+ * are only the ones loaded so far: Collapse all written as a list says nothing
2322
+ * about the groups the next page brings, and they arrive open under a header that
2323
+ * said everything was shut. `all` is what a group does unless `except` names it.
2324
+ */
2325
+ export interface CollapseState {
2326
+ /** True when every group is shut but the ones `except` names. */
2327
+ all: boolean;
2328
+ /** The keys that go the other way from `all`. */
2329
+ except: string[];
2330
+ }
2331
+ /** Every group shut, including the ones not loaded yet. */
2332
+ export declare function collapseAll(): CollapseState;
2333
+ /** Every group open. */
2334
+ export declare function expandAll(): CollapseState;
2335
+ /**
2336
+ * A caller's value as a state. A bare key list is the open mode with those keys
2337
+ * shut, and a mode written without `except` has none.
2338
+ */
2339
+ export declare function asCollapseState(value: readonly string[] | Pick<CollapseState, 'all'> | CollapseState | null | undefined): CollapseState;
2340
+ /** True when the group under `key` is shut. */
2341
+ export declare function isCollapsed(state: CollapseState, key: string): boolean;
2342
+ /** `state` with the group under `key` shut or open. `on` forces the direction. */
2343
+ export declare function toggleCollapsed(state: CollapseState, key: string, on?: boolean): CollapseState;
2344
+ /** The keys of `keys` that are shut, in the order given. */
2345
+ export declare function collapsedKeys(state: CollapseState, keys: readonly string[]): string[];
2346
+ /**
2347
+ * The state a set of shut keys reads as, under the mode already in force.
2348
+ *
2349
+ * For a widget whose own machinery answers which headers are shut rather than which
2350
+ * key was pressed. The mode is kept, so the groups the next page brings still follow
2351
+ * it, and an exception for a key no longer drawn is dropped.
2352
+ */
2353
+ export declare function collapseStateFrom(state: CollapseState, shut: readonly string[], keys: readonly string[]): CollapseState;
2354
+ /** True when two collapse states shut the same groups. */
2355
+ export declare function sameCollapse(a: CollapseState, b: CollapseState): boolean;
2356
+ //#endregion
2357
+ //#region src/entity-card.d.ts
2358
+ export interface EntityCardOptions {
2359
+ /** Dotted field paths for the grid, in order. */
2360
+ fields?: readonly string[];
2361
+ /** The `image` field to read the thumbnail from. Default `image`. */
2362
+ imagePath?: string;
2363
+ }
2364
+ /** One field of the grid, resolved. */
2365
+ export interface EntityCardColumn {
2366
+ path: string;
2367
+ /** `pathLabel` of the resolved path; the raw path when the path does not resolve. */
2368
+ label: string;
2369
+ dataType: string;
2370
+ /** The leaf field's schema, for a status label out of `display_values` (probe 009). */
2371
+ field: FieldSchema | null;
2372
+ value: unknown;
2373
+ }
2374
+ export interface EntityCardModel {
2375
+ entity: EntityRef;
2376
+ /** The row the model was built from, for a caller's own label or sub-label. */
2377
+ row: EntityRow;
2378
+ name: string;
2379
+ /** The type's display name, from the site's enabled types. */
2380
+ typeLabel: string;
2381
+ thumbnail: string | null;
2382
+ /** The type's status field and the row's code, when the type has one. */
2383
+ status: {
2384
+ code: string;
2385
+ field: FieldSchema;
2386
+ } | null;
2387
+ /** The caller's paths, less the one naming the type's own status field. */
2388
+ columns: EntityCardColumn[];
2389
+ }
2390
+ /**
2391
+ * The fields one card read asks for: the identity chain the type actually has,
2392
+ * its thumbnail, its status field and the caller's paths.
2393
+ *
2394
+ * The chain is intersected with the type's schema because a name absent from a
2395
+ * type is not an empty column but a 400 on the read: Task has neither `code` nor
2396
+ * `name` (entity_types/Task).
2397
+ */
2398
+ export declare function entityCardFields(context: SgContext, entityType: string, options?: EntityCardOptions): Promise<string[]>;
2399
+ /** The card model for a row already read. Resolves the labels and types of the paths. */
2400
+ export declare function describeEntityCard(context: SgContext, row: EntityRow, options?: EntityCardOptions): Promise<EntityCardModel>;
2401
+ /**
2402
+ * The column one metadata slot of a tile draws, or null when there is nothing to draw.
2403
+ *
2404
+ * The slot takes the same spec as every other row-anatomy prop: a bare path, resolved
2405
+ * against the card's own columns so the value renders by its data type, or a column the
2406
+ * caller already resolved, which is taken at its word and costs no lookup. A slot naming
2407
+ * the row's own status field draws it: the grid leaves that field to the header, and a
2408
+ * caller who puts it on the metadata line has asked for it there. A status reads the name
2409
+ * the site gives the code, off the field's `display_values`, which covers the whole
2410
+ * vocabulary (recipes/010, field_types/status_list).
2411
+ */
2412
+ export declare function entityCardSlot(card: EntityCardModel, spec: FieldSpec | null | undefined): EntityCardColumn | null;
2413
+ /** The card model for a reference: one search for the row, then its description. */
2414
+ export declare function loadEntityCard(context: SgContext, entity: EntityRef, options?: EntityCardOptions): Promise<EntityCardModel>;
2415
+ //#endregion
2416
+ //#region src/tree.d.ts
2417
+ /** One node of a level, as a loader hands it over. */
2418
+ export interface TreeSourceNode {
2419
+ /** The key everything else is held under, and what the loader is called with. */
2420
+ path: string;
2421
+ label: string;
2422
+ ref: HierarchyRef;
2423
+ /** False when opening this node would return nothing. */
2424
+ hasChildren: boolean;
2425
+ /** Fields of the row the node stands for, flattened. */
2426
+ values?: Record<string, unknown>;
2427
+ }
2428
+ /** One level: the node that was opened, and the level below it. */
2429
+ export interface TreeLevel {
2430
+ node: TreeSourceNode;
2431
+ children: TreeSourceNode[];
2432
+ }
2433
+ /** Reads one level. A node already read is never read again. */
2434
+ export type TreeLoader = (path: string) => Promise<TreeLevel>;
2435
+ /** A node in the tree, with where it sits. */
2436
+ export interface TreeNode {
2437
+ path: string;
2438
+ label: string;
2439
+ ref: HierarchyRef;
2440
+ /** The row the node stands for, or null where it stands for a type or nothing. */
2441
+ entity: EntityRef | null;
2442
+ hasChildren: boolean;
2443
+ parentPath: string | null;
2444
+ /** Depth below the root, which is 0. */
2445
+ level: number;
2446
+ /** Fields of the row the node stands for, flattened. Empty until a loader fills it. */
2447
+ values: Record<string, unknown>;
2448
+ }
2449
+ export type TreeCheckState = 'checked' | 'mixed' | 'unchecked';
2450
+ export type TreeSelectionMode = 'none' | 'single' | 'multiple';
2451
+ export type TreeStatus = 'idle' | 'loading' | 'ready' | 'error';
2452
+ /** One line of the visible list, with everything a row draws itself from. */
2453
+ export interface TreeRow {
2454
+ node: TreeNode;
2455
+ expanded: boolean;
2456
+ /** True while this node's level is being read. */
2457
+ loading: boolean;
2458
+ checked: TreeCheckState;
2459
+ selected: boolean;
2460
+ /** True on the one node that owns the tab stop. */
2461
+ focused: boolean;
2462
+ /** True when the caller disabled the node: the cursor skips it and it never selects. */
2463
+ disabled: boolean;
2464
+ /** True when the search placed this row, or its label holds every word. */
2465
+ match: boolean;
2466
+ }
2467
+ /** Everything a view renders. A new object on every change, so identity is the signal. */
2468
+ export interface TreeState {
2469
+ rows: TreeRow[];
2470
+ status: TreeStatus;
2471
+ error: Error | null;
2472
+ /** Path of the focus cursor, or null before the root is read. */
2473
+ cursor: string | null;
2474
+ /** The text the tree is searching for. */
2475
+ search: string;
2476
+ /** True while a search is in flight. */
2477
+ searching: boolean;
2478
+ /** Paths of the rows the search marked, in visible order. */
2479
+ matches: string[];
2480
+ /** Paths whose box is fully checked, branches included. */
2481
+ checked: string[];
2482
+ selected: string[];
2483
+ /** Paths that are open, in visible order. */
2484
+ expanded: string[];
2485
+ }
2486
+ /** A keyboard event, reduced to what the model reads. */
2487
+ export interface TreeKey {
2488
+ key: string;
2489
+ ctrlKey?: boolean;
2490
+ metaKey?: boolean;
2491
+ shiftKey?: boolean;
2492
+ altKey?: boolean;
2493
+ }
2494
+ export interface TreeSelectOptions {
2495
+ /** Add to the selection instead of replacing it. Ignored unless selection is `multiple`. */
2496
+ additive?: boolean;
2497
+ }
2498
+ export interface TreeOptions {
2499
+ loader: TreeLoader;
2500
+ /** Where the tree starts, `/Project/<id>` against the hierarchy loader. */
2501
+ rootPath: string;
2502
+ selection?: TreeSelectionMode;
2503
+ /** Places rows by text. Without one, a search marks the labels already loaded. */
2504
+ searcher?: TreeSearcher;
2505
+ /** Types a search covers. Default: the types the levels already read stand for. */
2506
+ searchTypes?: readonly string[];
2507
+ /** How many levels `expandAll` opens under a node. Default 3. */
2508
+ expandDepth?: number;
2509
+ /** True for a node the cursor skips and selection refuses. */
2510
+ disabled?: (node: TreeNode) => boolean;
2511
+ /** How long a type-ahead buffer survives, in milliseconds. Default 800. */
2512
+ typeAheadMs?: number;
2513
+ /** The clock type-ahead measures on. */
2514
+ now?: () => number;
2515
+ }
2516
+ /** One row a search found, and where the tree puts it. */
2517
+ export interface TreeSearchHit {
2518
+ ref: EntityRef;
2519
+ label: string;
2520
+ /** One path per level, root first; the last entry is the row itself. */
2521
+ incrementalPath: string[];
2522
+ }
2523
+ /** Matches words and answers where each hit sits. */
2524
+ export type TreeSearcher = (text: string, entityTypes: readonly string[]) => Promise<TreeSearchHit[]>;
2525
+ export interface TreeEngine {
2526
+ readonly rootPath: string;
2527
+ snapshot(): TreeState;
2528
+ subscribe(listener: () => void): () => void;
2529
+ /** A node already loaded, by path. */
2530
+ node(path: string): TreeNode | undefined;
2531
+ /** The paths of a node's level, once it has been read. */
2532
+ childPaths(path: string): string[];
2533
+ checkStateOf(path: string): TreeCheckState;
2534
+ /** Read the root and open it. */
2535
+ load(): Promise<void>;
2536
+ expand(path: string): Promise<void>;
2537
+ collapse(path: string): void;
2538
+ toggle(path: string): Promise<void>;
2539
+ /** Open every level down to a path, or to the deepest entry of an incremental path. */
2540
+ expandToPath(refs: string | readonly string[]): Promise<void>;
2541
+ /** Open every level under a node, breadth first, down to `depth`. */
2542
+ expandAll(path: string, depth?: number): Promise<void>;
2543
+ /** Move the focus cursor. A path outside the visible list is ignored. */
2544
+ focus(path: string): void;
2545
+ /** Open exactly these paths, reading whatever level is not loaded, and shut the rest. */
2546
+ setExpanded(paths: readonly string[]): Promise<void>;
2547
+ /** Select exactly these paths. A path outside the tree is dropped. */
2548
+ setSelected(paths: readonly string[]): void;
2549
+ setChecked(path: string, on: boolean): void;
2550
+ toggleChecked(path: string): void;
2551
+ /** The rows the checked nodes stand for, in visible order. */
2552
+ checkedRefs(): EntityRef[];
2553
+ select(path: string, options?: TreeSelectOptions): void;
2554
+ clearSelection(): void;
2555
+ /**
2556
+ * Place every row the words match, open the tree onto them and mark them. An
2557
+ * empty text drops the marks and restores the expansion the search opened onto.
2558
+ */
2559
+ search(text: string): Promise<void>;
2560
+ /** True when the tree handled the key, which is when the caller stops it. */
2561
+ keyDown(event: TreeKey): boolean;
2562
+ }
2563
+ export declare function createTree(options: TreeOptions): TreeEngine;
2564
+ export interface HierarchyLoaderOptions {
2565
+ /** Field paths read for the rows the nodes of a level stand for. */
2566
+ fields?: readonly string[];
2567
+ }
2568
+ /**
2569
+ * `hierarchyExpand` as a tree loader.
2570
+ *
2571
+ * With `fields`, the rows a level's nodes stand for are read once per type over
2572
+ * the ids just returned, so a sub-label or a status comes back with the level
2573
+ * rather than one read per row. A name a type does not have is dropped at 200, so
2574
+ * one list of names serves every type (probe 003).
2575
+ */
2576
+ export declare function hierarchyLoader(client: SgClient, options?: HierarchyLoaderOptions): TreeLoader;
2577
+ export interface HierarchySearcherOptions {
2578
+ /** Answers whether a type carries `project`, so the words are scoped to it. */
2579
+ schema?: FieldLookup;
2580
+ /** Rows the text search asks for. Each one costs a path lookup. Default 10. */
2581
+ limit?: number;
2582
+ }
2583
+ /**
2584
+ * `_text_search` and `hierarchy/_search` chained, as a tree searcher.
2585
+ *
2586
+ * The hierarchy endpoint takes an entity and answers where it sits rather than
2587
+ * matching words, so the words go to `_text_search` first and each hit is then
2588
+ * asked for its path (post_hierarchy_search). A root path naming a project scopes
2589
+ * the words to it, on every type that carries a `project` field.
2590
+ */
2591
+ export declare function hierarchySearcher(client: SgClient, rootPath: string, options?: HierarchySearcherOptions): TreeSearcher;
2592
+ /** The schemas a tree row needs, one entry per type the tree has shown. */
2593
+ export interface TreeFieldPlan {
2594
+ /** The type's own status field, when it has a real one. */
2595
+ status: Record<string, FieldSchema | null>;
2596
+ /** The secondary column's field, per type. */
2597
+ secondary: Record<string, FieldSchema | null>;
2598
+ /** The sub-label field's, per type, so the line reads by its data type. */
2599
+ subLabel: Record<string, FieldSchema | null>;
2600
+ /** `Status` rows by code, read once and only when a status is on show (probe 010). */
2601
+ statuses: Record<string, StatusRecord> | null;
2602
+ }
2603
+ /** The row-anatomy fields a tree resolves per type, as bare paths. */
2604
+ export interface TreeFieldNames {
2605
+ secondary?: string | undefined;
2606
+ subLabel?: string | undefined;
2607
+ }
2608
+ /**
2609
+ * The status field, the secondary field and the sub-label field of every type a tree shows.
2610
+ *
2611
+ * A type's status field is whichever field is a `status_list`. Project's
2612
+ * `sg_status` is a plain `list` with no Status row behind its values, so it
2613
+ * carries no badge (entity_types/Project, probe 009).
2614
+ */
2615
+ export declare function resolveTreeFields(schema: SchemaService, statusTable: StatusService, types: readonly string[], names?: TreeFieldNames): Promise<TreeFieldPlan>;
2616
+ /** The field names a level is read under so a row can show a status: the two the API uses. */
2617
+ export declare const TREE_STATUS_FIELDS: readonly ['sg_status_list', 'sg_status'];
2618
+ //#endregion
2619
+ //#region src/paging.d.ts
2620
+ /** How a collection walks a set: a page number, a load-more row, or the scroller. */
2621
+ export type PagingMode = 'pages' | 'more' | 'scroll';
2622
+ /** Rows before the last at which a scroller asks for the next page. */
2623
+ export declare const SCROLL_THRESHOLD = 5;
2624
+ /** The source mode a paging mode needs: `pages` shows one page, the other two append. */
2625
+ export declare function sourceModeFor(paging: PagingMode): SourceMode;
2626
+ /**
2627
+ * True when every row the filter matches is loaded.
2628
+ *
2629
+ * A set that has never been read has not reached its end, however empty it is.
2630
+ */
2631
+ export declare function reachedEnd(state: EntitySourceState): boolean;
2632
+ /** True when the next page may be asked for now. */
2633
+ export declare function canLoadNext(state: EntitySourceState, paging: PagingMode): boolean;
2634
+ /** True when a read failed with rows already on screen: the error belongs under them, with a retry. */
2635
+ export declare function hasFailedPage(state: EntitySourceState): boolean;
2636
+ export interface LoadNextOptions {
2637
+ paging: PagingMode;
2638
+ /** Index of the last row the viewport reaches. -1 when none is. */
2639
+ lastVisible: number;
2640
+ /** Rows before the last at which the next page is asked for. Default `SCROLL_THRESHOLD`. */
2641
+ threshold?: number;
2642
+ }
2643
+ /**
2644
+ * True when the scroller has reached far enough down for the next page.
2645
+ *
2646
+ * A failed page is not retried here, only on the retry the error line carries:
2647
+ * a scroller that retried on its own would ask again on every pixel.
2648
+ */
2649
+ export declare function shouldLoadNext(state: EntitySourceState, options: LoadNextOptions): boolean;
2650
+ /**
2651
+ * True when a cursor stepping to `index` runs past the loaded rows and should ask
2652
+ * for the next page instead of moving. The cursor stays where it is until the rows
2653
+ * arrive.
2654
+ */
2655
+ export declare function loadsOnArrowDown(state: EntitySourceState, paging: PagingMode, index: number): boolean;
2656
+ /** A contiguous run of rows sharing a value, under the key a collapsed group is named by. */
2657
+ export interface KeyedRowGroup extends RowGroup {
2658
+ key: string;
2659
+ }
2660
+ /**
2661
+ * Contiguous runs with a stable key.
2662
+ *
2663
+ * The key is the run's position and its value, so a page whose first rows carry
2664
+ * the value the last run carries grows that run rather than opening a second one,
2665
+ * and a group shut before the page arrived is still shut after it.
2666
+ */
2667
+ export declare function groupRowsKeyed(rows: readonly EntityRow[], by: GroupBy): KeyedRowGroup[];
2668
+ /**
2669
+ * What a collection draws in place of its rows.
2670
+ *
2671
+ * A read that failed with rows already loaded is not this block: those rows stay and
2672
+ * the error goes under them, which is what `hasFailedPage` answers.
2673
+ */
2674
+ export declare function collectionView(state: EntitySourceState, lines: number): WidgetState;
2675
+ /** What sits under the last loaded row, if anything. */
2676
+ export type CollectionBottom = 'error' | 'loading' | 'more' | 'sentinel' | null;
2677
+ /**
2678
+ * The block under the rows: the failed page with its retry, the skeleton of a page on
2679
+ * the way, the load-more row, or the sentinel a scroller watches. One at a time.
2680
+ */
2681
+ export declare function collectionBottom(state: EntitySourceState, paging: PagingMode): CollectionBottom;
2682
+ //#endregion
2683
+ //#region src/list-chrome.d.ts
2684
+ /** A read is on its way and the list is standing by. */
2685
+ export declare const SEARCHING_LABEL = "Searching…";
2686
+ /** What the live row says about a list that answered. */
2687
+ export declare function countLine(count: number): string;
2688
+ /** What the live row is told about the list under it. */
2689
+ export interface ListStatusState {
2690
+ /** A read is in flight. */
2691
+ loading: boolean;
2692
+ /** Rows on show. */
2693
+ count: number;
2694
+ /** What the failed read said, or null. */
2695
+ error?: string | null;
2696
+ /** Nothing is announced until something has been asked for. */
2697
+ asked?: boolean;
2698
+ }
2699
+ /**
2700
+ * The one line a list's live region carries.
2701
+ *
2702
+ * A failure wins, then a read in flight, then what the read answered. Nothing is
2703
+ * announced before anything has been asked for, so an idle list stays silent.
2704
+ */
2705
+ export declare function listStatus(state: ListStatusState, labels?: StateLabels): string;
2706
+ /** How far a scroller has more content past each of its edges, in pixels. */
2707
+ export interface OverflowEdges {
2708
+ start: number;
2709
+ end: number;
2710
+ }
2711
+ /** The room above and below what a scroller shows. */
2712
+ export declare function overflowEdges(scrollTop: number, scrollHeight: number, clientHeight: number): OverflowEdges;
2713
+ /** The variables the fade reads. The names are Base UI's, so one class string serves both registries. */
2714
+ export declare const OVERFLOW_START_VAR = "--scroll-area-overflow-y-start";
2715
+ export declare const OVERFLOW_END_VAR = "--scroll-area-overflow-y-end";
2716
+ /** Write a scroller's overflow onto itself, so the fade on each edge follows what is past it. */
2717
+ export declare function markOverflow(list: HTMLElement | null | undefined): boolean;
2718
+ /**
2719
+ * Keep a scroller's overflow variables current while it is on screen.
2720
+ *
2721
+ * The measurement follows a scroll, a resize of the scroller and a change to what it
2722
+ * holds, which is every way the room past an edge can move. Returns the teardown.
2723
+ */
2724
+ export declare function watchOverflow(list: HTMLElement | null | undefined): () => void;
2725
+ //#endregion
2726
+ export { ConditionValue, DATA_TYPES, DISPLAY_NAME_FIELDS, DataType, EVENT_LOG_FIELDS, EntityRef, EntityRow, EntityTypeInfo, EventLogEntry, EventLogOptions, EventLogResult, FIELD_SCHEMA_OVERRIDES, FieldSchema, FilterCondition, FilterGroup, FilterNode, FollowingOptions, HierarchyNode, HierarchyPath, HierarchyRef, LogicalOperator, NATIVE_STATUSES, NEGATING_OPERATORS, NativeStatus, OPERATORS, OPERATORS_BY_TYPE, Operator, RawFieldSchema, RawFieldsResponse, RawHierarchyNode, RawProperty, RestClient, RestClientOptions, Rgb, STOCK_ICON_CELLS, STOCK_ICON_DATA_URLS, STOCK_ICON_KEYS, STOCK_SPRITE_PATH, Scalar, SearchOptions, SearchResult, SgApiError, SgClient, SpriteCell, StatusGlyph, StatusIcon, StatusOption, StatusPaint, StatusRecord, StockIconSource, SummarizeOptions, SummarizeResult, SummaryField, SummaryGroup, SummaryGrouping, SummaryType, TIME_UNITS, TextSearchFilter, TextSearchRow, ThreadAuthor, ThreadRow, TimeUnit, UploadFile, UploadResult, VALUE_SHAPE, ValueShape, WireCondition, WireGroup, condition, displayNameOf, emptyFilter, eventLogFilters, fieldSchemaOverride, foregroundFor, fromWire, group, hierarchyEntity, intersectStatuses, isBlankCondition, isDataType, isEmptyFilter, isFilterable, isLinkType, isNativeStatus, isNumericType, isOperator, isTemporalType, nativeStatus, normalizeEventLogEntry, normalizeField, normalizeFields, normalizeHierarchyNode, operatorsFor, parseBgColor, referencedPaths, relativeLuminance, rgbToCss, spriteStyle, statusFieldFor, statusFieldNameFor, statusGlyph, statusLabel, statusPaint, stockIconSource, supportsOperator, toApi3Hash, toFilterArray, undeclaredField, usableStatuses, walk };