grist-widget-sdk 0.3.27 → 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 (88) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/README.md +79 -208
  3. package/dist/app.cjs +1336 -4415
  4. package/dist/app.cjs.map +1 -1
  5. package/dist/app.d.cts +6 -103
  6. package/dist/app.d.ts +6 -103
  7. package/dist/app.js +5 -7
  8. package/dist/chunk-4ZGVKGDF.js +204 -0
  9. package/dist/chunk-4ZGVKGDF.js.map +1 -0
  10. package/dist/{chunk-6Y2IONGJ.js → chunk-LSALXIGM.js} +191 -20
  11. package/dist/chunk-LSALXIGM.js.map +1 -0
  12. package/dist/chunk-MAPAWVQC.js +1898 -0
  13. package/dist/chunk-MAPAWVQC.js.map +1 -0
  14. package/dist/{chunk-YQNMIL33.js → chunk-OXA2VITC.js} +532 -181
  15. package/dist/chunk-OXA2VITC.js.map +1 -0
  16. package/dist/define-widget-B1FIvI-h.d.cts +433 -0
  17. package/dist/define-widget-B9gEtJP0.d.ts +433 -0
  18. package/dist/emulator/testing.cjs +3341 -6512
  19. package/dist/emulator/testing.cjs.map +1 -1
  20. package/dist/emulator/testing.d.cts +29 -14
  21. package/dist/emulator/testing.d.ts +29 -14
  22. package/dist/emulator/testing.js +78 -72
  23. package/dist/emulator/testing.js.map +1 -1
  24. package/dist/emulator.cjs +63 -249
  25. package/dist/emulator.cjs.map +1 -1
  26. package/dist/emulator.d.cts +6 -8
  27. package/dist/emulator.d.ts +6 -8
  28. package/dist/emulator.js +5 -11
  29. package/dist/index.cjs +1442 -4862
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +81 -260
  32. package/dist/index.d.ts +81 -260
  33. package/dist/index.js +12 -158
  34. package/dist/mount-grist-widget-CkB-3iG_.d.ts +137 -0
  35. package/dist/mount-grist-widget-Dtelqao_.d.cts +137 -0
  36. package/dist/{inline-DpcT8tV9.d.ts → templates-CGmRIQBV.d.cts} +89 -35
  37. package/dist/{inline-C7DkvuET.d.cts → templates-Cq94qOcf.d.ts} +89 -35
  38. package/dist/{types-D3JxUCfL.d.cts → types-DBStn0Dk.d.cts} +2 -26
  39. package/dist/{types-D3JxUCfL.d.ts → types-DBStn0Dk.d.ts} +2 -26
  40. package/package.json +8 -37
  41. package/dist/advanced.cjs +0 -2799
  42. package/dist/advanced.cjs.map +0 -1
  43. package/dist/advanced.d.cts +0 -822
  44. package/dist/advanced.d.ts +0 -822
  45. package/dist/advanced.js +0 -872
  46. package/dist/advanced.js.map +0 -1
  47. package/dist/chunk-22U7WAY3.js +0 -463
  48. package/dist/chunk-22U7WAY3.js.map +0 -1
  49. package/dist/chunk-64SVRWW7.js +0 -758
  50. package/dist/chunk-64SVRWW7.js.map +0 -1
  51. package/dist/chunk-6Y2IONGJ.js.map +0 -1
  52. package/dist/chunk-B4AKVSWV.js +0 -2362
  53. package/dist/chunk-B4AKVSWV.js.map +0 -1
  54. package/dist/chunk-ELGVOHFD.js +0 -32
  55. package/dist/chunk-ELGVOHFD.js.map +0 -1
  56. package/dist/chunk-GTTD5HPO.js +0 -36
  57. package/dist/chunk-GTTD5HPO.js.map +0 -1
  58. package/dist/chunk-NO6FYX33.js +0 -2084
  59. package/dist/chunk-NO6FYX33.js.map +0 -1
  60. package/dist/chunk-OPTOYD2Y.js +0 -277
  61. package/dist/chunk-OPTOYD2Y.js.map +0 -1
  62. package/dist/chunk-PM3B4WYV.js +0 -12
  63. package/dist/chunk-PM3B4WYV.js.map +0 -1
  64. package/dist/chunk-REV5AUXD.js +0 -25
  65. package/dist/chunk-REV5AUXD.js.map +0 -1
  66. package/dist/chunk-YQNMIL33.js.map +0 -1
  67. package/dist/create-grist-emulator-BCqp1BF9.d.ts +0 -81
  68. package/dist/create-grist-emulator-DBM8OSIR.d.cts +0 -81
  69. package/dist/emulator/devtools.cjs +0 -1404
  70. package/dist/emulator/devtools.cjs.map +0 -1
  71. package/dist/emulator/devtools.d.cts +0 -48
  72. package/dist/emulator/devtools.d.ts +0 -48
  73. package/dist/emulator/devtools.js +0 -506
  74. package/dist/emulator/devtools.js.map +0 -1
  75. package/dist/emulator/transports.cjs +0 -1132
  76. package/dist/emulator/transports.cjs.map +0 -1
  77. package/dist/emulator/transports.d.cts +0 -38
  78. package/dist/emulator/transports.d.ts +0 -38
  79. package/dist/emulator/transports.js +0 -12
  80. package/dist/emulator/transports.js.map +0 -1
  81. package/dist/templates-8k2LKoO-.d.cts +0 -24
  82. package/dist/templates-DIwi_mvI.d.ts +0 -24
  83. package/dist/use-grist-reads-DUMvJgV7.d.ts +0 -480
  84. package/dist/use-grist-reads-DpjVuI0H.d.cts +0 -480
  85. package/dist/use-grist-types-DdOLEJXN.d.cts +0 -499
  86. package/dist/use-grist-types-kwxoFeub.d.ts +0 -499
  87. package/dist/use-grist-widget-options-from-context-CGOlQ5Vc.d.cts +0 -43
  88. package/dist/use-grist-widget-options-from-context-CGOlQ5Vc.d.ts +0 -43
