grist-widget-sdk 0.3.20 → 0.3.26

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 (38) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/advanced.d.cts +3 -2
  3. package/dist/advanced.d.ts +3 -2
  4. package/dist/app.cjs +5176 -0
  5. package/dist/app.cjs.map +1 -0
  6. package/dist/app.d.cts +147 -0
  7. package/dist/app.d.ts +147 -0
  8. package/dist/app.js +527 -0
  9. package/dist/app.js.map +1 -0
  10. package/dist/chunk-PDGEZ24B.js +2366 -0
  11. package/dist/chunk-PDGEZ24B.js.map +1 -0
  12. package/dist/{create-grist-emulator-_3_JK7Rr.d.ts → create-grist-emulator-BCqp1BF9.d.ts} +2 -2
  13. package/dist/{create-grist-emulator-K-iUKUCZ.d.cts → create-grist-emulator-DBM8OSIR.d.cts} +2 -2
  14. package/dist/emulator/testing.d.cts +2 -2
  15. package/dist/emulator/testing.d.ts +2 -2
  16. package/dist/emulator/transports.d.cts +3 -3
  17. package/dist/emulator/transports.d.ts +3 -3
  18. package/dist/emulator.d.cts +5 -5
  19. package/dist/emulator.d.ts +5 -5
  20. package/dist/index.d.cts +9 -500
  21. package/dist/index.d.ts +9 -500
  22. package/dist/index.js +57 -2291
  23. package/dist/index.js.map +1 -1
  24. package/dist/{inline-A8yaddmp.d.cts → inline-C7DkvuET.d.cts} +1 -1
  25. package/dist/{inline-B7QQeWul.d.ts → inline-DpcT8tV9.d.ts} +1 -1
  26. package/dist/{templates-CB_VwjEU.d.cts → templates-8k2LKoO-.d.cts} +1 -1
  27. package/dist/{templates-pRIXc2RR.d.ts → templates-DIwi_mvI.d.ts} +1 -1
  28. package/dist/{types-YcRfo7oy.d.cts → types-D3JxUCfL.d.cts} +1 -1
  29. package/dist/{types-YcRfo7oy.d.ts → types-D3JxUCfL.d.ts} +1 -1
  30. package/dist/{use-grist-widget-options-from-context-hl-NwOwP.d.ts → use-grist-reads-DUMvJgV7.d.ts} +3 -44
  31. package/dist/{use-grist-widget-options-from-context-Bbd5-f39.d.cts → use-grist-reads-DpjVuI0H.d.cts} +3 -44
  32. package/dist/use-grist-types-DdOLEJXN.d.cts +499 -0
  33. package/dist/use-grist-types-kwxoFeub.d.ts +499 -0
  34. package/dist/use-grist-widget-options-from-context-CGOlQ5Vc.d.cts +43 -0
  35. package/dist/use-grist-widget-options-from-context-CGOlQ5Vc.d.ts +43 -0
  36. package/package.json +15 -2
  37. package/tsconfig/app.json +38 -0
  38. package/tsconfig/node.json +36 -0
