grist-widget-sdk 0.4.0 → 0.5.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +79 -208
  3. package/dist/app.cjs +183 -65
  4. package/dist/app.cjs.map +1 -1
  5. package/dist/app.d.cts +5 -100
  6. package/dist/app.d.ts +5 -100
  7. package/dist/app.js +4 -2
  8. package/dist/{chunk-P6VTEKWO.js → chunk-LSALXIGM.js} +82 -10
  9. package/dist/chunk-LSALXIGM.js.map +1 -0
  10. package/dist/{chunk-O7NY46QY.js → chunk-MAPAWVQC.js} +88 -20
  11. package/dist/chunk-MAPAWVQC.js.map +1 -0
  12. package/dist/{chunk-B5ZZNLVS.js → chunk-OXA2VITC.js} +5 -1
  13. package/dist/chunk-OXA2VITC.js.map +1 -0
  14. package/dist/{use-grist-BK0eke9K.d.cts → define-widget-B1FIvI-h.d.cts} +179 -104
  15. package/dist/{use-grist-C5DMZ3wU.d.ts → define-widget-B9gEtJP0.d.ts} +179 -104
  16. package/dist/emulator/testing.cjs +2390 -2243
  17. package/dist/emulator/testing.cjs.map +1 -1
  18. package/dist/emulator/testing.d.cts +27 -9
  19. package/dist/emulator/testing.d.ts +27 -9
  20. package/dist/emulator/testing.js +74 -55
  21. package/dist/emulator/testing.js.map +1 -1
  22. package/dist/emulator.cjs +4 -0
  23. package/dist/emulator.cjs.map +1 -1
  24. package/dist/emulator.d.cts +3 -3
  25. package/dist/emulator.d.ts +3 -3
  26. package/dist/emulator.js +1 -1
  27. package/dist/index.cjs +133 -67
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +2 -2
  30. package/dist/index.d.ts +2 -2
  31. package/dist/index.js +5 -1
  32. package/dist/mount-grist-widget-CkB-3iG_.d.ts +137 -0
  33. package/dist/mount-grist-widget-Dtelqao_.d.cts +137 -0
  34. package/package.json +1 -1
  35. package/dist/chunk-B5ZZNLVS.js.map +0 -1
  36. package/dist/chunk-O7NY46QY.js.map +0 -1
  37. package/dist/chunk-P6VTEKWO.js.map +0 -1
@@ -1,100 +1,5 @@
1
1
  import { e as GristReplicaColumn, f as GristRowRecord, i as GristReadyOptions, j as GristFetchSelectedOptions, k as GristTableOperations, o as GristApplyUserActionsOptions, c as GristCursorPos, b as GristWidgetColumnMap } from './types-DBStn0Dk.cjs';
2
2
 