@@ -0,0 +1,137 @@
1
+ import * as react from 'react';
2
+ import { ComponentType, ReactNode } from 'react';
3
+ import { U as UseGristOptions, G as GristWidget } from './define-widget-B9gEtJP0.js';
4
+
5
+ /**
6
+ * Where a deployed build is being served from.
7
+ *
8
+ * A widget's deploy pipeline publishes the same build at its root and at
9
+ * `/latest/`, a preview at `/dev/`, and each release at `/v<version>/`, all
10
+ * under one base path (e.g. `/my-widget/` on GitHub Pages). Opened outside
11
+ * Grist, the page tells them apart from `window.location.pathname` alone.
12
+ */
13
+ type DeployChannel = {
14
+ kind: "latest";
15
+ } | {
16
+ kind: "dev";
17
+ } | {
18
+ kind: "version";
19
+ version: string;
20
+ };
21
+ type DeployPath = {
22
+ channel: DeployChannel;
23
+ /**
24
+ * The path the channels live under, always ending in `/`: the current
25
+ * path without its channel segment. `"/my-widget/v1.2.0/"` gives
26
+ * `"/my-widget/"`.
27
+ */
28
+ basePath: string;
29
+ };
30
+ /**
31
+ * Parse a pathname into its deploy channel and base path. A path without a
32
+ * channel segment (the deployed root, or a local dev server) is `latest`:
33
+ * the deploy places the latest release at the root too.
34
+ */
35
+ declare function parseDeployPath(pathname: string): DeployPath;
36
+ /** `"latest"`, `"dev"` or `"v1.2.0"`. */
37
+ declare function deployChannelLabel(channel: DeployChannel): string;
38
+
39
+ type GristWidgetAppProps = {
40
+ /** The widget. A `React.lazy()` component works too: its code then loads only inside Grist. */
41
+ App: ComponentType;
42
+ /** The widget's `GRIST_OPTIONS`: access level, columns, and so on. */
43
+ options: UseGristOptions;
44
+ /** The page's title and description (the widget's `WIDGET_METADATA`, or its declaration's). */
45
+ metadata?: {
46
+ title?: string;
47
+ description?: string;
48
+ };
49
+ /**
50
+ * Whether this is the local dev server. Pass `import.meta.env.DEV`.
51
+ * Outside Grist it picks the local-dev page over the release page.
52
+ */
53
+ dev?: boolean;
54
+ /**
55
+ * Wraps the widget inside Grist, under {@link GristBoundary}. The default
56
+ * lists what needs the user's attention (column mapping, access level,
57
+ * failed writes) above the widget; `null` renders the widget alone.
58
+ */
59
+ Alerts?: ComponentType<{
60
+ children: ReactNode;
61
+ }> | null;
62
+ /** Shown outside Grist on the local dev server. Defaults to a page pointing at `pnpm test` and the `dev` loop. */
63
+ LocalDevPage?: ComponentType;
64
+ /**
65
+ * Shown outside Grist on a deployed URL (`/latest/`, `/dev/`, `/v<version>/`).
66
+ * Defaults to a page linking every channel and version, with the URL to
67
+ * paste into Grist.
68
+ */
69
+ ReleasePage?: ComponentType<{
70
+ channel: DeployChannel;
71
+ }>;
72
+ };
73
+ type MountGristWidgetConfig = GristWidgetAppProps & {
74
+ /** The element to render into, or its CSS selector. Defaults to `#root`. */
75
+ root?: Element | string;
76
+ };
77
+ /**
78
+ * A widget's entry module, `src/app/App.tsx`, as `import * as entry` gives
79
+ * it: the widget as its default export, its declaration as `widget` (or, in
80
+ * a widget made before `defineGristWidget`, `GRIST_OPTIONS` and
81
+ * `WIDGET_METADATA`), and optionally its own `Alerts`, `LocalDevPage` and
82
+ * `ReleasePage`. Everything a widget customises lives there, in its own
83
+ * file: the template's `src/main.tsx` stays the same in every widget, and
84
+ * `create-grist-widget upgrade` can rewrite it without losing anything.
85
+ */
86
+ type GristWidgetEntry = {
87
+ default: ComponentType;
88
+ widget?: GristWidget;
89
+ GRIST_OPTIONS?: UseGristOptions;
90
+ WIDGET_METADATA?: {
91
+ title?: string;
92
+ description?: string;
93
+ };
94
+ Alerts?: GristWidgetAppProps["Alerts"];
95
+ LocalDevPage?: GristWidgetAppProps["LocalDevPage"];
96
+ ReleasePage?: GristWidgetAppProps["ReleasePage"];
97
+ };
98
+ /** The props {@link GristWidgetApp} takes, from a widget's entry module. */
99
+ declare function entryProps(entry: GristWidgetEntry, dev?: boolean): GristWidgetAppProps;
100
+ type MountedGristWidget = {
101
+ unmount: () => void;
102
+ };
103
+ /**
104
+ * Render a widget into the page: everything `src/main.tsx` used to wire by
105
+ * hand.
106
+ *
107
+ * ```tsx
108
+ * import { mountGristWidget } from "grist-widget-sdk/app"
109
+ * import "./index.css"
110
+ * import * as entry from "./app/App"
111
+ *
112
+ * mountGristWidget(entry, { dev: import.meta.env.DEV })
113
+ * ```
114
+ *
115
+ * It reads the entry module's exports ({@link GristWidgetEntry}): the widget,
116
+ * its declaration and any page it replaces. The older form,
117
+ * `mountGristWidget({ App, options: GRIST_OPTIONS, dev })`, still works.
118
+ *
119
+ * Inside Grist, the widget renders in {@link GristWidgetProvider} and
120
+ * {@link GristBoundary}, which gates on its mapped columns when it declares
121
+ * any, and follows Grist's light/dark theme. Outside Grist, where there is
122
+ * nothing to connect to, the page shows `LocalDevPage` on the dev server and
123
+ * `ReleasePage` on a deployed URL.
124
+ */
125
+ declare function mountGristWidget(entry: GristWidgetEntry, options?: {
126
+ dev?: boolean;
127
+ root?: Element | string;
128
+ }): MountedGristWidget;
129
+ declare function mountGristWidget(config: MountGristWidgetConfig): MountedGristWidget;
130
+ /**
131
+ * The tree {@link mountGristWidget} renders, as a component: for a test, or
132
+ * for a page that manages its own React root.
133
+ */
134
+ declare function GristWidgetApp({ App, options, metadata, dev, Alerts, LocalDevPage, ReleasePage, }: GristWidgetAppProps): react.JSX.Element;
135
+
136
+ export { GristWidgetApp as G, deployChannelLabel as e, entryProps as f, mountGristWidget as m, parseDeployPath as p };
137
+ export type { DeployChannel as D, MountGristWidgetConfig as M, DeployPath as a, GristWidgetAppProps as b, GristWidgetEntry as c, MountedGristWidget as d };
@@ -0,0 +1,137 @@
1
+ import * as react from 'react';
2
+ import { ComponentType, ReactNode } from 'react';
3
+ import { U as UseGristOptions, G as GristWidget } from './define-widget-B1FIvI-h.cjs';
4
+
5
+ /**
6
+ * Where a deployed build is being served from.
7
+ *
8
+ * A widget's deploy pipeline publishes the same build at its root and at
9
+ * `/latest/`, a preview at `/dev/`, and each release at `/v<version>/`, all
10
+ * under one base path (e.g. `/my-widget/` on GitHub Pages). Opened outside
11
+ * Grist, the page tells them apart from `window.location.pathname` alone.
12
+ */
13
+ type DeployChannel = {
14
+ kind: "latest";
15
+ } | {
16
+ kind: "dev";
17
+ } | {
18
+ kind: "version";
19
+ version: string;
20
+ };
21
+ type DeployPath = {
22
+ channel: DeployChannel;
23
+ /**
24
+ * The path the channels live under, always ending in `/`: the current
25
+ * path without its channel segment. `"/my-widget/v1.2.0/"` gives
26
+ * `"/my-widget/"`.
27
+ */
28
+ basePath: string;
29
+ };
30
+ /**
31
+ * Parse a pathname into its deploy channel and base path. A path without a
32
+ * channel segment (the deployed root, or a local dev server) is `latest`:
33
+ * the deploy places the latest release at the root too.
34
+ */
35
+ declare function parseDeployPath(pathname: string): DeployPath;
36
+ /** `"latest"`, `"dev"` or `"v1.2.0"`. */
37
+ declare function deployChannelLabel(channel: DeployChannel): string;
38
+
39
+ type GristWidgetAppProps = {
40
+ /** The widget. A `React.lazy()` component works too: its code then loads only inside Grist. */
41
+ App: ComponentType;
42
+ /** The widget's `GRIST_OPTIONS`: access level, columns, and so on. */
43
+ options: UseGristOptions;
44
+ /** The page's title and description (the widget's `WIDGET_METADATA`, or its declaration's). */
45
+ metadata?: {
46
+ title?: string;
47
+ description?: string;
48
+ };
49
+ /**
50
+ * Whether this is the local dev server. Pass `import.meta.env.DEV`.
51
+ * Outside Grist it picks the local-dev page over the release page.
52
+ */
53
+ dev?: boolean;
54
+ /**
55
+ * Wraps the widget inside Grist, under {@link GristBoundary}. The default
56
+ * lists what needs the user's attention (column mapping, access level,
57
+ * failed writes) above the widget; `null` renders the widget alone.
58
+ */
59
+ Alerts?: ComponentType<{
60
+ children: ReactNode;
61
+ }> | null;
62
+ /** Shown outside Grist on the local dev server. Defaults to a page pointing at `pnpm test` and the `dev` loop. */
63
+ LocalDevPage?: ComponentType;
64
+ /**
65
+ * Shown outside Grist on a deployed URL (`/latest/`, `/dev/`, `/v<version>/`).
66
+ * Defaults to a page linking every channel and version, with the URL to
67
+ * paste into Grist.
68
+ */
69
+ ReleasePage?: ComponentType<{
70
+ channel: DeployChannel;
71
+ }>;
72
+ };
73
+ type MountGristWidgetConfig = GristWidgetAppProps & {
74
+ /** The element to render into, or its CSS selector. Defaults to `#root`. */
75
+ root?: Element | string;
76
+ };
77
+ /**
78
+ * A widget's entry module, `src/app/App.tsx`, as `import * as entry` gives
79
+ * it: the widget as its default export, its declaration as `widget` (or, in
80
+ * a widget made before `defineGristWidget`, `GRIST_OPTIONS` and
81
+ * `WIDGET_METADATA`), and optionally its own `Alerts`, `LocalDevPage` and
82
+ * `ReleasePage`. Everything a widget customises lives there, in its own
83
+ * file: the template's `src/main.tsx` stays the same in every widget, and
84
+ * `create-grist-widget upgrade` can rewrite it without losing anything.
85
+ */
86
+ type GristWidgetEntry = {
87
+ default: ComponentType;
88
+ widget?: GristWidget;
89
+ GRIST_OPTIONS?: UseGristOptions;
90
+ WIDGET_METADATA?: {
91
+ title?: string;
92
+ description?: string;
93
+ };
94
+ Alerts?: GristWidgetAppProps["Alerts"];
95
+ LocalDevPage?: GristWidgetAppProps["LocalDevPage"];
96
+ ReleasePage?: GristWidgetAppProps["ReleasePage"];
97
+ };
98
+ /** The props {@link GristWidgetApp} takes, from a widget's entry module. */
99
+ declare function entryProps(entry: GristWidgetEntry, dev?: boolean): GristWidgetAppProps;
100
+ type MountedGristWidget = {
101
+ unmount: () => void;
102
+ };
103
+ /**
104
+ * Render a widget into the page: everything `src/main.tsx` used to wire by
105
+ * hand.
106
+ *
107
+ * ```tsx
108
+ * import { mountGristWidget } from "grist-widget-sdk/app"
109
+ * import "./index.css"
110
+ * import * as entry from "./app/App"
111
+ *
112
+ * mountGristWidget(entry, { dev: import.meta.env.DEV })
113
+ * ```
114
+ *
115
+ * It reads the entry module's exports ({@link GristWidgetEntry}): the widget,
116
+ * its declaration and any page it replaces. The older form,
117
+ * `mountGristWidget({ App, options: GRIST_OPTIONS, dev })`, still works.
118
+ *
119
+ * Inside Grist, the widget renders in {@link GristWidgetProvider} and
120
+ * {@link GristBoundary}, which gates on its mapped columns when it declares
121
+ * any, and follows Grist's light/dark theme. Outside Grist, where there is
122
+ * nothing to connect to, the page shows `LocalDevPage` on the dev server and
123
+ * `ReleasePage` on a deployed URL.
124
+ */
125
+ declare function mountGristWidget(entry: GristWidgetEntry, options?: {
126
+ dev?: boolean;
127
+ root?: Element | string;
128
+ }): MountedGristWidget;
129
+ declare function mountGristWidget(config: MountGristWidgetConfig): MountedGristWidget;
130
+ /**
131
+ * The tree {@link mountGristWidget} renders, as a component: for a test, or
132
+ * for a page that manages its own React root.
133
+ */
134
+ declare function GristWidgetApp({ App, options, metadata, dev, Alerts, LocalDevPage, ReleasePage, }: GristWidgetAppProps): react.JSX.Element;
135
+
136
+ export { GristWidgetApp as G, deployChannelLabel as e, entryProps as f, mountGristWidget as m, parseDeployPath as p };
137
+ export type { DeployChannel as D, MountGristWidgetConfig as M, DeployPath as a, GristWidgetAppProps as b, GristWidgetEntry as c, MountedGristWidget as d };
@@ -1,4 +1,4 @@
1
- import { e as GristCursorPos, f as GristWidgetInteraction, d as GristWidgetColumnMap, c as GristReplicaColumn, G as GristRowRecord, g as GristReplicaSelection, h as GristWidgetConfiguration, i as GristReadyOptions, j as GristFetchSelectedOptions, k as GristTableOperations } from './types-D3JxUCfL.js';
1
+ import { c as GristCursorPos, d as GristWidgetInteraction, b as GristWidgetColumnMap, e as GristReplicaColumn, f as GristRowRecord, g as GristReplicaSelection, h as GristWidgetConfiguration, i as GristReadyOptions, j as GristFetchSelectedOptions, k as GristTableOperations, G as GristReplicaDocument } from './types-DBStn0Dk.cjs';
2
2
 