@@ -0,0 +1,499 @@
1
+ import { s as GristDocApi, c as GristReplicaColumn, b as GristReplicaDocument, r as GristColumnsToMap, d as GristWidgetColumnMap, G as GristRowRecord, f as GristWidgetInteraction, k as GristTableOperations, p as GristApplyUserActionsOptions, j as GristFetchSelectedOptions, e as GristCursorPos, i as GristReadyOptions } from './types-D3JxUCfL.js';
2
+ import { G as GristWidgetSnapshot, q as GristWidgetStatus, p as GristUserActionTuple, u as UseGristCoreResult, U as UseGristCoreOptions, c as GristCapabilities, g as GristColumnarTable, F as FetchGristTableRowsOptions, S as SafeParseGristTableDataOptions, h as GristSafeParseCellResult, r as ListTablesOptions, L as ListColumnsOptions, e as GristColumnInfo, b as GristAccessToken } from './use-grist-reads-DUMvJgV7.js';
3
+
4
+ type GristFetchedAttachment = {
5
+ base64: string;
6
+ contentType: string;
7
+ };
8
+ type GristFetchedAttachmentBlob = {
9
+ blob: Blob;
10
+ contentType: string;
11
+ };
12
+ /**
13
+ * All attachment ids found in a Grist attachment cell value (ordered, deduped).
14
+ * Attachment columns are usually an **array** of file refs; a single id or one JSON object is also supported.
15
+ */
16
+ declare function extractGristAttachmentIdsFromCell(value: unknown): string[];
17
+ /** First attachment id in the cell, if any (see {@link extractGristAttachmentIdsFromCell}). */
18
+ declare function extractGristAttachmentId(value: unknown): string | undefined;
19
+ /**
20
+ * Signed Grist download URL for a single attachment id (`getAccessToken` + query auth).
21
+ */
22
+ declare function getAttachmentDownloadUrl(docApi: GristDocApi, attachmentId: string, options?: {
23
+ readOnly?: boolean;
24
+ }): Promise<string>;
25
+ /** URL for the `index`’th attachment in a cell value (0-based). */
26
+ declare function getAttachmentDownloadUrlForCell(docApi: GristDocApi, cellValue: unknown, index: number, options?: {
27
+ readOnly?: boolean;
28
+ }): Promise<string | null>;
29
+ /** Token + URL + fetch for one attachment id. */
30
+ declare function fetchAttachmentBase64(docApi: GristDocApi, attachmentId: string, options?: {
31
+ readOnly?: boolean;
32
+ }): Promise<GristFetchedAttachment>;
33
+ /** Download attachment bytes as a `Blob` (preferred for large files). */
34
+ declare function fetchAttachmentBlob(docApi: GristDocApi, attachmentId: string, options?: {
35
+ readOnly?: boolean;
36
+ }): Promise<GristFetchedAttachmentBlob>;
37
+ /** Download the `index`’th attachment in a cell (0-based). */
38
+ declare function fetchAttachmentBase64ForCell(docApi: GristDocApi, cellValue: unknown, index: number, options?: {
39
+ readOnly?: boolean;
40
+ }): Promise<GristFetchedAttachment | null>;
41
+
42
+ type GristEncodedAttachmentList = ["L", ...(number | string)[]];
43
+ type GristAttachmentUploadResult = {
44
+ /** All attachment ids returned by `POST /attachments`. */
45
+ ids: string[];
46
+ /** First id in `ids` (convenience for single-file uploads). */
47
+ firstId: string;
48
+ };
49
+ /**
50
+ * Parse the body of `POST /attachments` (widget or REST).
51
+ * Grist returns a JSON array of attachment ids, e.g. `[42]` or `[101, 102]`.
52
+ */
53
+ declare function parseGristAttachmentUploadResponseIds(raw: string, options?: {
54
+ strict?: boolean;
55
+ }): string[];
56
+ /**
57
+ * First attachment id from an upload response (see {@link parseGristAttachmentUploadResponseIds}).
58
+ */
59
+ declare function parseGristAttachmentUploadResponse(raw: string, options?: {
60
+ strict?: boolean;
61
+ }): string;
62
+ declare function extractAttachmentIdsFromUploadPayload(parsed: unknown, options?: {
63
+ strict?: boolean;
64
+ }): string[];
65
+ /** @deprecated Prefer {@link extractAttachmentIdsFromUploadPayload}. */
66
+ declare function extractFirstAttachmentIdFromUploadPayload(parsed: unknown): string | undefined;
67
+ /** Wire value for an Attachments cell (Grist list encoding). */
68
+ declare function gristAttachmentCellValues(attachmentIds: (string | number)[]): GristEncodedAttachmentList;
69
+ /** Wire value for a single-file Attachments cell. */
70
+ declare function gristAttachmentCellValue(attachmentId: string | number): GristEncodedAttachmentList;
71
+ /**
72
+ * Merge new attachment ids into an existing Attachments cell value (deduped, order preserved).
73
+ */
74
+ declare function mergeGristAttachmentCellValue(existingCell: unknown, newIds: (string | number)[]): GristEncodedAttachmentList;
75
+ /**
76
+ * Upload a file to the current document via `POST /attachments`.
77
+ *
78
+ * Uses `getAccessToken({ readOnly: false })` and `?auth=` (see Grist widget docs).
79
+ * Requires document **full** access in the widget.
80
+ */
81
+ declare function uploadGristAttachment(docApi: GristDocApi, file: Blob | File, options?: {
82
+ fileName?: string;
83
+ }): Promise<GristAttachmentUploadResult>;
84
+
85
+ type ReplicaTableColumnMetaLite = {
86
+ colRef?: number;
87
+ type?: string;
88
+ label?: string;
89
+ parentPos?: number;
90
+ isFormula?: boolean;
91
+ formula?: string;
92
+ description?: string;
93
+ widgetOptions?: Record<string, unknown>;
94
+ displayColRef?: number;
95
+ visibleColRef?: number;
96
+ };
97
+ declare function loadColumnMetadataByTable(docApi: GristDocApi): Promise<Map<string, Map<string, ReplicaTableColumnMetaLite>>>;
98
+ declare function orderedColumnIds(data: Record<string, unknown[]>, metaByCol: Map<string, ReplicaTableColumnMetaLite>): string[];
99
+ /** Build replica column metadata from columnar `fetchTable` + `_grist_Tables_column` maps. */
100
+ declare function buildReplicaColumnsFromColumnar(data: Record<string, unknown[]>, colMeta: Map<string, ReplicaTableColumnMetaLite>): Record<string, GristReplicaColumn>;
101
+ type BuildReplicaDocumentFromDocApiOptions = {
102
+ /** When false (default), skip fetching row data for `tableId` starting with `_grist`. */
103
+ includeSystemTableData?: boolean;
104
+ /**
105
+ * Row bodies: omit (`false`), first N (`samples`), or full decode (`full`).
106
+ * Default `samples`.
107
+ */
108
+ includeRows?: false | "samples" | "full";
109
+ /** Max rows per table when `includeRows` is `samples`. Default 10. */
110
+ sampleRowLimit?: number;
111
+ /** Set on the replica as `source` for host logging / provenance. */
112
+ source?: string;
113
+ };
114
+ /**
115
+ * Build {@link GristReplicaDocument} from a host `docApi`:
116
+ * metadata from `_grist_Tables` / `_grist_Tables_column` when readable, plus optional decoded `rows`.
117
+ *
118
+ * Prefer {@link useGrist}'s `buildReplicaDocumentFromDocApi()` when you already use the primary hook.
119
+ */
120
+ declare function buildReplicaDocumentFromDocApi(docApi: GristDocApi, options?: BuildReplicaDocumentFromDocApiOptions): Promise<GristReplicaDocument>;
121
+
122
+ type ValidateColumnMappingsResult = {
123
+ ok: boolean;
124
+ /**
125
+ * `true` while the host has not yet reported column mappings (e.g. between
126
+ * `grist.ready()` resolving and the first `onRecord` / `onRecords` callback,
127
+ * or before `sectionApi.mappings()` returns). Callers should not gate UI or
128
+ * emit mapping alerts while pending.
129
+ */
130
+ pending?: boolean;
131
+ /** Logical names of required columns the user has not yet mapped. */
132
+ missing: string[];
133
+ /** Logical names of `allowMultiple` columns mapped to an empty list. */
134
+ emptyMultiples: string[];
135
+ };
136
+ /**
137
+ * Verifies that the current Grist mappings cover every required column declared
138
+ * in `grist.ready({ columns })`.
139
+ *
140
+ * Use this before reading `mappedRecord` so you can render a "please configure
141
+ * the widget" prompt instead of a confusing `null`.
142
+ */
143
+ declare function validateColumnMappings(spec: GristColumnsToMap | undefined | null, mappings: GristWidgetColumnMap | null | undefined): ValidateColumnMappingsResult;
144
+
145
+ type GristWidgetMode = "empty" | "row" | "new-row";
146
+ type UseGristSelectionOptions = {
147
+ columns?: GristColumnsToMap;
148
+ subscribeToWidgetOptions?: boolean;
149
+ subscribeToNewRecord?: boolean;
150
+ };
151
+ type UseGristSelectionResult<TRow = Record<string, unknown>, TMapped = Record<string, unknown>> = {
152
+ record: GristRowRecord<TRow> | null;
153
+ records: GristRowRecord<TRow>[] | null;
154
+ mappedRecord: TMapped | null;
155
+ mappings: GristWidgetColumnMap;
156
+ recordsMappings: GristWidgetColumnMap | null;
157
+ columnMappingStatus: ValidateColumnMappingsResult;
158
+ mode: GristWidgetMode;
159
+ resolveMappedColumnId: (logicalFieldName: string) => string;
160
+ mapBack: (patch: Partial<TMapped>) => Record<string, unknown>;
161
+ mapBackSkipped: readonly string[];
162
+ isNewRecord: boolean;
163
+ newRecordMappings: GristWidgetColumnMap | null;
164
+ widgetOptions: Record<string, unknown> | null;
165
+ widgetInteraction: GristWidgetInteraction | null;
166
+ };
167
+
168
+ /**
169
+ * Minimal widget slice for deriving non-fatal SDK callouts. Any
170
+ * {@link import("../use-grist").UseGristResult} satisfies this.
171
+ */
172
+ type GristSdkAlertsWidgetSlice = {
173
+ status: GristWidgetStatus;
174
+ columnMappingStatus: ValidateColumnMappingsResult;
175
+ actionError: string | null;
176
+ /**
177
+ * Last access-insufficient error from a read method (`fetchTable`, etc.).
178
+ * When set and matching the access-error pattern, an "access-insufficient"
179
+ * alert is emitted — same as for `actionError`.
180
+ */
181
+ readError?: string | null;
182
+ /**
183
+ * Logical column names that the most recent `mapBack(...)` call could not
184
+ * reverse-map (e.g. `allowMultiple` columns where one logical name resolves
185
+ * to several real columns).
186
+ */
187
+ mapBackSkipped?: readonly string[];
188
+ /** Selection mode from `useGrist()` — used for section-not-linked detection. */
189
+ mode?: GristWidgetMode;
190
+ /** `onOptions` settings from Grist (`InteractionOptions`). */
191
+ widgetInteraction?: GristWidgetInteraction | null;
192
+ /**
193
+ * Alert kinds suppressed via `UseGristOptions.suppressAlerts`.
194
+ * Consumed automatically by `useGristSdkAlertDescriptors`.
195
+ */
196
+ suppressAlerts?: readonly GristSdkAlertKind[];
197
+ /**
198
+ * Whether the widget declared `allowSelectBy: true` — used to distinguish
199
+ * "section-not-linked" (expects incoming link) from "source-not-wired"
200
+ * (expects to drive other sections).
201
+ */
202
+ allowSelectBy?: boolean;
203
+ };
204
+ type GristSdkAlertKind = "column-mapping" | "mapping-pending" | "mapping-unreported" | "link-stale" | "section-not-linked" | "source-not-wired" | "current-table-error" | "action-error" | "access-insufficient" | "map-back-skip";
205
+ /** Classic callout level for host styling: info / warning / error. */
206
+ type GristSdkAlertSeverity = "info" | "warning" | "error";
207
+ type GristSdkAlertDescriptor = {
208
+ id: string;
209
+ kind: GristSdkAlertKind;
210
+ severity: GristSdkAlertSeverity;
211
+ title: string;
212
+ /** Suggested WAI-ARIA role for the host shell. */
213
+ ariaRole: "status" | "alert";
214
+ message: string;
215
+ };
216
+ type GetGristSdkAlertDescriptorsOptions = {
217
+ /**
218
+ * Handshake snapshot from `useGristHandshake()` / the provider manager.
219
+ * Enables link-stale, mapping-unreported, and current-table alerts that are
220
+ * not visible on the widget slice alone.
221
+ */
222
+ snapshot?: GristWidgetSnapshot;
223
+ /** Appended to {@link formatColumnMappingAlertMessage} when a mapping alert is emitted. */
224
+ columnMappingHint?: string;
225
+ /** Appended to the map-back-skip message. */
226
+ mapBackHint?: string;
227
+ /** Override copy for {@link formatMappingPendingAlertMessage}. */
228
+ mappingPendingHint?: string;
229
+ /** Appended to {@link formatSectionNotLinkedAlertMessage}. */
230
+ sectionNotLinkedHint?: string;
231
+ /** Appended to {@link formatSourceNotWiredAlertMessage}. */
232
+ sourceNotWiredHint?: string;
233
+ /** Appended to {@link formatAccessInsufficientMessage}. */
234
+ accessInsufficientHint?: string;
235
+ /**
236
+ * Alert kinds to omit from the output. Useful for widgets that intentionally
237
+ * operate without a link source (`["section-not-linked"]`) or handle certain
238
+ * conditions in their own UI.
239
+ */
240
+ suppressKinds?: readonly GristSdkAlertKind[];
241
+ };
242
+ /** Default alert title for shadcn / toast shells. */
243
+ declare function getGristSdkAlertTitle(kind: GristSdkAlertKind): string;
244
+ /** Suggested severity for host styling (destructive vs muted). */
245
+ declare function getGristSdkAlertSeverity(kind: GristSdkAlertKind): GristSdkAlertSeverity;
246
+ /**
247
+ * Human-readable text for an incomplete column mapping (when
248
+ * `columnMappingStatus.ok` is false).
249
+ */
250
+ declare function formatColumnMappingAlertMessage(status: ValidateColumnMappingsResult, hint?: string): string;
251
+ declare function formatMappingPendingAlertMessage(hint?: string): string;
252
+ declare function formatMappingUnreportedAlertMessage(hint?: string): string;
253
+ declare function formatLinkStaleAlertMessage(snapshot: GristWidgetSnapshot): string;
254
+ /**
255
+ * True when Grist reports this section is not a link target (no selector drives
256
+ * the cursor / `onRecord` stream). Requires `linking` on `widgetInteraction` so
257
+ * older hosts do not trigger false positives.
258
+ */
259
+ declare function isWidgetSectionNotLinked(interaction: GristWidgetInteraction | null | undefined): boolean;
260
+ declare function formatSectionNotLinkedAlertMessage(hint?: string): string;
261
+ declare function formatSourceNotWiredAlertMessage(hint?: string): string;
262
+ /**
263
+ * Non-fatal SDK messages to show while `status === "ready"` (e.g. incomplete
264
+ * mappings, stale link, or `actionError` when not promoted to `status === "error"`).
265
+ * Host apps map each descriptor to their own UI (shadcn Alert, toast, etc.).
266
+ *
267
+ * Pass `options.snapshot` (from `useGristHandshake()` or the provider manager)
268
+ * for FSM-only signals: link stale, mapping unreported, current-table errors.
269
+ */
270
+ declare function getGristSdkAlertDescriptors(widget: GristSdkAlertsWidgetSlice, options?: GetGristSdkAlertDescriptorsOptions): GristSdkAlertDescriptor[];
271
+ /**
272
+ * Heuristic: returns `true` when `actionError` looks like it was caused by
273
+ * insufficient access rights rather than a domain / schema error.
274
+ */
275
+ declare function isAccessInsufficientError(message: string): boolean;
276
+ declare function formatAccessInsufficientMessage(hint?: string): string;
277
+
278
+ type GristWidgetActionStatus = "idle" | "running" | "error";
279
+ type UseGristTableOpsBoundResult = {
280
+ table: GristTableOperations;
281
+ getTable: (tableId: string) => GristTableOperations;
282
+ actionStatus: GristWidgetActionStatus;
283
+ actionError: string | null;
284
+ applyActions: (actions: GristUserActionTuple[], applyOptions?: GristApplyUserActionsOptions) => Promise<unknown>;
285
+ };
286
+
287
+ type GristTheme = "light" | "dark";
288
+ type UseGristThemeResult = {
289
+ theme: GristTheme | null;
290
+ };
291
+
292
+ type UseGristCurrentTableResult = {
293
+ currentTableId: string | null;
294
+ currentTableLoading: boolean;
295
+ refreshCurrentTable: () => Promise<void>;
296
+ tableError: string | null;
297
+ };
298
+
299
+ type GristCoreContextValue = UseGristCoreResult & {
300
+ tableError: string | null;
301
+ };
302
+ type GristStatusSliceValue = GristCoreContextValue & UseGristCurrentTableResult;
303
+
304
+ type ComposeGristWidgetOptions = UseGristCoreOptions & UseGristSelectionOptions & {
305
+ /**
306
+ * Alert kinds that should never be emitted for this widget.
307
+ * E.g. `["section-not-linked"]` for widgets that don't need a link source.
308
+ */
309
+ suppressAlerts?: GristSdkAlertKind[];
310
+ };
311
+
312
+ type UseGristOptions = ComposeGristWidgetOptions;
313
+ /** Return type of `useGristStatus()`: connection state plus current-table info. */
314
+ type UseGristStatusResult = GristStatusSliceValue;
315
+ /** Return type of `useGristWrites()`. */
316
+ type UseGristWritesResult = UseGristTableOpsBoundResult;
317
+ /**
318
+ * The full Grist widget state and API surface.
319
+ *
320
+ * **Method stability:** all function-typed fields (`fetchTable`, `listTables`,
321
+ * `patchWidgetOptions`, `getTable`, etc.) are `useCallback`-wrapped internally
322
+ * and **referentially stable** as long as the underlying `docApi` hasn't changed.
323
+ * You can safely use individual methods in React dependency arrays:
324
+ *
325
+ * ```tsx
326
+ * // GOOD — w.listTables is stable, effect only runs once
327
+ * useEffect(() => { void w.listTables().then(setTables) }, [w.listTables])
328
+ *
329
+ * // BAD — w is a new object whenever any Grist state changes (record, options, …)
330
+ * useEffect(() => { void w.listTables().then(setTables) }, [w])
331
+ * ```
332
+ *
333
+ * The `UseGristResult` object itself is **not** referentially stable — it updates
334
+ * whenever any reactive field changes (e.g. `record`, `widgetOptions`, `mode`).
335
+ * Do **not** use the whole object as a `useEffect` / `useMemo` dependency.
336
+ */
337
+ type UseGristResult<TRow = Record<string, unknown>, TMapped = Record<string, unknown>> = {
338
+ /** @access `"none"` — Connection state. */
339
+ status: GristWidgetStatus;
340
+ /** @access `"none"` — `true` if the page is inside a Grist iframe. */
341
+ isAvailable: boolean | null;
342
+ /** @access `"none"` — Convenience for `status === "ready"`. */
343
+ isReady: boolean;
344
+ /** @access `"none"` — Connection-level error string. */
345
+ error: string | null;
346
+ /**
347
+ * Handshake-derived gates (`canRender`, `canWriteRecords`, …).
348
+ * Prefer `capabilities.canRender` over re-deriving from `columnMappingStatus` alone.
349
+ * @access `"none"`
350
+ */
351
+ capabilities: GristCapabilities;
352
+ /** @access `"none"` — Re-run the handshake from scratch. */
353
+ reload: () => Promise<void>;
354
+ /** @access `"none"` — Resolved Grist theme (`"light" | "dark"`) or `null` before ready. */
355
+ theme: GristTheme | null;
356
+ /** @access `"read table"` — Currently selected row (raw column ids). */
357
+ record: GristRowRecord<TRow> | null;
358
+ /** @access `"read table"` — All visible rows in the section (raw column ids). */
359
+ records: GristRowRecord<TRow>[] | null;
360
+ /** @access `"read table"` — Selected row with logical column names. `null` if required mappings are missing. */
361
+ mappedRecord: TMapped | null;
362
+ /** @access `"read table"` — Logical → real column id map for the single-record stream. */
363
+ mappings: GristWidgetColumnMap;
364
+ /** @access `"read table"` — Logical → real column id map for the multi-record stream. */
365
+ recordsMappings: GristWidgetColumnMap | null;
366
+ /** @access `"read table"` — Required-column gating result. */
367
+ columnMappingStatus: ValidateColumnMappingsResult;
368
+ /** @access `"read table"` — Derived from selection state: `"empty"`, `"row"`, or `"new-row"`. */
369
+ mode: GristWidgetMode;
370
+ /** @access `"read table"` — One-shot column resolution (returns `name` as a fallback). */
371
+ resolveMappedColumnId: (logicalFieldName: string) => string;
372
+ /** @access `"read table"` — Reverse-map a logical-name patch to real column ids for writes. */
373
+ mapBack: (patch: Partial<TMapped>) => Record<string, unknown>;
374
+ /**
375
+ * Logical column names that the most recent `mapBack(...)` call could not
376
+ * reverse-map (typically `allowMultiple` columns).
377
+ *
378
+ * Used by {@link import("./helpers/grist-sdk-alerts").getGristSdkAlertDescriptors}
379
+ * to surface a non-fatal alert.
380
+ * @access `"read table"`
381
+ */
382
+ mapBackSkipped: readonly string[];
383
+ /** @access `"read table"` — `true` when the cursor is on the new-row placeholder. */
384
+ isNewRecord: boolean;
385
+ /** @access `"read table"` — Logical → real map for the new-row placeholder. */
386
+ newRecordMappings: GristWidgetColumnMap | null;
387
+ /** @access `"read table"` — `onOptions` payload from Grist, includes `access_level`. */
388
+ widgetInteraction: GristWidgetInteraction | null;
389
+ /** @access `"read table"` — Current section's table id. */
390
+ currentTableId: string | null;
391
+ /** @access `"read table"` — `true` while the SDK re-reads `selectedTable.getTableId()`. */
392
+ currentTableLoading: boolean;
393
+ /** @access `"read table"` — Force a re-read of the current table id. */
394
+ refreshCurrentTable: () => Promise<void>;
395
+ /**
396
+ * Last access-insufficient error from a read method (`fetchTable`, `listColumns`, …).
397
+ * `null` when no error or after a successful call clears it.
398
+ * Consumed by {@link import("./helpers/grist-sdk-alerts").getGristSdkAlertDescriptors}
399
+ * to surface the "access-insufficient" alert.
400
+ * @access `"none"`
401
+ */
402
+ readError: string | null;
403
+ /** @access `"full"` — Raw columnar payload for any table. Requires full document access. */
404
+ fetchTable: (tableId: string) => Promise<GristColumnarTable>;
405
+ /** @access `"full"` — Row-shaped, decoded, optionally safe-parsed read of any table. Requires full document access. */
406
+ fetchTableRows: {
407
+ <T extends Record<string, unknown> = Record<string, unknown>>(tableId: string, options: FetchGristTableRowsOptions & {
408
+ safeParse: true | Omit<SafeParseGristTableDataOptions, "columns" | "order">;
409
+ }): Promise<GristRowRecord<{
410
+ [K in keyof T]: GristSafeParseCellResult<T[K]> | null;
411
+ }>[]>;
412
+ <T extends Record<string, unknown> = Record<string, unknown>>(tableId: string, options?: FetchGristTableRowsOptions): Promise<GristRowRecord<T>[]>;
413
+ };
414
+ /** @access `"full"` — Fetch a single row by id from any table. Requires full document access. */
415
+ fetchRow: <T = Record<string, unknown>>(tableId: string, rowId: number) => Promise<GristRowRecord<T> | null>;
416
+ /** @access `"read table"` — List table ids (system/hidden tables filtered by default). */
417
+ listTables: (options?: ListTablesOptions) => Promise<string[]>;
418
+ /** @access `"full"` — Column metadata without fetching rows. Requires full document access. */
419
+ listColumns: (tableId: string, options?: ListColumnsOptions) => Promise<GristColumnInfo[]>;
420
+ /** @access `"read table"` — Document name. */
421
+ getDocName: () => Promise<string>;
422
+ /** @access `"full"` — Build a replica document snapshot from the live doc API. Requires full document access. */
423
+ buildReplicaDocumentFromDocApi: (options?: BuildReplicaDocumentFromDocApiOptions) => Promise<GristReplicaDocument>;
424
+ /** @access `"read table"` — Raw columnar read of the current section. */
425
+ fetchSelectedTable: (fetchOptions?: GristFetchSelectedOptions) => Promise<GristColumnarTable>;
426
+ /** @access `"read table"` — Single row from the current section. */
427
+ fetchSelectedRecord: (rowId: number, fetchOptions?: GristFetchSelectedOptions) => Promise<Record<string, unknown>>;
428
+ /** @access `"full"` — CRUD operations on the current table (`create`, `update`, `upsert`, `destroy`). */
429
+ table: GristTableOperations;
430
+ /** @access `"full"` — Memoized CRUD operations for any table by id. */
431
+ getTable: (tableId: string) => GristTableOperations;
432
+ /** @access `"full"` — Last write status (`"idle"`, `"running"`, `"error"`). */
433
+ actionStatus: GristWidgetActionStatus;
434
+ /** @access `"full"` — Last write error message. */
435
+ actionError: string | null;
436
+ /** @access `"full"` — Apply user-action tuples atomically (schema + data mutations). */
437
+ applyActions: (actions: GristUserActionTuple[], applyOptions?: GristApplyUserActionsOptions) => Promise<unknown>;
438
+ /** @access `"read table"` — Current widget options from Grist's `onOptions` stream. */
439
+ widgetOptions: Record<string, unknown> | null;
440
+ /** @access `"read table"` — Fetch the current widget options imperatively. */
441
+ getWidgetOptions: () => Promise<Record<string, unknown> | null>;
442
+ /** @access `"read table"` — Fetch a single widget option by key. */
443
+ getWidgetOption: (key: string) => Promise<unknown>;
444
+ /** @access `"full"` — Set a single widget option. */
445
+ setWidgetOption: (key: string, value: unknown) => Promise<void>;
446
+ /** @access `"full"` — Replace all widget options. */
447
+ setWidgetOptions: (next: Record<string, unknown>) => Promise<void>;
448
+ /** @access `"full"` — Merge a patch into existing widget options. */
449
+ patchWidgetOptions: (patch: Record<string, unknown>) => Promise<void>;
450
+ /** @access `"full"` — Remove all widget options. */
451
+ clearWidgetOptions: () => Promise<void>;
452
+ /** @access `"read table"` — Move the host cursor to a row / field / section. */
453
+ setCursorPosition: (pos: GristCursorPos) => Promise<void>;
454
+ /** @access `"read table"` — Set the widget's linked row selection (requires `allowSelectBy`). */
455
+ setLinkedRowSelection: (rowIds: null | number[]) => Promise<void>;
456
+ /** @access `"full"` — Signed download URL for an attachment. */
457
+ getAttachmentUrl: (attachmentId: string, options?: {
458
+ readOnly?: boolean;
459
+ }) => Promise<string>;
460
+ /** @access `"full"` — Fetch an attachment as a base64-encoded string. */
461
+ fetchAttachmentBase64: (attachmentId: string, options?: {
462
+ readOnly?: boolean;
463
+ }) => Promise<GristFetchedAttachment>;
464
+ /** @access `"full"` — Fetch an attachment as a Blob. */
465
+ fetchAttachmentBlob: (attachmentId: string, options?: {
466
+ readOnly?: boolean;
467
+ }) => Promise<GristFetchedAttachmentBlob>;
468
+ /** @access `"full"` — Obtain a short-lived access token for the Grist REST API. */
469
+ getAccessToken: (options?: {
470
+ readOnly?: boolean;
471
+ }) => Promise<GristAccessToken>;
472
+ /** @access `"full"` — `fetch()` with the doc access token attached. Path is relative to the document API root. */
473
+ fetchWithAuth: (path: string, init?: RequestInit & {
474
+ readOnly?: boolean;
475
+ }) => Promise<Response>;
476
+ /** @access `"full"` — Upload a file via `POST /attachments` (returns attachment ids). */
477
+ uploadAttachment: (file: Blob | File, options?: {
478
+ fileName?: string;
479
+ }) => Promise<GristAttachmentUploadResult>;
480
+ /** @access `"read table"` — Re-issue `grist.ready()` with new options. */
481
+ configure: (options: GristReadyOptions) => Promise<void>;
482
+ /** @access `"read table"` — Force a re-read of `sectionApi.mappings()`. */
483
+ refreshMappings: () => Promise<GristWidgetColumnMap | null>;
484
+ /**
485
+ * Alert kinds suppressed via `UseGristOptions.suppressAlerts`.
486
+ * Consumed automatically by `useGristSdkAlertDescriptors`.
487
+ * @access `"none"`
488
+ */
489
+ suppressAlerts?: readonly GristSdkAlertKind[];
490
+ /**
491
+ * Whether the widget declared `allowSelectBy: true` in its options.
492
+ * Used by the alert system to distinguish "section-not-linked" from "source-not-wired".
493
+ * @access `"none"`
494
+ */
495
+ allowSelectBy?: boolean;
496
+ };
497
+
498
+ export { formatColumnMappingAlertMessage as A, formatLinkStaleAlertMessage as C, formatMappingPendingAlertMessage as D, formatMappingUnreportedAlertMessage as E, formatSectionNotLinkedAlertMessage as F, formatSourceNotWiredAlertMessage as H, getAttachmentDownloadUrl as I, getAttachmentDownloadUrlForCell as J, getGristSdkAlertDescriptors as K, getGristSdkAlertSeverity as L, getGristSdkAlertTitle as M, gristAttachmentCellValue as N, gristAttachmentCellValues as O, isAccessInsufficientError as P, isWidgetSectionNotLinked as Q, loadColumnMetadataByTable as S, mergeGristAttachmentCellValue as T, orderedColumnIds as W, parseGristAttachmentUploadResponse as X, parseGristAttachmentUploadResponseIds as Y, uploadGristAttachment as Z, validateColumnMappings as _, buildReplicaColumnsFromColumnar as q, buildReplicaDocumentFromDocApi as r, extractAttachmentIdsFromUploadPayload as s, extractFirstAttachmentIdFromUploadPayload as t, extractGristAttachmentId as u, extractGristAttachmentIdsFromCell as v, fetchAttachmentBase64 as w, fetchAttachmentBase64ForCell as x, fetchAttachmentBlob as y, formatAccessInsufficientMessage as z };
499
+ export type { BuildReplicaDocumentFromDocApiOptions as B, GristSdkAlertsWidgetSlice as G, ReplicaTableColumnMetaLite as R, UseGristOptions as U, ValidateColumnMappingsResult as V, UseGristResult as a, GetGristSdkAlertDescriptorsOptions as b, GristSdkAlertDescriptor as c, UseGristSelectionResult as d, UseGristStatusResult as e, UseGristThemeResult as f, UseGristWritesResult as g, GristAttachmentUploadResult as h, GristEncodedAttachmentList as i, GristFetchedAttachment as j, GristFetchedAttachmentBlob as k, GristSdkAlertKind as l, GristSdkAlertSeverity as m, GristTheme as n, GristWidgetActionStatus as o, GristWidgetMode as p };
@@ -0,0 +1,43 @@
1
+ type UseGristWidgetOptionsFromContextConfig<T> = {
2
+ /** Default values returned before Grist responds and merged with stored options. */
3
+ defaults: T;
4
+ /** Debounce window for `setOptions` writes (ms). Default: 250. */
5
+ debounceMs?: number;
6
+ /**
7
+ * Optional namespace key. When set, this hook reads/writes
8
+ * `options[namespace]` instead of the whole options blob.
9
+ */
10
+ namespace?: string;
11
+ };
12
+ type UseGristWidgetOptionsFromContextResult<T> = {
13
+ options: T;
14
+ /** True until Grist has responded with the initial `onOptions` payload. */
15
+ loading: boolean;
16
+ /** Replace the options object (debounced write to Grist). */
17
+ setOptions: (next: T) => void;
18
+ /** Patch a subset of keys (debounced write to Grist). */
19
+ patchOptions: (patch: Partial<T>) => void;
20
+ /** Reset to `defaults` (immediate write to Grist). */
21
+ reset: () => Promise<void>;
22
+ };
23
+ /**
24
+ * Typed widget options hook designed for use **inside a `<GristWidgetProvider>`**.
25
+ *
26
+ * Unlike `useGristWidgetOptions()` (which calls `useGristReady()` and conflicts
27
+ * with the provider), this hook reads from the provider's existing context —
28
+ * `widgetOptions` for reactive reads, and `patchWidgetOptions` / `setWidgetOptions`
29
+ * for writes.
30
+ *
31
+ * ```tsx
32
+ * function MyConfigPanel() {
33
+ * const { options, patchOptions } = useGristWidgetOptionsFromContext({
34
+ * defaults: { color: '#FF0000', showLabels: true },
35
+ * })
36
+ * return <input value={options.color} onChange={e => patchOptions({ color: e.target.value })} />
37
+ * }
38
+ * ```
39
+ */
40
+ declare function useGristWidgetOptionsFromContext<T extends Record<string, unknown>>(config: UseGristWidgetOptionsFromContextConfig<T>): UseGristWidgetOptionsFromContextResult<T>;
41
+
42
+ export { useGristWidgetOptionsFromContext as u };
43
+ export type { UseGristWidgetOptionsFromContextConfig as U, UseGristWidgetOptionsFromContextResult as a };
@@ -0,0 +1,43 @@
1
+ type UseGristWidgetOptionsFromContextConfig<T> = {
2
+ /** Default values returned before Grist responds and merged with stored options. */
3
+ defaults: T;
4
+ /** Debounce window for `setOptions` writes (ms). Default: 250. */
5
+ debounceMs?: number;
6
+ /**
7
+ * Optional namespace key. When set, this hook reads/writes
8
+ * `options[namespace]` instead of the whole options blob.
9
+ */
10
+ namespace?: string;
11
+ };
12
+ type UseGristWidgetOptionsFromContextResult<T> = {
13
+ options: T;
14
+ /** True until Grist has responded with the initial `onOptions` payload. */
15
+ loading: boolean;
16
+ /** Replace the options object (debounced write to Grist). */
17
+ setOptions: (next: T) => void;
18
+ /** Patch a subset of keys (debounced write to Grist). */
19
+ patchOptions: (patch: Partial<T>) => void;
20
+ /** Reset to `defaults` (immediate write to Grist). */
21
+ reset: () => Promise<void>;
22
+ };
23
+ /**
24
+ * Typed widget options hook designed for use **inside a `<GristWidgetProvider>`**.
25
+ *
26
+ * Unlike `useGristWidgetOptions()` (which calls `useGristReady()` and conflicts
27
+ * with the provider), this hook reads from the provider's existing context —
28
+ * `widgetOptions` for reactive reads, and `patchWidgetOptions` / `setWidgetOptions`
29
+ * for writes.
30
+ *
31
+ * ```tsx
32
+ * function MyConfigPanel() {
33
+ * const { options, patchOptions } = useGristWidgetOptionsFromContext({
34
+ * defaults: { color: '#FF0000', showLabels: true },
35
+ * })
36
+ * return <input value={options.color} onChange={e => patchOptions({ color: e.target.value })} />
37
+ * }
38
+ * ```
39
+ */
40
+ declare function useGristWidgetOptionsFromContext<T extends Record<string, unknown>>(config: UseGristWidgetOptionsFromContextConfig<T>): UseGristWidgetOptionsFromContextResult<T>;
41
+
42
+ export { useGristWidgetOptionsFromContext as u };
43
+ export type { UseGristWidgetOptionsFromContextConfig as U, UseGristWidgetOptionsFromContextResult as a };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grist-widget-sdk",
3
- "version": "0.3.20",
3
+ "version": "0.3.26",
4
4
  "description": "React hooks and helpers for Grist custom widgets",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -50,6 +50,16 @@