3
- type ValidateColumnMappingsResult = {
4
- ok: boolean;
5
- /**
6
- * `true` while the host has not yet reported column mappings (e.g. between
7
- * `grist.ready()` resolving and the first `onRecord` / `onRecords` callback,
8
- * or before `sectionApi.mappings()` returns). Callers should not gate UI or
9
- * emit mapping alerts while pending.
10
- */
11
- pending?: boolean;
12
- /** Logical names of required columns the user has not yet mapped. */
13
- missing: string[];
14
- /** Logical names of `allowMultiple` columns mapped to an empty list. */
15
- emptyMultiples: string[];
16
- };
17
-
18
- type GristNoiseColumnsOptions = {
19
- excludeId?: boolean;
20
- excludeGristHelpers?: boolean;
21
- excludeLoggingColumns?: boolean;
22
- /**
23
- * When `true`, treat Grist's `manualSort` column as noise (omit from filtered replicas).
24
- * Default `false` — `manualSort` is kept unless you opt in.
25
- */
26
- excludeManualSort?: boolean;
27
- };
28
-
29
- type GristFetchedAttachmentBlob = {
30
- blob: Blob;
31
- contentType: string;
32
- };
33
- /**
34
- * All attachment ids found in a Grist attachment cell value (ordered, deduped).
35
- * Attachment columns are usually an **array** of file refs; a single id or one JSON object is also supported.
36
- */
37
- declare function extractGristAttachmentIdsFromCell(value: unknown): string[];
38
- /** First attachment id in the cell, if any (see {@link extractGristAttachmentIdsFromCell}). */
39
- declare function extractGristAttachmentId(value: unknown): string | undefined;
40
-
41
- type GristEncodedAttachmentList = ["L", ...(number | string)[]];
42
- type GristAttachmentUploadResult = {
43
- /** All attachment ids returned by `POST /attachments`. */
44
- ids: string[];
45
- /** First id in `ids` (convenience for single-file uploads). */
46
- firstId: string;
47
- };
48
- /** Wire value for an Attachments cell (Grist list encoding). */
49
- declare function gristAttachmentCellValues(attachmentIds: (string | number)[]): GristEncodedAttachmentList;
50
- /** Wire value for a single-file Attachments cell. */
51
- declare function gristAttachmentCellValue(attachmentId: string | number): GristEncodedAttachmentList;
52
- /**
53
- * Merge new attachment ids into an existing Attachments cell value (deduped, order preserved).
54
- */
55
- declare function mergeGristAttachmentCellValue(existingCell: unknown, newIds: (string | number)[]): GristEncodedAttachmentList;
56
-
57
- /** Column-oriented table payload (`fetchTable`, `fetchSelectedTable`): `{ id: [...], Name: [...] }`. */
58
- type GristColumnarTable = Record<string, unknown[]>;
59
- type GristTableRowsOrder = "none" | "id" | "grist";
60
- type FetchGristTableRowsOptions = {
61
- /** Column metadata, to decode each cell (except `id`) with `decodeGristValue`. */
62
- columns?: Record<string, GristReplicaColumn>;
63
- /**
64
- * Row order:
65
- * - `grist` (default): `manualSort` ascending when present, then `id`.
66
- * - `id`: numeric `id` ascending.
67
- * - `none`: as `fetchTable` returned them.
68
- */
69
- order?: GristTableRowsOrder;
70
- };
71
- /** Turn `fetchTable` output into row objects (order follows the `id` column). */
72
- declare function tableDataToRows<T = Record<string, unknown>>(data: GristColumnarTable): GristRowRecord<T>[];
73
-
74
- /**
75
- * Grist `docApi.applyUserActions` payloads are arrays of user-action tuples.
76
- * @see https://support.getgrist.com/code/interfaces/grist_plugin_api.GristDocAPI/#applyuseractions
77
- */
78
- type GristUserActionTuple = unknown[];
79
- /** `["UpdateRecord", tableId, rowId, { col: value, ... }]` */
80
- declare function gristUpdateRecordAction(tableId: string, rowId: number, patch: Record<string, unknown>): GristUserActionTuple;
81
- /** `["AddRecord", tableId, null, { col: value, ... }]` */
82
- declare function gristAddRecordAction(tableId: string, values: Record<string, unknown>): GristUserActionTuple;
83
- /** `["RemoveRecord", tableId, rowId]` */
84
- declare function gristRemoveRecordAction(tableId: string, rowId: number): GristUserActionTuple;
85
- /** `["AddColumn", tableId, colId, colInfo]` — `colInfo` shape depends on Grist version / column type. */
86
- declare function gristAddColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple;
87
- /** `["AddVisibleColumn", tableId, colId, colInfo]` — like `AddColumn` but also adds the column to the current view section. */
88
- declare function gristAddVisibleColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple;
89
- /** `["RenameColumn", tableId, colId, newColId]` */
90
- declare function gristRenameColumnAction(tableId: string, colId: string, newColId: string): GristUserActionTuple;
91
- /** `["ModifyColumn", tableId, colId, colInfo]` */
92
- declare function gristModifyColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple;
93
- /** `["RemoveColumn", tableId, colId]` */
94
- declare function gristRemoveColumnAction(tableId: string, colId: string): GristUserActionTuple;
95
- /** `["AddTable", tableId, tableInfo]` */
96
- declare function gristAddTableAction(tableId: string, tableInfo: Record<string, unknown>): GristUserActionTuple;
97
-
98
3
  /**
99
4
  * Typed reads, with read-only access: each mapped cell checked against the
100
5
  * type its column declares in `GRIST_OPTIONS.columns`.
@@ -125,12 +30,20 @@ type Check = {
125
30
  ok: false;
126
31
  message: string;
127
32
  };
33
+ declare function checkGristCell(type: string | undefined, value: unknown): Check;
128
34
  /**
129
- * `value` as its declared `type` promises ("Numeric", "Ref:Tasks",
130
- * "Text,Ref"…): a match of any listed type, or a failure that says why.
131
- * Without a type (or "Any"), the value as it is.
35
+ * A value as Grist stores it in a column of the declared `type`: the
36
+ * reverse of what typed reads give. Lists become `["L", ...]` (a RefList's
37
+ * `Reference` items become their row ids), a `Date` for a Date or DateTime
38
+ * column becomes seconds since the epoch, a `Reference` for a Ref column its
39
+ * row id. A list already encoded (`["L", ...]`, as the attachment helpers
40
+ * return it) and every other value pass through, so it is safe to call twice.
41
+ *
42
+ * Grist's plugin API writes values as given: a bare array written to a
43
+ * ChoiceList or RefList column is stored as an error cell. Checked in
44
+ * Grist 1.7.22.
132
45
  */
133
- declare function checkGristCell(type: string | undefined, value: unknown): Check;
46
+ declare function encodeGristCell(type: string | undefined, value: unknown): unknown;
134
47
  /** Minimal Standard Schema (https://standardschema.dev): zod 4, valibot, arktype implement it. */
