@xnetjs/data 0.2.0 → 0.4.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,645 @@
1
+ /**
2
+ * Cell value types for database rows.
3
+ *
4
+ * Cell values are stored as dynamic properties on DatabaseRow nodes,
5
+ * keyed by column ID with a `cell_` prefix to avoid collisions with
6
+ * schema-defined properties.
7
+ */
8
+ /**
9
+ * Reference to a file stored in the system.
10
+ * Same shape as the blob-layer FileRef (schema/properties/file.ts):
11
+ * content-addressed, resolved to URLs through the BlobService.
12
+ */
13
+ interface FileRef {
14
+ /** Content-addressed ID (CID) of the file */
15
+ cid: string;
16
+ /** Original file name */
17
+ name: string;
18
+ /** MIME type */
19
+ mimeType: string;
20
+ /** File size in bytes */
21
+ size: number;
22
+ }
23
+ /**
24
+ * A date range with start and end dates.
25
+ */
26
+ interface DateRange {
27
+ /** Start date (ISO 8601 string) */
28
+ start: string;
29
+ /** End date (ISO 8601 string) */
30
+ end: string;
31
+ }
32
+ /**
33
+ * All possible cell value types.
34
+ *
35
+ * - string: text, url, email, phone, select (option ID), person (DID)
36
+ * - number: number
37
+ * - boolean: checkbox
38
+ * - string (ISO 8601): date
39
+ * - DateRange: dateRange
40
+ * - string[]: multiSelect (option IDs), relation (row IDs)
41
+ * - FileRef: file
42
+ * - null: empty cell
43
+ */
44
+ type CellValue = string | number | boolean | DateRange | string[] | FileRef | null;
45
+ /**
46
+ * Prefix for cell value property keys.
47
+ * This prevents collisions with schema-defined properties like 'database' and 'sortKey'.
48
+ */
49
+ declare const CELL_PREFIX = "cell_";
50
+ /**
51
+ * Convert a column ID to a cell property key.
52
+ *
53
+ * @example
54
+ * cellKey('name') // 'cell_name'
55
+ * cellKey('status') // 'cell_status'
56
+ */
57
+ declare function cellKey(columnId: string): string;
58
+ /**
59
+ * Check if a property key is a cell value key.
60
+ *
61
+ * @example
62
+ * isCellKey('cell_name') // true
63
+ * isCellKey('database') // false
64
+ */
65
+ declare function isCellKey(key: string): boolean;
66
+ /**
67
+ * Extract the column ID from a cell property key.
68
+ *
69
+ * @example
70
+ * columnIdFromKey('cell_name') // 'name'
71
+ * columnIdFromKey('cell_status') // 'status'
72
+ */
73
+ declare function columnIdFromKey(key: string): string;
74
+ /**
75
+ * Convert a record of column ID -> value to cell key -> value.
76
+ *
77
+ * @example
78
+ * toCellProperties({ name: 'John', age: 30 })
79
+ * // { cell_name: 'John', cell_age: 30 }
80
+ */
81
+ declare function toCellProperties(cells: Record<string, CellValue>): Record<string, CellValue>;
82
+ /**
83
+ * Extract cell values from a node's properties, converting cell keys back to column IDs.
84
+ *
85
+ * @example
86
+ * fromCellProperties({ cell_name: 'John', cell_age: 30, database: 'db1' })
87
+ * // { name: 'John', age: 30 }
88
+ */
89
+ declare function fromCellProperties(properties: Record<string, unknown>): Record<string, CellValue>;
90
+ /**
91
+ * Check if a value is a valid DateRange.
92
+ */
93
+ declare function isDateRange(value: unknown): value is DateRange;
94
+ /**
95
+ * Check if a value is a valid FileRef.
96
+ */
97
+ declare function isFileRef(value: unknown): value is FileRef;
98
+ /**
99
+ * Check if a value is a valid CellValue.
100
+ */
101
+ declare function isCellValue(value: unknown): value is CellValue;
102
+
103
+ /**
104
+ * Column type definitions for database columns.
105
+ *
106
+ * Columns are stored in the database's Y.Doc as a Y.Array of Y.Maps,
107
+ * enabling CRDT-based ordering and real-time schema sync.
108
+ */
109
+ /**
110
+ * All supported column types.
111
+ */
112
+ type ColumnType = 'text' | 'number' | 'checkbox' | 'date' | 'dateRange' | 'select' | 'multiSelect' | 'person' | 'url' | 'email' | 'phone' | 'file' | 'relation' | 'tasks' | 'rollup' | 'formula' | 'richText' | 'created' | 'createdBy' | 'updated' | 'updatedBy';
113
+ /**
114
+ * Column definition stored in the database's Y.Doc.
115
+ */
116
+ interface ColumnDefinition {
117
+ /** Unique column ID (nanoid) */
118
+ id: string;
119
+ /** Display name */
120
+ name: string;
121
+ /** Column type */
122
+ type: ColumnType;
123
+ /** Type-specific configuration */
124
+ config: ColumnConfig;
125
+ /** Width in pixels (for table view) */
126
+ width?: number;
127
+ /** Whether this is the title column */
128
+ isTitle?: boolean;
129
+ }
130
+ /**
131
+ * Union of all column configuration types.
132
+ */
133
+ type ColumnConfig = TextColumnConfig | NumberColumnConfig | SelectColumnConfig | RelationColumnConfig | RollupColumnConfig | FormulaColumnConfig | DateColumnConfig | FileColumnConfig | EmptyConfig;
134
+ /**
135
+ * Empty config for simple types with no configuration.
136
+ */
137
+ type EmptyConfig = Record<string, never>;
138
+ /**
139
+ * Text column configuration.
140
+ */
141
+ interface TextColumnConfig {
142
+ /** Maximum length (optional) */
143
+ maxLength?: number;
144
+ }
145
+ /**
146
+ * Number column configuration.
147
+ */
148
+ interface NumberColumnConfig {
149
+ /** Number format */
150
+ format?: 'number' | 'percent' | 'currency';
151
+ /** Currency code (if format is currency) */
152
+ currency?: string;
153
+ /** Decimal places */
154
+ precision?: number;
155
+ }
156
+ /**
157
+ * Select/MultiSelect column configuration.
158
+ */
159
+ interface SelectColumnConfig {
160
+ /** Available options */
161
+ options: SelectOption[];
162
+ /** Allow creating new options inline */
163
+ allowCreate?: boolean;
164
+ }
165
+ /**
166
+ * A single select option.
167
+ */
168
+ interface SelectOption {
169
+ id: string;
170
+ name: string;
171
+ color?: SelectColor;
172
+ }
173
+ /**
174
+ * Available colors for select options.
175
+ */
176
+ type SelectColor = 'gray' | 'brown' | 'orange' | 'yellow' | 'green' | 'blue' | 'purple' | 'pink' | 'red';
177
+ /**
178
+ * Relation column configuration.
179
+ */
180
+ interface RelationColumnConfig {
181
+ /** Target database ID */
182
+ targetDatabase: string;
183
+ /** Allow multiple relations */
184
+ allowMultiple?: boolean;
185
+ }
186
+ /**
187
+ * Rollup column configuration.
188
+ */
189
+ interface RollupColumnConfig {
190
+ /** Column ID of the relation to aggregate */
191
+ relationColumn: string;
192
+ /** Column ID on related rows to aggregate */
193
+ targetColumn: string;
194
+ /** Aggregation function */
195
+ aggregation: RollupAggregation;
196
+ }
197
+ /**
198
+ * Available rollup aggregation functions.
199
+ */
200
+ type RollupAggregation = 'sum' | 'avg' | 'count' | 'min' | 'max' | 'concat' | 'unique' | 'empty' | 'notEmpty' | 'percentEmpty' | 'percentNotEmpty';
201
+ /**
202
+ * Formula column configuration.
203
+ */
204
+ interface FormulaColumnConfig {
205
+ /** Formula expression with {{columnId}} references */
206
+ expression: string;
207
+ /** Result type */
208
+ resultType: 'text' | 'number' | 'date' | 'checkbox';
209
+ /** Cached dependencies (auto-computed from expression) */
210
+ dependencies?: string[];
211
+ }
212
+ /**
213
+ * Date column configuration.
214
+ */
215
+ interface DateColumnConfig {
216
+ /** Include time */
217
+ includeTime?: boolean;
218
+ /** Date format */
219
+ format?: 'full' | 'short' | 'relative';
220
+ }
221
+ /**
222
+ * File column configuration.
223
+ */
224
+ interface FileColumnConfig {
225
+ /** Accepted MIME types */
226
+ accept?: string[];
227
+ /** Allow multiple files */
228
+ allowMultiple?: boolean;
229
+ }
230
+ /**
231
+ * Check if a column type stores data in NodeStore (vs Y.Doc).
232
+ */
233
+ declare function isNodeStoreColumnType(type: ColumnType): boolean;
234
+ /**
235
+ * Check if a column type is computed (formula/rollup).
236
+ */
237
+ declare function isComputedColumnType(type: ColumnType): boolean;
238
+ /**
239
+ * Check if a column type is auto-populated.
240
+ */
241
+ declare function isAutoColumnType(type: ColumnType): boolean;
242
+ /**
243
+ * Check if a column type uses Y.Doc for storage.
244
+ */
245
+ declare function isYDocColumnType(type: ColumnType): boolean;
246
+
247
+ /**
248
+ * Field type definitions for the V2 database model.
249
+ *
250
+ * V2 stores fields as first-class DatabaseField nodes (see
251
+ * schema/schemas/database-field.ts) ordered by fractional sortKey, rather
252
+ * than as Y.Array entries in the database Y.Doc. The type/config unions are
253
+ * shared with the legacy column model (column-types.ts) so the pure engines
254
+ * (filter/sort/group/rollup/formula) work unchanged.
255
+ *
256
+ * Select/multiSelect options are NOT part of the field config in V2 — they
257
+ * are DatabaseSelectOption nodes keyed by `field`, so concurrent option
258
+ * creation merges cleanly. SelectFieldConfig retains only behavioral flags.
259
+ */
260
+
261
+ /** All supported field types. */
262
+ type FieldType = ColumnType;
263
+ /** Type-specific field configuration. */
264
+ type FieldConfig = ColumnConfig;
265
+
266
+ /** Valid field type values, for runtime enforcement in field-operations. */
267
+ declare const FIELD_TYPES: readonly FieldType[];
268
+ declare function isFieldType(value: unknown): value is FieldType;
269
+ /** Valid select option colors. */
270
+ declare const SELECT_COLORS: readonly SelectColor[];
271
+ declare function isSelectColor(value: unknown): value is SelectColor;
272
+ /**
273
+ * Pick a deterministic color for a new option from its name, so
274
+ * typeahead-created tags get stable, pleasant colors without a picker.
275
+ */
276
+ declare function autoColor(name: string): SelectColor;
277
+ declare const isNodeStoreFieldType: typeof isNodeStoreColumnType;
278
+ declare const isComputedFieldType: typeof isComputedColumnType;
279
+ declare const isAutoFieldType: typeof isAutoColumnType;
280
+ declare const isYDocFieldType: typeof isYDocColumnType;
281
+ /** A DatabaseField node narrowed with typed properties access. */
282
+ interface FieldNode {
283
+ id: string;
284
+ database: string;
285
+ name: string;
286
+ type: FieldType;
287
+ config: FieldConfig;
288
+ sortKey: string;
289
+ width?: number;
290
+ isTitle?: boolean;
291
+ hidden?: boolean;
292
+ }
293
+ /** A DatabaseSelectOption node narrowed for picker use. */
294
+ interface SelectOptionNode {
295
+ id: string;
296
+ field: string;
297
+ database: string;
298
+ name: string;
299
+ color?: SelectColor;
300
+ sortKey: string;
301
+ }
302
+ /** Extract a FieldNode from a raw node's properties. */
303
+ declare function toFieldNode(node: {
304
+ id: string;
305
+ properties: Record<string, unknown>;
306
+ }): FieldNode;
307
+ /** Extract a SelectOptionNode from a raw node's properties. */
308
+ declare function toSelectOptionNode(node: {
309
+ id: string;
310
+ properties: Record<string, unknown>;
311
+ }): SelectOptionNode;
312
+
313
+ /**
314
+ * Row-height density tiers for the database grid (Airtable parity).
315
+ *
316
+ * A view persists a named tier; the grid resolves it to a pixel height that
317
+ * `GridSurface` uses for virtualization and row layout. Pure + tiny so the
318
+ * toolbar control, the schema default, and the grid all agree on one mapping.
319
+ */
320
+ /** Named row-height tiers, densest first. */
321
+ type RowHeight = 'short' | 'medium' | 'tall' | 'extraTall';
322
+ /** Pixel height per tier (approximating Airtable's Short/Medium/Tall/Extra). */
323
+ declare const ROW_HEIGHT_PX: Record<RowHeight, number>;
324
+ /** Tier order for cycling / menu rendering. */
325
+ declare const ROW_HEIGHTS: RowHeight[];
326
+ /** Human label for a tier. */
327
+ declare function rowHeightLabel(height: RowHeight): string;
328
+ /** The default tier (densest, matching the prior fixed look). */
329
+ declare const DEFAULT_ROW_HEIGHT: RowHeight;
330
+ /**
331
+ * Resolve a persisted value (possibly undefined or an unknown string) to a
332
+ * pixel height, falling back to the default tier.
333
+ */
334
+ declare function resolveRowHeightPx(height: string | null | undefined): number;
335
+ /** Narrow an arbitrary string to a `RowHeight`, or the default. */
336
+ declare function asRowHeight(height: string | null | undefined): RowHeight;
337
+
338
+ /**
339
+ * Summary engine for the database grid's footer bar (Airtable parity).
340
+ *
341
+ * Pure, store-agnostic per-column aggregations: given the rows currently in
342
+ * view, a column, and a chosen {@link SummaryFunction}, compute one footer
343
+ * value. Type-aware — `SUMMARY_FUNCTIONS_BY_TYPE` lists which functions a
344
+ * given column type may offer. Numbers add sum/average/min/max/range/median;
345
+ * checkboxes add checked/unchecked; dates add earliest/latest; every type
346
+ * offers the count family (filled/empty/unique and their percentages).
347
+ *
348
+ * Kept deliberately free of React/Y.Doc/NodeStore so it is trivially unit
349
+ * tested and shared by `GridSummaryBar` and any future query-result footer.
350
+ */
351
+
352
+ /** A row reduced to the cells the summary bar reads. */
353
+ interface SummaryRow {
354
+ cells: Record<string, unknown>;
355
+ }
356
+ /** The minimum a summary needs to know about a column. */
357
+ interface SummaryColumnLike {
358
+ id: string;
359
+ type: ColumnType;
360
+ }
361
+ /** A footer aggregation. `none` renders nothing. */
362
+ type SummaryFunction = 'none' | 'filled' | 'empty' | 'percentFilled' | 'percentEmpty' | 'unique' | 'sum' | 'average' | 'min' | 'max' | 'range' | 'median' | 'checked' | 'unchecked' | 'percentChecked' | 'percentUnchecked' | 'earliest' | 'latest';
363
+ /** The result of computing one column summary. */
364
+ interface ColumnSummaryResult {
365
+ fn: SummaryFunction;
366
+ /** Numeric value where meaningful (percentages are 0–100); else null. */
367
+ value: number | null;
368
+ /** Formatted display string (`''` for `none`, `'—'` for empty/N-A). */
369
+ display: string;
370
+ }
371
+ /** Which summary functions each column type offers, in menu order. */
372
+ declare const SUMMARY_FUNCTIONS_BY_TYPE: Record<ColumnType, SummaryFunction[]>;
373
+ /** Human label for a summary function (for menus and footer captions). */
374
+ declare function summaryFunctionLabel(fn: SummaryFunction): string;
375
+ /** A cell counts as filled unless it is null/undefined, '', or an empty array. */
376
+ declare function isFilledValue(value: unknown): boolean;
377
+ /**
378
+ * Compute a single column summary over the in-view rows.
379
+ *
380
+ * @param rows - rows currently displayed (already filtered)
381
+ * @param column - the column being summarised
382
+ * @param fn - the chosen aggregation
383
+ */
384
+ declare function computeColumnSummary(rows: readonly SummaryRow[], column: SummaryColumnLike, fn: SummaryFunction): ColumnSummaryResult;
385
+
386
+ /**
387
+ * View type definitions for database views.
388
+ *
389
+ * Views are stored in the database's Y.Doc as a Y.Map of view configs,
390
+ * enabling collaborative view editing and real-time sync.
391
+ */
392
+
393
+ /**
394
+ * Available view types.
395
+ */
396
+ type ViewType = 'table' | 'board' | 'list' | 'gallery' | 'calendar' | 'timeline' | 'form';
397
+ /**
398
+ * View configuration stored in the database's Y.Doc.
399
+ */
400
+ interface ViewConfig {
401
+ /** Unique view ID */
402
+ id: string;
403
+ /** Display name */
404
+ name: string;
405
+ /** View type */
406
+ type: ViewType;
407
+ /** Column visibility and order */
408
+ visibleColumns: string[];
409
+ /** Per-column widths (overrides column.width) */
410
+ columnWidths?: Record<string, number>;
411
+ /** Filter configuration */
412
+ filters?: FilterGroup | null;
413
+ /** Sort configuration */
414
+ sorts?: SortConfig[];
415
+ /** Group by column ID */
416
+ groupBy?: string | null;
417
+ /** Group sort direction */
418
+ groupSort?: 'asc' | 'desc';
419
+ /** Collapsed group IDs */
420
+ collapsedGroups?: string[];
421
+ /** Row-height density tier */
422
+ rowHeight?: RowHeight;
423
+ /** Per-column footer summary functions: columnId -> SummaryFunction */
424
+ columnSummaries?: Record<string, SummaryFunction>;
425
+ /** Cover image column ID */
426
+ coverColumn?: string;
427
+ /** Card size */
428
+ cardSize?: 'small' | 'medium' | 'large';
429
+ /** Start date column ID */
430
+ dateColumn?: string;
431
+ /** End date column ID */
432
+ endDateColumn?: string;
433
+ }
434
+ /**
435
+ * A group of filter conditions combined with AND/OR.
436
+ */
437
+ interface FilterGroup {
438
+ /** Logical operator */
439
+ operator: 'and' | 'or';
440
+ /** Conditions or nested groups */
441
+ conditions: Array<FilterCondition | FilterGroup>;
442
+ }
443
+ /**
444
+ * A single filter condition.
445
+ */
446
+ interface FilterCondition {
447
+ /** Column ID to filter on */
448
+ columnId: string;
449
+ /** Filter operator */
450
+ operator: FilterOperator;
451
+ /** Filter value (type depends on column type and operator) */
452
+ value: unknown;
453
+ }
454
+ /**
455
+ * Available filter operators.
456
+ */
457
+ type FilterOperator = 'equals' | 'notEquals' | 'contains' | 'notContains' | 'startsWith' | 'endsWith' | 'isEmpty' | 'isNotEmpty' | 'greaterThan' | 'lessThan' | 'greaterOrEqual' | 'lessOrEqual' | 'before' | 'after' | 'between' | 'hasAny' | 'hasAll' | 'hasNone';
458
+ /**
459
+ * Sort configuration for a column.
460
+ */
461
+ interface SortConfig {
462
+ /** Column ID to sort by */
463
+ columnId: string;
464
+ /** Sort direction */
465
+ direction: 'asc' | 'desc';
466
+ }
467
+ /**
468
+ * Check if a filter item is a group (vs a condition).
469
+ */
470
+ declare function isFilterGroup(item: FilterCondition | FilterGroup): item is FilterGroup;
471
+ /**
472
+ * Check if a filter item is a condition (vs a group).
473
+ */
474
+ declare function isFilterCondition(item: FilterCondition | FilterGroup): item is FilterCondition;
475
+ /**
476
+ * Check if a view type supports grouping.
477
+ */
478
+ declare function supportsGrouping(type: ViewType): boolean;
479
+ /**
480
+ * Check if a view type requires a date column.
481
+ */
482
+ declare function requiresDateColumn(type: ViewType): boolean;
483
+
484
+ /**
485
+ * Form view types and validation (exploration 0278).
486
+ *
487
+ * A form is a `DatabaseView` of type `'form'`: the view node carries the
488
+ * question list, per-question show-if rules, and submission settings; every
489
+ * accepted submission becomes a new `DatabaseRow`. The validation core here
490
+ * is deliberately UI-free so the same code runs in the in-app fill view and
491
+ * at drain time when the owner's client materializes public submissions
492
+ * (which may arrive long after the fields changed).
493
+ *
494
+ * Show-if rules reuse the `FilterCondition` grammar and the filter engine:
495
+ * a rule is evaluated by running the in-progress answers as a single
496
+ * pseudo-row through `filterRows`, so form logic and view filters can never
497
+ * drift apart in operator semantics.
498
+ */
499
+
500
+ /** One question on the form; omitted fields are not asked. */
501
+ interface FormQuestion {
502
+ /** DatabaseField node id this question writes to. */
503
+ fieldId: string;
504
+ /** Overrides the field name as the question label. */
505
+ label?: string;
506
+ /** Helper text under the label. */
507
+ description?: string;
508
+ /** Require an answer (independent of the field's own required flag). */
509
+ required?: boolean;
510
+ }
511
+ /** Confirmation screen shown after a successful submission. */
512
+ interface FormConfirmation {
513
+ title?: string;
514
+ body?: string;
515
+ }
516
+ /** Form view configuration (the `formConfig` json property). */
517
+ interface FormViewConfig {
518
+ /** Defaults to the database title. */
519
+ title?: string;
520
+ description?: string;
521
+ /** Ordered question list. */
522
+ questions: FormQuestion[];
523
+ confirmation?: FormConfirmation;
524
+ submitLabel?: string;
525
+ }
526
+ /**
527
+ * Per-question show-if rule (the `formRules` json property, keyed by
528
+ * fieldId). The question renders only when the conditions hold against the
529
+ * in-progress answers. Conditions use the same shape/operators as view
530
+ * filters (`FilterCondition`).
531
+ */
532
+ interface FormFieldRule {
533
+ when: FilterCondition[];
534
+ /** 'all' = AND, 'any' = OR. */
535
+ match: 'all' | 'any';
536
+ }
537
+ /**
538
+ * Provenance stamped on rows created by a form submission (the row's
539
+ * `submissionMeta` property). `nonce` is the submitter's idempotency key:
540
+ * public submissions derive the row id from it, so retries and double-drains
541
+ * LWW-upsert instead of duplicating.
542
+ */
543
+ interface FormSubmissionMeta {
544
+ via: 'form';
545
+ /** The form view the submission came through. */
546
+ viewId: string;
547
+ /** Client-generated idempotency key (public submissions). */
548
+ nonce?: string;
549
+ /** When the submission was received (hub time for public, client for in-app). */
550
+ submittedAt: number;
551
+ }
552
+ /**
553
+ * A question as published to the hub for the public form page: the
554
+ * `FormQuestion` config plus just enough field shape (type, select options)
555
+ * to render an editor — never the full field node.
556
+ */
557
+ interface PublicFormQuestion extends FormQuestion {
558
+ type: FieldType;
559
+ options?: Array<{
560
+ id: string;
561
+ name: string;
562
+ color?: string;
563
+ }>;
564
+ }
565
+ /**
566
+ * The sanitized snapshot the owner's client publishes when minting or
567
+ * refreshing a public form link. This is the ONLY thing the hub serves to
568
+ * anonymous respondents, so building it here — through the public field
569
+ * gate — is the leak barrier: no DIDs, node ids (beyond fieldIds), row
570
+ * data, or workspace structure.
571
+ */
572
+ interface PublicFormDefinition {
573
+ title?: string;
574
+ description?: string;
575
+ questions: PublicFormQuestion[];
576
+ /** Show-if rules for the published questions only. */
577
+ rules?: Record<string, FormFieldRule>;
578
+ submitLabel?: string;
579
+ confirmation?: FormConfirmation;
580
+ }
581
+ type PublicOption = {
582
+ id: string;
583
+ name: string;
584
+ color?: string;
585
+ };
586
+ /**
587
+ * Build the public snapshot from the form view config and the current
588
+ * fields. Questions whose field is missing or not public-safe are dropped;
589
+ * select options are reduced to id/name/color.
590
+ */
591
+ declare function buildPublicFormDefinition(config: FormViewConfig, rules: Record<string, FormFieldRule> | undefined, columns: ColumnDefinition[], fieldOptions?: Record<string, PublicOption[]>): PublicFormDefinition;
592
+ /**
593
+ * Deterministic row id for a drained public submission: retries and
594
+ * double-drains produce the same id, so the create is an LWW upsert.
595
+ * SHA-256 via WebCrypto (browser and Node ≥19).
596
+ */
597
+ declare function submissionRowId(tokenHash: string, nonce: string): Promise<string>;
598
+ /**
599
+ * Field types an anonymous public respondent may be asked. Everything that
600
+ * leaks workspace data (person/relation), requires identity or blob plumbing
601
+ * (file), or is computed/auto is excluded.
602
+ */
603
+ declare const PUBLIC_SAFE_FORM_FIELD_TYPES: readonly FieldType[];
604
+ /** Where the form is being filled from. */
605
+ type FormAudience = 'workspace' | 'public';
606
+ /**
607
+ * Whether a field type may appear as a form question for the given audience.
608
+ * Workspace forms additionally allow person/relation/file (respondents are
609
+ * authenticated members with blob + graph access); computed and auto fields
610
+ * are never askable.
611
+ */
612
+ declare function isFormFieldTypeAllowed(type: FieldType, audience: FormAudience): boolean;
613
+ /**
614
+ * Evaluate a show-if rule against the in-progress answers. Empty/missing
615
+ * rules always show. A condition referencing a deleted field passes (the
616
+ * filter-engine semantic), so the question stays reachable instead of being
617
+ * permanently hidden behind an unsatisfiable rule.
618
+ */
619
+ declare function isFormQuestionVisible(rule: FormFieldRule | undefined, answers: Record<string, CellValue>, columns: ColumnDefinition[]): boolean;
620
+ /**
621
+ * The questions currently visible: configured, allowed for the audience,
622
+ * backed by a live field, and passing their show-if rule.
623
+ */
624
+ declare function visibleFormQuestions(config: FormViewConfig, rules: Record<string, FormFieldRule> | undefined, answers: Record<string, CellValue>, columns: ColumnDefinition[], audience: FormAudience): FormQuestion[];
625
+ interface FormValidationError {
626
+ fieldId: string;
627
+ /** Machine-readable reason; the UI maps these to copy. */
628
+ reason: 'required' | 'unknown-field' | 'type-not-allowed' | 'bad-option' | 'bad-value';
629
+ }
630
+ interface FormValidationResult {
631
+ ok: boolean;
632
+ errors: FormValidationError[];
633
+ /** Answers restricted to visible questions, ready for `cell_` mapping. */
634
+ cells: Record<string, CellValue>;
635
+ }
636
+ /**
637
+ * Validate a submission against the form config and the *current* fields.
638
+ * Answers for hidden (rule-suppressed) or unconfigured questions are dropped,
639
+ * not errors — respondents may race a config edit. Unknown field ids and
640
+ * disallowed types ARE errors: they signal drift between submission and
641
+ * schema that a human should review (exploration 0278's Rejected state).
642
+ */
643
+ declare function validateFormSubmission(config: FormViewConfig, rules: Record<string, FormFieldRule> | undefined, answers: Record<string, CellValue>, columns: ColumnDefinition[], audience: FormAudience): FormValidationResult;
644
+
645
+ export { isFilterCondition as $, toSelectOptionNode as A, type ColumnType as B, type CellValue as C, type DateRange as D, type ColumnDefinition as E, type FileRef as F, type ColumnConfig as G, type EmptyConfig as H, type SelectColumnConfig as I, type SelectOption as J, type SelectColor as K, type RollupAggregation as L, isNodeStoreColumnType as M, type NumberColumnConfig as N, isComputedColumnType as O, isAutoColumnType as P, isYDocColumnType as Q, type RelationColumnConfig as R, type SelectOptionNode as S, type TextColumnConfig as T, type ViewConfig as U, type ViewType as V, type FilterGroup as W, type FilterCondition as X, type FilterOperator as Y, type SortConfig as Z, isFilterGroup as _, CELL_PREFIX as a, supportsGrouping as a0, requiresDateColumn as a1, type FormQuestion as a2, type FormConfirmation as a3, type FormViewConfig as a4, type FormFieldRule as a5, type FormAudience as a6, type FormSubmissionMeta as a7, type FormValidationError as a8, type FormValidationResult as a9, type PublicFormQuestion as aa, type PublicFormDefinition as ab, PUBLIC_SAFE_FORM_FIELD_TYPES as ac, isFormFieldTypeAllowed as ad, isFormQuestionVisible as ae, visibleFormQuestions as af, validateFormSubmission as ag, buildPublicFormDefinition as ah, submissionRowId as ai, SUMMARY_FUNCTIONS_BY_TYPE as aj, summaryFunctionLabel as ak, isFilledValue as al, computeColumnSummary as am, type SummaryFunction as an, type SummaryRow as ao, type SummaryColumnLike as ap, type ColumnSummaryResult as aq, ROW_HEIGHT_PX as ar, ROW_HEIGHTS as as, DEFAULT_ROW_HEIGHT as at, rowHeightLabel as au, resolveRowHeightPx as av, asRowHeight as aw, type RowHeight as ax, columnIdFromKey as b, cellKey as c, isDateRange as d, isFileRef as e, fromCellProperties as f, isCellValue as g, type FieldType as h, isCellKey as i, type FieldConfig as j, type FieldNode as k, type RollupColumnConfig as l, type FormulaColumnConfig as m, type DateColumnConfig as n, type FileColumnConfig as o, FIELD_TYPES as p, SELECT_COLORS as q, isFieldType as r, isSelectColor as s, toCellProperties as t, autoColor as u, isNodeStoreFieldType as v, isComputedFieldType as w, isAutoFieldType as x, isYDocFieldType as y, toFieldNode as z };