50
50
  "default": "./dist/advanced.cjs"
51
51
  }
52
52
  },
53
+ "./app": {
54
+ "import": {
55
+ "types": "./dist/app.d.ts",
56
+ "default": "./dist/app.js"
57
+ },
58
+ "require": {
59
+ "types": "./dist/app.d.cts",
60
+ "default": "./dist/app.cjs"
61
+ }
62
+ },
53
63
  "./emulator": {
54
64
  "import": {
55
65
  "types": "./dist/emulator.d.ts",
@@ -89,10 +99,13 @@
89
99
  "types": "./dist/emulator/devtools.d.cts",
90
100
  "default": "./dist/emulator/devtools.cjs"
91
101
  }
92
- }
102
+ },
103
+ "./tsconfig/app.json": "./tsconfig/app.json",
104
+ "./tsconfig/node.json": "./tsconfig/node.json"
93
105
  },
94
106
  "files": [
95
107
  "dist",
108
+ "tsconfig",
96
109
  "CHANGELOG.md",
97
110
  "LICENSE"
98
111
  ],
@@ -0,0 +1,38 @@
1
+ {
2
+ // Shared TypeScript settings for a widget's browser code (src/).
3
+ //
4
+ // // tsconfig.app.json
5
+ // { "extends": "grist-widget-sdk/tsconfig/app.json", ... }
6
+ //
7
+ // The widget's own file keeps whatever names its files and packages:
8
+ // tsBuildInfoFile, paths and include (TypeScript resolves those relative
9
+ // to the file that declares them), and types (its own list of global type
10
+ // packages, e.g. "vite/client"; a list set there would replace this one).
11
+ //
12
+ // Needs TypeScript 5.8 or later (erasableSyntaxOnly). Making a check
13
+ // stricter can fail a widget's typecheck, so it only happens in a minor
14
+ // (breaking, before 1.0) release of grist-widget-sdk.
15
+ "compilerOptions": {
16
+ "target": "ES2022",
17
+ "useDefineForClassFields": true,
18
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
19
+ "module": "ESNext",
20
+ "skipLibCheck": true,
21
+
22
+ /* Bundler mode */
23
+ "moduleResolution": "bundler",
24
+ "allowImportingTsExtensions": true,
25
+ "verbatimModuleSyntax": true,
26
+ "moduleDetection": "force",
27
+ "noEmit": true,
28
+ "jsx": "react-jsx",
29
+
30
+ /* Linting */
31
+ "strict": true,
32
+ "noUnusedLocals": true,
33
+ "noUnusedParameters": true,
34
+ "erasableSyntaxOnly": true,
35
+ "noFallthroughCasesInSwitch": true,
36
+ "noUncheckedSideEffectImports": true
37
+ }
38
+ }