135
48
  type StandardSchema<T = unknown> = {
136
49
  readonly "~standard": {
@@ -154,9 +67,9 @@ type CellOf<T extends string> = T extends `${infer A},${infer Rest}` ? CellOf<A>
154
67
  type ColumnValue<C> = C extends {
155
68
  type: infer T extends string;
156
69
  allowMultiple: true;
157
- } ? CellOf<T>[] : C extends {
70
+ } ? (CellOf<T> | null)[] : C extends {
158
71
  type: infer T extends string;
159
- } ? CellOf<T> : C extends {
72
+ } ? CellOf<T> | null : C extends {
160
73
  allowMultiple: true;
161
74
  } ? unknown[] : unknown;
162
75
  type RequiredName<C> = C extends {
@@ -196,6 +109,101 @@ type GristMappedRow<O extends {
196
109
  [N in OptionalName<C>]?: ColumnValue<ByName<C, N>>;
197
110
  } : Record<string, unknown>;
198
111
 
112
+ type ValidateColumnMappingsResult = {
113
+ ok: boolean;
114
+ /**
115
+ * `true` while the host has not yet reported column mappings (e.g. between
116
+ * `grist.ready()` resolving and the first `onRecord` / `onRecords` callback,
117
+ * or before `sectionApi.mappings()` returns). Callers should not gate UI or
118
+ * emit mapping alerts while pending.
119
+ */
120
+ pending?: boolean;
121
+ /** Logical names of required columns the user has not yet mapped. */
122
+ missing: string[];
123
+ /** Logical names of `allowMultiple` columns mapped to an empty list. */
124
+ emptyMultiples: string[];
125
+ };
126
+
127
+ type GristNoiseColumnsOptions = {
128
+ excludeId?: boolean;
129
+ excludeGristHelpers?: boolean;
130
+ excludeLoggingColumns?: boolean;
131
+ /**
132
+ * When `true`, treat Grist's `manualSort` column as noise (omit from filtered replicas).
133
+ * Default `false` — `manualSort` is kept unless you opt in.
134
+ */
135
+ excludeManualSort?: boolean;
136
+ };
137
+
138
+ type GristFetchedAttachmentBlob = {
139
+ blob: Blob;
140
+ contentType: string;
141
+ };
142
+ /**
143
+ * All attachment ids found in a Grist attachment cell value (ordered, deduped).
144
+ * Attachment columns are usually an **array** of file refs; a single id or one JSON object is also supported.
145
+ */
146
+ declare function extractGristAttachmentIdsFromCell(value: unknown): string[];
147
+ /** First attachment id in the cell, if any (see {@link extractGristAttachmentIdsFromCell}). */
148
+ declare function extractGristAttachmentId(value: unknown): string | undefined;
149
+
150
+ type GristEncodedAttachmentList = ["L", ...(number | string)[]];
151
+ type GristAttachmentUploadResult = {
152
+ /** All attachment ids returned by `POST /attachments`. */
153
+ ids: string[];
154
+ /** First id in `ids` (convenience for single-file uploads). */
155
+ firstId: string;
156
+ };
157
+ /** Wire value for an Attachments cell (Grist list encoding). */
158
+ declare function gristAttachmentCellValues(attachmentIds: (string | number)[]): GristEncodedAttachmentList;
159
+ /** Wire value for a single-file Attachments cell. */
160
+ declare function gristAttachmentCellValue(attachmentId: string | number): GristEncodedAttachmentList;
161
+ /**
162
+ * Merge new attachment ids into an existing Attachments cell value (deduped, order preserved).
163
+ */
164
+ declare function mergeGristAttachmentCellValue(existingCell: unknown, newIds: (string | number)[]): GristEncodedAttachmentList;
165
+
166
+ /** Column-oriented table payload (`fetchTable`, `fetchSelectedTable`): `{ id: [...], Name: [...] }`. */
167
+ type GristColumnarTable = Record<string, unknown[]>;
168
+ type GristTableRowsOrder = "none" | "id" | "grist";
169
+ type FetchGristTableRowsOptions = {
170
+ /** Column metadata, to decode each cell (except `id`) with `decodeGristValue`. */
171
+ columns?: Record<string, GristReplicaColumn>;
172
+ /**
173
+ * Row order:
174
+ * - `grist` (default): `manualSort` ascending when present, then `id`.
175
+ * - `id`: numeric `id` ascending.
176
+ * - `none`: as `fetchTable` returned them.
177
+ */
178
+ order?: GristTableRowsOrder;
179
+ };
180
+ /** Turn `fetchTable` output into row objects (order follows the `id` column). */
181
+ declare function tableDataToRows<T = Record<string, unknown>>(data: GristColumnarTable): GristRowRecord<T>[];
182
+
183
+ /**
184
+ * Grist `docApi.applyUserActions` payloads are arrays of user-action tuples.
185
+ * @see https://support.getgrist.com/code/interfaces/grist_plugin_api.GristDocAPI/#applyuseractions
186
+ */
187
+ type GristUserActionTuple = unknown[];
188
+ /** `["UpdateRecord", tableId, rowId, { col: value, ... }]` */
189
+ declare function gristUpdateRecordAction(tableId: string, rowId: number, patch: Record<string, unknown>): GristUserActionTuple;
190
+ /** `["AddRecord", tableId, null, { col: value, ... }]` */
191
+ declare function gristAddRecordAction(tableId: string, values: Record<string, unknown>): GristUserActionTuple;
192
+ /** `["RemoveRecord", tableId, rowId]` */
193
+ declare function gristRemoveRecordAction(tableId: string, rowId: number): GristUserActionTuple;
194
+ /** `["AddColumn", tableId, colId, colInfo]` — `colInfo` shape depends on Grist version / column type. */
195
+ declare function gristAddColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple;
196
+ /** `["AddVisibleColumn", tableId, colId, colInfo]` — like `AddColumn` but also adds the column to the current view section. */
197
+ declare function gristAddVisibleColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple;
198
+ /** `["RenameColumn", tableId, colId, newColId]` */
199
+ declare function gristRenameColumnAction(tableId: string, colId: string, newColId: string): GristUserActionTuple;
200
+ /** `["ModifyColumn", tableId, colId, colInfo]` */
201
+ declare function gristModifyColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple;
202
+ /** `["RemoveColumn", tableId, colId]` */
203
+ declare function gristRemoveColumnAction(tableId: string, colId: string): GristUserActionTuple;
204
+ /** `["AddTable", tableId, tableInfo]` */
205
+ declare function gristAddTableAction(tableId: string, tableInfo: Record<string, unknown>): GristUserActionTuple;
206
+
199
207
  /**
200
208
  * The alerts `mountGristWidget()` shows above a running widget: what needs the
201
209
  * user's attention in Grist (column mapping, access, a failed write, an
@@ -293,6 +301,8 @@ type GristWidgetStatus = "booting" | "ready" | "unavailable" | "error";
293
301
 
294
302
  /** What the widget asks Grist for: access, columns to map, and the alerts it doesn't want. */
295
303
  type UseGristOptions = GristRuntimeOptions;
304
+ /** A write under declared names: any of the mapped record's fields but its `id`. */
305
+ type GristRecordPatch<TMapped> = Partial<Omit<TMapped, "id">>;
296
306
  /** Derived from the selection: no row, a row, or the add-row placeholder. */
297
307
  type GristWidgetMode = "empty" | "row" | "new-row";
298
308
  /**
@@ -331,8 +341,22 @@ type UseGristResult<TRow = Record<string, unknown>, TMapped = Record<string, unk
331
341
  mode: GristWidgetMode;
332
342
  /** The Grist column id mapped to a declared name, or the name itself. */
333
343
  resolveMappedColumnId: (name: string) => string;
334
- /** A patch under declared names, as real column ids for a write (multi-column mappings are skipped). */
344
+ /**
345
+ * A patch under declared names, as real column ids for a write, each value
346
+ * encoded for its declared type (multi-column mappings are skipped).
347
+ */
335
348
  mapBack: (patch: Partial<TMapped>) => Record<string, unknown>;
349
+ /**
350
+ * Writes a patch under declared names to the selected row (or to `rowId`),
351
+ * encoded for each declared type: `mapBack` + `table.update` in one call.
352
+ * Rejects when no row is selected.
353
+ */
354
+ updateRecord: {
355
+ (patch: GristRecordPatch<TMapped>): Promise<void>;
356
+ (rowId: number, patch: GristRecordPatch<TMapped>): Promise<void>;
357
+ };
358
+ /** Adds a row to the section's table from a patch under declared names; resolves to its row id. */
359
+ addRecord: (patch: GristRecordPatch<TMapped>) => Promise<number>;
336
360
  /** The section's table id. */
337
361
  currentTableId: string | null;
338
362
  /** Any table as rows (full access). */
@@ -354,5 +378,56 @@ declare function useGristOptional<TRow = Record<string, unknown>, TMapped = Reco
354
378
  /** `useGrist()` inside `<GristWidgetProvider>`; throws outside one. */
355
379
  declare function useGristContext<TRow = Record<string, unknown>, TMapped = Record<string, unknown>>(): UseGristResult<TRow, TMapped>;
356
380
 
357
- export { gristRenameColumnAction as A, gristUpdateRecordAction as B, mergeGristAttachmentCellValue as C, tableDataToRows as D, useGrist as E, useGristContext as F, useGristOptional as H, checkGristCell as o, extractGristAttachmentId as p, extractGristAttachmentIdsFromCell as q, gristAddColumnAction as r, gristAddRecordAction as s, gristAddTableAction as t, gristAddVisibleColumnAction as u, gristAttachmentCellValue as v, gristAttachmentCellValues as w, gristModifyColumnAction as x, gristRemoveColumnAction as y, gristRemoveRecordAction as z };
358
- export type { GristNoiseColumnsOptions as G, ListColumnsOptions as L, StandardSchema as S, UseGristOptions as U, ValidateColumnMappingsResult as V, GristAttachmentUploadResult as a, GristCellIssue as b, GristColumnInfo as c, GristColumnarTable as d, GristFetchedAttachmentBlob as e, GristMappedRow as f, GristSdkAlertKind as g, GristTheme as h, GristUserActionTuple as i, GristWidgetActionStatus as j, GristWidgetMode as k, GristWidgetStatus as l, ListTablesOptions as m, UseGristResult as n };
381
+ /**
382
+ * `defineGristWidget()`: a widget's whole declaration in one typed value.
383
+ * Everything else reads it: `widget.useGrist()` types the mapped records and
384
+ * the writes from it, `mountGristWidget()` takes the access level, the
385
+ * columns and the title from it, and the tests map its columns.
386
+ *
387
+ * ```ts
388
+ * export const widget = defineGristWidget({
389
+ * title: "Tasks",
390
+ * requiredAccess: "full",
391
+ * columns: [{ name: "Title", type: "Text" }, { name: "Done", type: "Bool" }],
392
+ * })
393
+ *
394
+ * const w = widget.useGrist() // w.mappedRecord: { id; Title: string | null; Done: boolean | null } | null
395
+ * await w.updateRecord({ Done: true })
396
+ * ```
397
+ */
398
+
399
+ /** What a widget declares: `UseGristOptions`, plus its title and description. */
400
+ type GristWidgetDefinition = UseGristOptions & {
401
+ /** The widget's name: the page's title, and its manifest's. */
402
+ title?: string;
403
+ /** One sentence: the page's description, and its manifest's. */
404
+ description?: string;
405
+ };
406
+ /** The widget's mapped record type, from its declaration. */
407
+ type GristWidgetRow<W> = W extends GristWidget<infer D> ? GristMappedRow<D> : never;
408
+ /** A declared widget. Pass it to `mountGristWidget()` (as `widget`, exported from `src/app/App.tsx`). */
409
+ type GristWidget<D extends GristWidgetDefinition = GristWidgetDefinition> = {
410
+ /** The declaration, as written. */
411
+ readonly definition: D;
412
+ /** What the widget asks Grist for: the declaration without its title and description. */
413
+ readonly options: UseGristOptions;
414
+ readonly metadata: {
415
+ title?: string;
416
+ description?: string;
417
+ };
418
+ /**
419
+ * `useGrist()`, typed from the declaration: `mappedRecord`, `mappedRecords`,
420
+ * `mapBack`, `updateRecord` and `addRecord` use the declared names and the
421
+ * value each declared type gives. A hook: call it in a component.
422
+ */
423
+ useGrist: () => UseGristResult<GristRowRecord, GristMappedRow<D>>;
424
+ };
425
+ /**
426
+ * Declares a widget. The `const` type parameter keeps the column names and
427
+ * types literal, so the mapped record is typed with no annotation, no
428
+ * `as const` and no generics.
429
+ */
430
+ declare function defineGristWidget<const D extends GristWidgetDefinition>(definition: D): GristWidget<D>;
431
+
432
+ export { gristAddVisibleColumnAction as A, gristAttachmentCellValue as B, gristAttachmentCellValues as C, gristModifyColumnAction as D, gristRemoveColumnAction as E, gristRemoveRecordAction as F, gristRenameColumnAction as H, gristUpdateRecordAction as I, mergeGristAttachmentCellValue as J, tableDataToRows as K, useGrist as M, useGristContext as N, useGristOptional as O, checkGristCell as s, defineGristWidget as t, encodeGristCell as u, extractGristAttachmentId as v, extractGristAttachmentIdsFromCell as w, gristAddColumnAction as x, gristAddRecordAction as y, gristAddTableAction as z };
433
+ export type { GristWidget as G, ListColumnsOptions as L, StandardSchema as S, UseGristOptions as U, ValidateColumnMappingsResult as V, GristNoiseColumnsOptions as a, GristAttachmentUploadResult as b, GristCellIssue as c, GristColumnInfo as d, GristColumnarTable as e, GristFetchedAttachmentBlob as f, GristMappedRow as g, GristRecordPatch as h, GristSdkAlertKind as i, GristTheme as j, GristUserActionTuple as k, GristWidgetActionStatus as l, GristWidgetDefinition as m, GristWidgetMode as n, GristWidgetRow as o, GristWidgetStatus as p, ListTablesOptions as q, UseGristResult as r };
@@ -1,100 +1,5 @@
1
1
  import { e as GristReplicaColumn, f as GristRowRecord, i as GristReadyOptions, j as GristFetchSelectedOptions, k as GristTableOperations, o as GristApplyUserActionsOptions, c as GristCursorPos, b as GristWidgetColumnMap } from './types-DBStn0Dk.js';
2
2
 
3
- type ValidateColumnMappingsResult = {
4
- ok: boolean;
5
- /**
6
- * `true` while the host has not yet reported column mappings (e.g. between
7
- * `grist.ready()` resolving and the first `onRecord` / `onRecords` callback,
8
- * or before `sectionApi.mappings()` returns). Callers should not gate UI or
9
- * emit mapping alerts while pending.
10
- */
11
- pending?: boolean;
12
- /** Logical names of required columns the user has not yet mapped. */
13
- missing: string[];
14
- /** Logical names of `allowMultiple` columns mapped to an empty list. */
15
- emptyMultiples: string[];
16
- };
17
-
18
- type GristNoiseColumnsOptions = {
19
- excludeId?: boolean;
20
- excludeGristHelpers?: boolean;
21
- excludeLoggingColumns?: boolean;
22
- /**
23
- * When `true`, treat Grist's `manualSort` column as noise (omit from filtered replicas).
24
- * Default `false` — `manualSort` is kept unless you opt in.
25
- */
26
- excludeManualSort?: boolean;
27
- };
28
-
29
- type GristFetchedAttachmentBlob = {
30
- blob: Blob;
31
- contentType: string;
32
- };
33
- /**
34
- * All attachment ids found in a Grist attachment cell value (ordered, deduped).
35
- * Attachment columns are usually an **array** of file refs; a single id or one JSON object is also supported.
36
- */
37
- declare function extractGristAttachmentIdsFromCell(value: unknown): string[];
38
- /** First attachment id in the cell, if any (see {@link extractGristAttachmentIdsFromCell}). */
39
- declare function extractGristAttachmentId(value: unknown): string | undefined;
40
-
41
- type GristEncodedAttachmentList = ["L", ...(number | string)[]];
42
- type GristAttachmentUploadResult = {
43
- /** All attachment ids returned by `POST /attachments`. */
44
- ids: string[];
45
- /** First id in `ids` (convenience for single-file uploads). */
46
- firstId: string;
47
- };
48
- /** Wire value for an Attachments cell (Grist list encoding). */
49
- declare function gristAttachmentCellValues(attachmentIds: (string | number)[]): GristEncodedAttachmentList;
50
- /** Wire value for a single-file Attachments cell. */
51
- declare function gristAttachmentCellValue(attachmentId: string | number): GristEncodedAttachmentList;
52
- /**
53
- * Merge new attachment ids into an existing Attachments cell value (deduped, order preserved).
54
- */
55
- declare function mergeGristAttachmentCellValue(existingCell: unknown, newIds: (string | number)[]): GristEncodedAttachmentList;
56
-
57
- /** Column-oriented table payload (`fetchTable`, `fetchSelectedTable`): `{ id: [...], Name: [...] }`. */
58
- type GristColumnarTable = Record<string, unknown[]>;
59
- type GristTableRowsOrder = "none" | "id" | "grist";
60
- type FetchGristTableRowsOptions = {
61
- /** Column metadata, to decode each cell (except `id`) with `decodeGristValue`. */
62
- columns?: Record<string, GristReplicaColumn>;
63
- /**
64
- * Row order:
65
- * - `grist` (default): `manualSort` ascending when present, then `id`.
66
- * - `id`: numeric `id` ascending.
67
- * - `none`: as `fetchTable` returned them.
68
- */
69
- order?: GristTableRowsOrder;
70
- };
71
- /** Turn `fetchTable` output into row objects (order follows the `id` column). */
72
- declare function tableDataToRows<T = Record<string, unknown>>(data: GristColumnarTable): GristRowRecord<T>[];
73
-
74
- /**
75
- * Grist `docApi.applyUserActions` payloads are arrays of user-action tuples.
76
- * @see https://support.getgrist.com/code/interfaces/grist_plugin_api.GristDocAPI/#applyuseractions
77
- */
78
- type GristUserActionTuple = unknown[];
79
- /** `["UpdateRecord", tableId, rowId, { col: value, ... }]` */
80
- declare function gristUpdateRecordAction(tableId: string, rowId: number, patch: Record<string, unknown>): GristUserActionTuple;
81
- /** `["AddRecord", tableId, null, { col: value, ... }]` */
82
- declare function gristAddRecordAction(tableId: string, values: Record<string, unknown>): GristUserActionTuple;
83
- /** `["RemoveRecord", tableId, rowId]` */
84
- declare function gristRemoveRecordAction(tableId: string, rowId: number): GristUserActionTuple;
85
- /** `["AddColumn", tableId, colId, colInfo]` — `colInfo` shape depends on Grist version / column type. */
86
- declare function gristAddColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple;
87
- /** `["AddVisibleColumn", tableId, colId, colInfo]` — like `AddColumn` but also adds the column to the current view section. */
88
- declare function gristAddVisibleColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple;
89
- /** `["RenameColumn", tableId, colId, newColId]` */
90
- declare function gristRenameColumnAction(tableId: string, colId: string, newColId: string): GristUserActionTuple;
91
- /** `["ModifyColumn", tableId, colId, colInfo]` */
92
- declare function gristModifyColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple;
93
- /** `["RemoveColumn", tableId, colId]` */
94
- declare function gristRemoveColumnAction(tableId: string, colId: string): GristUserActionTuple;
95
- /** `["AddTable", tableId, tableInfo]` */
96
- declare function gristAddTableAction(tableId: string, tableInfo: Record<string, unknown>): GristUserActionTuple;
97
-
98
3
  /**
99
4
  * Typed reads, with read-only access: each mapped cell checked against the
100
5
  * type its column declares in `GRIST_OPTIONS.columns`.
@@ -125,12 +30,20 @@ type Check = {
125
30
  ok: false;
126
31
  message: string;
127
32
  };
33
+ declare function checkGristCell(type: string | undefined, value: unknown): Check;
128
34
  /**
129
- * `value` as its declared `type` promises ("Numeric", "Ref:Tasks",
130
- * "Text,Ref"…): a match of any listed type, or a failure that says why.
131
- * Without a type (or "Any"), the value as it is.
35
+ * A value as Grist stores it in a column of the declared `type`: the
36
+ * reverse of what typed reads give. Lists become `["L", ...]` (a RefList's
37
+ * `Reference` items become their row ids), a `Date` for a Date or DateTime
38
+ * column becomes seconds since the epoch, a `Reference` for a Ref column its
39
+ * row id. A list already encoded (`["L", ...]`, as the attachment helpers
40
+ * return it) and every other value pass through, so it is safe to call twice.
41
+ *
42
+ * Grist's plugin API writes values as given: a bare array written to a
43
+ * ChoiceList or RefList column is stored as an error cell. Checked in
44
+ * Grist 1.7.22.
132
45
  */
133
- declare function checkGristCell(type: string | undefined, value: unknown): Check;
46
+ declare function encodeGristCell(type: string | undefined, value: unknown): unknown;
134
47
  /** Minimal Standard Schema (https://standardschema.dev): zod 4, valibot, arktype implement it. */
135
48
  type StandardSchema<T = unknown> = {
136
49
  readonly "~standard": {
@@ -154,9 +67,9 @@ type CellOf<T extends string> = T extends `${infer A},${infer Rest}` ? CellOf<A>
154
67
  type ColumnValue<C> = C extends {
155
68
  type: infer T extends string;
156
69
  allowMultiple: true;
157
- } ? CellOf<T>[] : C extends {
70
+ } ? (CellOf<T> | null)[] : C extends {
158
71
  type: infer T extends string;
159
- } ? CellOf<T> : C extends {
72
+ } ? CellOf<T> | null : C extends {
160
73
  allowMultiple: true;
161
74
  } ? unknown[] : unknown;
162
75
  type RequiredName<C> = C extends {
@@ -196,6 +109,101 @@ type GristMappedRow<O extends {
196
109
  [N in OptionalName<C>]?: ColumnValue<ByName<C, N>>;
197
110
  } : Record<string, unknown>;
198
111
 
112
+ type ValidateColumnMappingsResult = {
113
+ ok: boolean;
114
+ /**
115
+ * `true` while the host has not yet reported column mappings (e.g. between
116
+ * `grist.ready()` resolving and the first `onRecord` / `onRecords` callback,
117
+ * or before `sectionApi.mappings()` returns). Callers should not gate UI or
118
+ * emit mapping alerts while pending.
119
+ */
120
+ pending?: boolean;
121
+ /** Logical names of required columns the user has not yet mapped. */
122
+ missing: string[];
123
+ /** Logical names of `allowMultiple` columns mapped to an empty list. */
124
+ emptyMultiples: string[];
125
+ };
126
+
127
+ type GristNoiseColumnsOptions = {
128
+ excludeId?: boolean;
129
+ excludeGristHelpers?: boolean;
130
+ excludeLoggingColumns?: boolean;
131
+ /**
132
+ * When `true`, treat Grist's `manualSort` column as noise (omit from filtered replicas).
133
+ * Default `false` — `manualSort` is kept unless you opt in.
134
+ */
135
+ excludeManualSort?: boolean;
136
+ };
137
+
138
+ type GristFetchedAttachmentBlob = {
139
+ blob: Blob;
140
+ contentType: string;
141
+ };
142
+ /**
143
+ * All attachment ids found in a Grist attachment cell value (ordered, deduped).
144
+ * Attachment columns are usually an **array** of file refs; a single id or one JSON object is also supported.
145
+ */
146
+ declare function extractGristAttachmentIdsFromCell(value: unknown): string[];
147
+ /** First attachment id in the cell, if any (see {@link extractGristAttachmentIdsFromCell}). */
148
+ declare function extractGristAttachmentId(value: unknown): string | undefined;
149
+
150
+ type GristEncodedAttachmentList = ["L", ...(number | string)[]];
151
+ type GristAttachmentUploadResult = {
152
+ /** All attachment ids returned by `POST /attachments`. */
153
+ ids: string[];
154
+ /** First id in `ids` (convenience for single-file uploads). */
155
+ firstId: string;
156
+ };
157
+ /** Wire value for an Attachments cell (Grist list encoding). */
158
+ declare function gristAttachmentCellValues(attachmentIds: (string | number)[]): GristEncodedAttachmentList;
159
+ /** Wire value for a single-file Attachments cell. */
160
+ declare function gristAttachmentCellValue(attachmentId: string | number): GristEncodedAttachmentList;
161
+ /**
162
+ * Merge new attachment ids into an existing Attachments cell value (deduped, order preserved).
163
+ */
164
+ declare function mergeGristAttachmentCellValue(existingCell: unknown, newIds: (string | number)[]): GristEncodedAttachmentList;
165
+
166
+ /** Column-oriented table payload (`fetchTable`, `fetchSelectedTable`): `{ id: [...], Name: [...] }`. */
167
+ type GristColumnarTable = Record<string, unknown[]>;
168
+ type GristTableRowsOrder = "none" | "id" | "grist";
169
+ type FetchGristTableRowsOptions = {
170
+ /** Column metadata, to decode each cell (except `id`) with `decodeGristValue`. */
171
+ columns?: Record<string, GristReplicaColumn>;
172
+ /**
173
+ * Row order:
174
+ * - `grist` (default): `manualSort` ascending when present, then `id`.
175
+ * - `id`: numeric `id` ascending.
176
+ * - `none`: as `fetchTable` returned them.
177
+ */
178
+ order?: GristTableRowsOrder;
179
+ };
180
+ /** Turn `fetchTable` output into row objects (order follows the `id` column). */
181
+ declare function tableDataToRows<T = Record<string, unknown>>(data: GristColumnarTable): GristRowRecord<T>[];
182
+
183
+ /**
184
+ * Grist `docApi.applyUserActions` payloads are arrays of user-action tuples.
185
+ * @see https://support.getgrist.com/code/interfaces/grist_plugin_api.GristDocAPI/#applyuseractions
186
+ */
187
+ type GristUserActionTuple = unknown[];
188
+ /** `["UpdateRecord", tableId, rowId, { col: value, ... }]` */
189
+ declare function gristUpdateRecordAction(tableId: string, rowId: number, patch: Record<string, unknown>): GristUserActionTuple;
190
+ /** `["AddRecord", tableId, null, { col: value, ... }]` */
191
+ declare function gristAddRecordAction(tableId: string, values: Record<string, unknown>): GristUserActionTuple;
192
+ /** `["RemoveRecord", tableId, rowId]` */
193
+ declare function gristRemoveRecordAction(tableId: string, rowId: number): GristUserActionTuple;
194
+ /** `["AddColumn", tableId, colId, colInfo]` — `colInfo` shape depends on Grist version / column type. */
195
+ declare function gristAddColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple;
196
+ /** `["AddVisibleColumn", tableId, colId, colInfo]` — like `AddColumn` but also adds the column to the current view section. */
197
+ declare function gristAddVisibleColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple;
198
+ /** `["RenameColumn", tableId, colId, newColId]` */
199
+ declare function gristRenameColumnAction(tableId: string, colId: string, newColId: string): GristUserActionTuple;
200
+ /** `["ModifyColumn", tableId, colId, colInfo]` */
201
+ declare function gristModifyColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple;
202
+ /** `["RemoveColumn", tableId, colId]` */
203
+ declare function gristRemoveColumnAction(tableId: string, colId: string): GristUserActionTuple;
204
+ /** `["AddTable", tableId, tableInfo]` */
205
+ declare function gristAddTableAction(tableId: string, tableInfo: Record<string, unknown>): GristUserActionTuple;
206
+
199
207
  /**
200
208
  * The alerts `mountGristWidget()` shows above a running widget: what needs the
201
209
  * user's attention in Grist (column mapping, access, a failed write, an
@@ -293,6 +301,8 @@ type GristWidgetStatus = "booting" | "ready" | "unavailable" | "error";
293
301
 
294
302
  /** What the widget asks Grist for: access, columns to map, and the alerts it doesn't want. */
295
303
  type UseGristOptions = GristRuntimeOptions;
304
+ /** A write under declared names: any of the mapped record's fields but its `id`. */
305
+ type GristRecordPatch<TMapped> = Partial<Omit<TMapped, "id">>;
296
306
  /** Derived from the selection: no row, a row, or the add-row placeholder. */
297
307
  type GristWidgetMode = "empty" | "row" | "new-row";
298
308
  /**
@@ -331,8 +341,22 @@ type UseGristResult<TRow = Record<string, unknown>, TMapped = Record<string, unk
331
341
  mode: GristWidgetMode;
332
342
  /** The Grist column id mapped to a declared name, or the name itself. */
333
343
  resolveMappedColumnId: (name: string) => string;
334
- /** A patch under declared names, as real column ids for a write (multi-column mappings are skipped). */
344
+ /**
345
+ * A patch under declared names, as real column ids for a write, each value
346
+ * encoded for its declared type (multi-column mappings are skipped).
347
+ */
335
348
  mapBack: (patch: Partial<TMapped>) => Record<string, unknown>;
349
+ /**
350
+ * Writes a patch under declared names to the selected row (or to `rowId`),
351
+ * encoded for each declared type: `mapBack` + `table.update` in one call.
352
+ * Rejects when no row is selected.
353
+ */
354
+ updateRecord: {
355
+ (patch: GristRecordPatch<TMapped>): Promise<void>;
356
+ (rowId: number, patch: GristRecordPatch<TMapped>): Promise<void>;
357
+ };
358
+ /** Adds a row to the section's table from a patch under declared names; resolves to its row id. */
359
+ addRecord: (patch: GristRecordPatch<TMapped>) => Promise<number>;
336
360
  /** The section's table id. */
337
361
  currentTableId: string | null;
338
362
  /** Any table as rows (full access). */
@@ -354,5 +378,56 @@ declare function useGristOptional<TRow = Record<string, unknown>, TMapped = Reco
354
378
  /** `useGrist()` inside `<GristWidgetProvider>`; throws outside one. */
355
379
  declare function useGristContext<TRow = Record<string, unknown>, TMapped = Record<string, unknown>>(): UseGristResult<TRow, TMapped>;
356
380
 
357
- export { gristRenameColumnAction as A, gristUpdateRecordAction as B, mergeGristAttachmentCellValue as C, tableDataToRows as D, useGrist as E, useGristContext as F, useGristOptional as H, checkGristCell as o, extractGristAttachmentId as p, extractGristAttachmentIdsFromCell as q, gristAddColumnAction as r, gristAddRecordAction as s, gristAddTableAction as t, gristAddVisibleColumnAction as u, gristAttachmentCellValue as v, gristAttachmentCellValues as w, gristModifyColumnAction as x, gristRemoveColumnAction as y, gristRemoveRecordAction as z };
358
- export type { GristNoiseColumnsOptions as G, ListColumnsOptions as L, StandardSchema as S, UseGristOptions as U, ValidateColumnMappingsResult as V, GristAttachmentUploadResult as a, GristCellIssue as b, GristColumnInfo as c, GristColumnarTable as d, GristFetchedAttachmentBlob as e, GristMappedRow as f, GristSdkAlertKind as g, GristTheme as h, GristUserActionTuple as i, GristWidgetActionStatus as j, GristWidgetMode as k, GristWidgetStatus as l, ListTablesOptions as m, UseGristResult as n };
381
+ /**
382
+ * `defineGristWidget()`: a widget's whole declaration in one typed value.
383
+ * Everything else reads it: `widget.useGrist()` types the mapped records and
384
+ * the writes from it, `mountGristWidget()` takes the access level, the
385
+ * columns and the title from it, and the tests map its columns.
386
+ *
387
+ * ```ts
388
+ * export const widget = defineGristWidget({
389
+ * title: "Tasks",
390
+ * requiredAccess: "full",
391
+ * columns: [{ name: "Title", type: "Text" }, { name: "Done", type: "Bool" }],
392
+ * })
393
+ *
394
+ * const w = widget.useGrist() // w.mappedRecord: { id; Title: string | null; Done: boolean | null } | null
395
+ * await w.updateRecord({ Done: true })
396
+ * ```
397
+ */
398
+
399
+ /** What a widget declares: `UseGristOptions`, plus its title and description. */
400
+ type GristWidgetDefinition = UseGristOptions & {
401
+ /** The widget's name: the page's title, and its manifest's. */
402
+ title?: string;
403
+ /** One sentence: the page's description, and its manifest's. */
404
+ description?: string;
405
+ };
406
+ /** The widget's mapped record type, from its declaration. */
407
+ type GristWidgetRow<W> = W extends GristWidget<infer D> ? GristMappedRow<D> : never;
408
+ /** A declared widget. Pass it to `mountGristWidget()` (as `widget`, exported from `src/app/App.tsx`). */
409
+ type GristWidget<D extends GristWidgetDefinition = GristWidgetDefinition> = {
410
+ /** The declaration, as written. */
411
+ readonly definition: D;
412
+ /** What the widget asks Grist for: the declaration without its title and description. */
413
+ readonly options: UseGristOptions;
414
+ readonly metadata: {
415
+ title?: string;
416
+ description?: string;
417
+ };
418
+ /**
419
+ * `useGrist()`, typed from the declaration: `mappedRecord`, `mappedRecords`,
420
+ * `mapBack`, `updateRecord` and `addRecord` use the declared names and the
421
+ * value each declared type gives. A hook: call it in a component.
422
+ */
423
+ useGrist: () => UseGristResult<GristRowRecord, GristMappedRow<D>>;
424
+ };
425
+ /**
426
+ * Declares a widget. The `const` type parameter keeps the column names and
427
+ * types literal, so the mapped record is typed with no annotation, no
428
+ * `as const` and no generics.
429
+ */
430
+ declare function defineGristWidget<const D extends GristWidgetDefinition>(definition: D): GristWidget<D>;
431
+
432
+ export { gristAddVisibleColumnAction as A, gristAttachmentCellValue as B, gristAttachmentCellValues as C, gristModifyColumnAction as D, gristRemoveColumnAction as E, gristRemoveRecordAction as F, gristRenameColumnAction as H, gristUpdateRecordAction as I, mergeGristAttachmentCellValue as J, tableDataToRows as K, useGrist as M, useGristContext as N, useGristOptional as O, checkGristCell as s, defineGristWidget as t, encodeGristCell as u, extractGristAttachmentId as v, extractGristAttachmentIdsFromCell as w, gristAddColumnAction as x, gristAddRecordAction as y, gristAddTableAction as z };
433
+ export type { GristWidget as G, ListColumnsOptions as L, StandardSchema as S, UseGristOptions as U, ValidateColumnMappingsResult as V, GristNoiseColumnsOptions as a, GristAttachmentUploadResult as b, GristCellIssue as c, GristColumnInfo as d, GristColumnarTable as e, GristFetchedAttachmentBlob as f, GristMappedRow as g, GristRecordPatch as h, GristSdkAlertKind as i, GristTheme as j, GristUserActionTuple as k, GristWidgetActionStatus as l, GristWidgetDefinition as m, GristWidgetMode as n, GristWidgetRow as o, GristWidgetStatus as p, ListTablesOptions as q, UseGristResult as r };