3
3
  /**
4
4
  * Tiny pub-sub used to broadcast every emulator state change to:
@@ -108,7 +108,7 @@ type EmulatorDocumentState = {
108
108
 
109
109
  /**
110
110
  * Type-only signatures of the Grist plugin interfaces that the emulator
111
- * exposes to the widget (under both inline and iframe transports).
111
+ * exposes to the widget.
112
112
  *
113
113
  * These mirror the upstream `grist-plugin-api` contracts. We re-declare them
114
114
  * here instead of `import`ing the upstream types because:
@@ -182,7 +182,7 @@ type CommandApiImpl = {
182
182
 
183
183
  /**
184
184
  * Bundles the six host-side Grist API implementations into one record so
185
- * transports (iframe, inline) can wire them up uniformly.
185
+ * inline transport can wire them up.
186
186
  */
187
187
 
188
188
  type EmulatorApiBundle = {
@@ -208,26 +208,6 @@ type EmulatorTransport = {
208
208
  /** Replays the post-ready bootstrap (options + cursor + data) as if the widget had just connected. */
209
209
  emitInitialState: () => void;
210
210
  };
211
- type TransportContext = {
212
- state: EmulatorDocumentState;
213
- bus: EventBus;
214
- api: EmulatorApiBundle;
215
- /** Initial document theme replayed on widget ready (host message channel). */
216
- initialTheme?: "light" | "dark";
217
- /**
218
- * Information piggybacked on `onOptions`. Same as `state.widgetInteraction`
219
- * by default but transports may augment it (e.g. `access_level` derived
220
- * from `state.configuration.requiredAccess`).
221
- */
222
- buildInteraction?: () => GristWidgetInteraction;
223
- /**
224
- * Cursor sentinel sent on initial connection. Defaults to the current
225
- * `state.selection.rowId` (which can be `null`, `"new"`, or a numeric id).
226
- */
227
- initialCursor?: () => number | "new" | null;
228
- /** Optional cursor sent alongside `onRecord` payloads. */
229
- cursor?: () => GristCursorPos | undefined;
230
- };
231
211
 
232
212
  /**
233
213
  * Inline transport — synthesises a `window.grist` global wired directly to
@@ -291,22 +271,96 @@ type InlineGristGlobal = {
291
271
  };
292
272
  on: (eventName: string, listener: (...args: unknown[]) => void) => () => void;
293
273
  };
294
- type InlineTransportOptions = TransportContext & {
274
+
275
+ /**
276
+ * Top-level emulator factory. Produces a fully-wired emulator instance:
277
+ *
278
+ * - typed in-memory document state,
279
+ * - shared event bus,
280
+ * - the six host API impls,
281
+ * - the inline transport, which installs `grist` on the page,
282
+ * - imperative helpers (`mutate`, `apply`, `selectRow`, `selectTable`)
283
+ * that drive the document via the action engine and emit corresponding
284
+ * events.
285
+ *
286
+ * The instance is multi-instance friendly: nothing is stored on `window`.
287
+ */
288
+
289
+ type CreateGristEmulatorOptions = {
290
+ /** The starting document (must satisfy {@link parseDocument}). */
291
+ document: GristReplicaDocument;
292
+ /** Installs `grist` on `target` (default: the global object). */
293
+ transport?: {
294
+ kind: "inline";
295
+ target?: Window & typeof globalThis;
296
+ };
297
+ /** Initial widget options exposed via `WidgetAPI`. */
298
+ widgetOptions?: Record<string, unknown>;
299
+ /**
300
+ * Initial widget interaction info (`access_level` and friends) sent
301
+ * alongside `onOptions`. Without an `access_level`, Grist grants the access
302
+ * the widget requests in `grist.ready()`.
303
+ */
304
+ widgetInteraction?: Record<string, unknown>;
305
+ /** Initial document theme replayed on widget ready (host `message` channel). */
306
+ initialTheme?: "light" | "dark";
307
+ };
308
+ type GristEmulator = {
309
+ /** Underlying mutable document. Treat as read-only outside of `mutate(...)`. */
310
+ state: EmulatorDocumentState;
311
+ bus: EventBus;
312
+ api: EmulatorApiBundle;
313
+ transport: EmulatorTransport;
314
+ /** Synthesised `grist` global (only for the inline transport). */
315
+ grist?: InlineGristGlobal;
316
+ /** Apply a list of Grist user actions; emits the matching events. */
317
+ apply: (actions: unknown[][]) => {
318
+ retValues: unknown[];
319
+ };
320
+ /** Convenience helpers for tests and demos. */
321
+ mutate: {
322
+ addRow: (tableId: string, fields: Record<string, unknown>) => number;
323
+ updateRow: (tableId: string, rowId: number, fields: Record<string, unknown>) => void;
324
+ removeRow: (tableId: string, rowId: number) => void;
325
+ };
326
+ selectRow: (rowId: number | "new" | null) => void;
327
+ selectTable: (tableId: string) => void;
295
328
  /**
296
- * Where to mount the synthesised `grist` global.
297
- * Defaults to the current window. Pass a different target (e.g. an iframe's
298
- * `contentWindow`) to install it in another realm.
329
+ * Set logical→real column mappings, as a user mapping columns in Grist would.
330
+ * Pass `undefined` to clear — `mappings()` becomes `null` until set again.
299
331
  */
300
- target?: Window & typeof globalThis;
332
+ setColumnMappings: (mappings: GristWidgetColumnMap | undefined) => void;
333
+ setOptions: (options: Record<string, unknown>) => Promise<void>;
334
+ setInteraction: (interaction: Record<string, unknown>) => void;
335
+ /** Host theme controls (`grist.on("message")` / `msg.theme`). */
336
+ theme: {
337
+ set: (theme: "light" | "dark") => void;
338
+ };
339
+ /** Tear down the emulator. Idempotent. */
340
+ dispose: () => void;
301
341
  };
342
+ declare function createGristEmulator(options: CreateGristEmulatorOptions): GristEmulator;
343
+
302
344
  /**
303
- * Returns an {@link EmulatorTransport} **and** the synthesised `grist`
304
- * object. `target.grist = global` is set as a side-effect for parity with
305
- * the production runtime.
345
+ * Document templates exposed at the top of the emulator API.
346
+ *
347
+ * Templates return plain data — they don't allocate state, the caller passes
348
+ * the returned value into `createGristEmulator(...)` which performs the allocation.
306
349
  */
307
- declare function createInlineTransport(options: InlineTransportOptions): EmulatorTransport & {
308
- grist: InlineGristGlobal;
350
+
351
+ /**
352
+ * Useful templates for quick demos and tests.
353
+ */
354
+ declare const presets: {
355
+ /**
356
+ * Default starter fixture for demos / sandboxes that need a neutral document
357
+ * without choosing a domain-specific preset.
358
+ */
359
+ defaultTemplate(): GristReplicaDocument;
360
+ blank(): GristReplicaDocument;
361
+ todoList(): GristReplicaDocument;
362
+ contacts(): GristReplicaDocument;
309
363
  };
310
364
 
311
- export { createInlineTransport as i };
312
- export type { EmulatorDocumentState as E, InlineGristGlobal as I, TransportContext as T, EmulatorEvent as a, EmulatorColumn as b, EmulatorApiBundle as c, EmulatorRowId as d, EmulatorTable as e, EmulatorTransport as f, EventBus as g, InlineTransportOptions as h };
365
+ export { createGristEmulator as g, presets as p };
366
+ export type { CreateGristEmulatorOptions as C, EmulatorColumn as E, GristEmulator as G, InlineGristGlobal as I, EmulatorDocumentState as a, EmulatorApiBundle as b, EmulatorEvent as c, EmulatorRowId as d, EmulatorTable as e, EmulatorTransport as f };
@@ -1,4 +1,4 @@
1
- import { e as GristCursorPos, f as GristWidgetInteraction, d as GristWidgetColumnMap, c as GristReplicaColumn, G as GristRowRecord, g as GristReplicaSelection, h as GristWidgetConfiguration, i as GristReadyOptions, j as GristFetchSelectedOptions, k as GristTableOperations } from './types-D3JxUCfL.cjs';
1
+ import { c as GristCursorPos, d as GristWidgetInteraction, b as GristWidgetColumnMap, e as GristReplicaColumn, f as GristRowRecord, g as GristReplicaSelection, h as GristWidgetConfiguration, i as GristReadyOptions, j as GristFetchSelectedOptions, k as GristTableOperations, G as GristReplicaDocument } from './types-DBStn0Dk.js';
2
2
 
3
3
  /**
4
4
  * Tiny pub-sub used to broadcast every emulator state change to:
@@ -108,7 +108,7 @@ type EmulatorDocumentState = {
108
108
 
109
109
  /**
110
110
  * Type-only signatures of the Grist plugin interfaces that the emulator
111
- * exposes to the widget (under both inline and iframe transports).
111
+ * exposes to the widget.
112
112
  *
113
113
  * These mirror the upstream `grist-plugin-api` contracts. We re-declare them
114
114
  * here instead of `import`ing the upstream types because:
@@ -182,7 +182,7 @@ type CommandApiImpl = {
182
182
 
183
183
  /**
184
184
  * Bundles the six host-side Grist API implementations into one record so
185
- * transports (iframe, inline) can wire them up uniformly.
185
+ * inline transport can wire them up.
186
186
  */
187
187
 
188
188
  type EmulatorApiBundle = {
@@ -208,26 +208,6 @@ type EmulatorTransport = {
208
208
  /** Replays the post-ready bootstrap (options + cursor + data) as if the widget had just connected. */
209
209
  emitInitialState: () => void;
210
210
  };
211
- type TransportContext = {
212
- state: EmulatorDocumentState;
213
- bus: EventBus;
214
- api: EmulatorApiBundle;
215
- /** Initial document theme replayed on widget ready (host message channel). */
216
- initialTheme?: "light" | "dark";
217
- /**
218
- * Information piggybacked on `onOptions`. Same as `state.widgetInteraction`
219
- * by default but transports may augment it (e.g. `access_level` derived
220
- * from `state.configuration.requiredAccess`).
221
- */
222
- buildInteraction?: () => GristWidgetInteraction;
223
- /**
224
- * Cursor sentinel sent on initial connection. Defaults to the current
225
- * `state.selection.rowId` (which can be `null`, `"new"`, or a numeric id).
226
- */
227
- initialCursor?: () => number | "new" | null;
228
- /** Optional cursor sent alongside `onRecord` payloads. */
229
- cursor?: () => GristCursorPos | undefined;
230
- };
231
211
 
232
212
  /**
233
213
  * Inline transport — synthesises a `window.grist` global wired directly to
@@ -291,22 +271,96 @@ type InlineGristGlobal = {
291
271
  };
292
272
  on: (eventName: string, listener: (...args: unknown[]) => void) => () => void;
293
273
  };
294
- type InlineTransportOptions = TransportContext & {
274
+
275
+ /**
276
+ * Top-level emulator factory. Produces a fully-wired emulator instance:
277
+ *
278
+ * - typed in-memory document state,
279
+ * - shared event bus,
280
+ * - the six host API impls,
281
+ * - the inline transport, which installs `grist` on the page,
282
+ * - imperative helpers (`mutate`, `apply`, `selectRow`, `selectTable`)
283
+ * that drive the document via the action engine and emit corresponding
284
+ * events.
285
+ *
286
+ * The instance is multi-instance friendly: nothing is stored on `window`.
287
+ */
288
+
289
+ type CreateGristEmulatorOptions = {
290
+ /** The starting document (must satisfy {@link parseDocument}). */
291
+ document: GristReplicaDocument;
292
+ /** Installs `grist` on `target` (default: the global object). */
293
+ transport?: {
294
+ kind: "inline";
295
+ target?: Window & typeof globalThis;
296
+ };
297
+ /** Initial widget options exposed via `WidgetAPI`. */
298
+ widgetOptions?: Record<string, unknown>;
299
+ /**
300
+ * Initial widget interaction info (`access_level` and friends) sent
301
+ * alongside `onOptions`. Without an `access_level`, Grist grants the access
302
+ * the widget requests in `grist.ready()`.
303
+ */
304
+ widgetInteraction?: Record<string, unknown>;
305
+ /** Initial document theme replayed on widget ready (host `message` channel). */
306
+ initialTheme?: "light" | "dark";
307
+ };
308
+ type GristEmulator = {
309
+ /** Underlying mutable document. Treat as read-only outside of `mutate(...)`. */
310
+ state: EmulatorDocumentState;
311
+ bus: EventBus;
312
+ api: EmulatorApiBundle;
313
+ transport: EmulatorTransport;
314
+ /** Synthesised `grist` global (only for the inline transport). */
315
+ grist?: InlineGristGlobal;
316
+ /** Apply a list of Grist user actions; emits the matching events. */
317
+ apply: (actions: unknown[][]) => {
318
+ retValues: unknown[];
319
+ };
320
+ /** Convenience helpers for tests and demos. */
321
+ mutate: {
322
+ addRow: (tableId: string, fields: Record<string, unknown>) => number;
323
+ updateRow: (tableId: string, rowId: number, fields: Record<string, unknown>) => void;
324
+ removeRow: (tableId: string, rowId: number) => void;
325
+ };
326
+ selectRow: (rowId: number | "new" | null) => void;
327
+ selectTable: (tableId: string) => void;
295
328
  /**
296
- * Where to mount the synthesised `grist` global.
297
- * Defaults to the current window. Pass a different target (e.g. an iframe's
298
- * `contentWindow`) to install it in another realm.
329
+ * Set logical→real column mappings, as a user mapping columns in Grist would.
330
+ * Pass `undefined` to clear — `mappings()` becomes `null` until set again.
299
331
  */
300
- target?: Window & typeof globalThis;
332
+ setColumnMappings: (mappings: GristWidgetColumnMap | undefined) => void;
333
+ setOptions: (options: Record<string, unknown>) => Promise<void>;
334
+ setInteraction: (interaction: Record<string, unknown>) => void;
335
+ /** Host theme controls (`grist.on("message")` / `msg.theme`). */
336
+ theme: {
337
+ set: (theme: "light" | "dark") => void;
338
+ };
339
+ /** Tear down the emulator. Idempotent. */
340
+ dispose: () => void;
301
341
  };
342
+ declare function createGristEmulator(options: CreateGristEmulatorOptions): GristEmulator;
343
+
302
344
  /**
303
- * Returns an {@link EmulatorTransport} **and** the synthesised `grist`
304
- * object. `target.grist = global` is set as a side-effect for parity with
305
- * the production runtime.
345
+ * Document templates exposed at the top of the emulator API.
346
+ *
347
+ * Templates return plain data — they don't allocate state, the caller passes
348
+ * the returned value into `createGristEmulator(...)` which performs the allocation.
306
349
  */
307
- declare function createInlineTransport(options: InlineTransportOptions): EmulatorTransport & {
308
- grist: InlineGristGlobal;
350
+
351
+ /**
352
+ * Useful templates for quick demos and tests.
353
+ */
354
+ declare const presets: {
355
+ /**
356
+ * Default starter fixture for demos / sandboxes that need a neutral document
357
+ * without choosing a domain-specific preset.
358
+ */
359
+ defaultTemplate(): GristReplicaDocument;
360
+ blank(): GristReplicaDocument;
361
+ todoList(): GristReplicaDocument;
362
+ contacts(): GristReplicaDocument;
309
363
  };
310
364
 
311
- export { createInlineTransport as i };
312
- export type { EmulatorDocumentState as E, InlineGristGlobal as I, TransportContext as T, EmulatorEvent as a, EmulatorColumn as b, EmulatorApiBundle as c, EmulatorRowId as d, EmulatorTable as e, EmulatorTransport as f, EventBus as g, InlineTransportOptions as h };
365
+ export { createGristEmulator as g, presets as p };
366
+ export type { CreateGristEmulatorOptions as C, EmulatorColumn as E, GristEmulator as G, InlineGristGlobal as I, EmulatorDocumentState as a, EmulatorApiBundle as b, EmulatorEvent as c, EmulatorRowId as d, EmulatorTable as e, EmulatorTransport as f };
@@ -86,7 +86,7 @@ type GristColumnToMap = {
86
86
  strictType?: boolean;
87
87
  };
88
88
  /** Argument shape for `grist.ready({ columns })`. */
89
- type GristColumnsToMap = Array<string | GristColumnToMap>;
89
+ type GristColumnsToMap = ReadonlyArray<string | GristColumnToMap>;
90
90
  /**
91
91
  * Mapping returned by Grist between logical widget column names and real table column ids.
92
92
  * Single mappings are `string`; `allowMultiple` columns are `string[]`.
@@ -103,12 +103,6 @@ type GristReadyOptions = {
103
103
  allowSelectBy?: boolean;
104
104
  hasCustomOptions?: boolean;
105
105
  onEditOptions?: () => void | Promise<void>;
106
- /** Passed to availability detection (default 10). */
107
- availabilityMaxAttempts?: number;
108
- /** Ms between availability polls (default 1000). */
109
- availabilityIntervalMs?: number;
110
- /** Log when the page is not iframe-embedded (default false). */
111
- logWhenNotEmbedded?: boolean;
112
106
  };
113
107
  /** Optional second argument to `docApi.applyUserActions`. */
114
108
  type GristApplyUserActionsOptions = {
@@ -206,23 +200,5 @@ type GristTableOperations = {
206
200
  upsert: (records: GristTableUpsertRecord | GristTableUpsertRecord[], options?: GristTableUpsertOptions) => Promise<void>;
207
201
  destroy: (recordIds: number | number[]) => Promise<void>;
208
202
  };
209
- /** Subset of `grist.docApi` from https://docs.getgrist.com/grist-plugin-api.js */
210
- type GristDocApi = {
211
- getDocName: () => Promise<string> | string;
212
- listTables: () => Promise<string[]> | string[];
213
- fetchTable: (tableId: string) => Promise<Record<string, unknown[]>>;
214
- applyUserActions: (actions: unknown[][], options?: GristApplyUserActionsOptions) => Promise<unknown>;
215
- /** Present when the widget requested sufficient access (e.g. full). */
216
- getAccessToken?: (options?: {
217
- readOnly?: boolean;
218
- }) => Promise<{
219
- token: string;
220
- baseUrl: string;
221
- }>;
222
- /** Current section's table data (often smaller than {@link fetchTable} for the whole table). */
223
- fetchSelectedTable?: (options?: GristFetchSelectedOptions) => Promise<Record<string, unknown[]>>;
224
- /** Single row in the current section by numeric row id. */
225
- fetchSelectedRecord?: (rowId: number, options?: GristFetchSelectedOptions) => Promise<Record<string, unknown>>;
226
- };
227
203
 
228
- export type { GristRowRecord as G, GristReplicaTable as a, GristReplicaDocument as b, GristReplicaColumn as c, GristWidgetColumnMap as d, GristCursorPos as e, GristWidgetInteraction as f, GristReplicaSelection as g, GristWidgetConfiguration as h, GristReadyOptions as i, GristFetchSelectedOptions as j, GristTableOperations as k, GristFormulaKind as l, GristReplicaRow as m, GristRequiredAccess as n, GristDocumentMode as o, GristApplyUserActionsOptions as p, GristColumnToMap as q, GristColumnsToMap as r, GristDocApi as s, GristMinimalRecord as t, GristTableNewRecord as u, GristTableOpOptions as v, GristTableUpdateRecord as w, GristTableUpsertOptions as x, GristTableUpsertRecord as y, GristWidgetLinkingInfo as z };
204
+ export type { GristReplicaDocument as G, GristReplicaTable as a, GristWidgetColumnMap as b, GristCursorPos as c, GristWidgetInteraction as d, GristReplicaColumn as e, GristRowRecord as f, GristReplicaSelection as g, GristWidgetConfiguration as h, GristReadyOptions as i, GristFetchSelectedOptions as j, GristTableOperations as k, GristRequiredAccess as l, GristDocumentMode as m, GristFormulaKind as n, GristApplyUserActionsOptions as o, GristColumnToMap as p, GristColumnsToMap as q, GristReplicaRow as r };