grist-widget-sdk 0.2.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.
- package/CHANGELOG.md +730 -0
- package/LICENSE +15 -0
- package/README.md +249 -0
- package/dist/advanced.cjs +2749 -0
- package/dist/advanced.cjs.map +1 -0
- package/dist/advanced.d.cts +790 -0
- package/dist/advanced.d.ts +790 -0
- package/dist/advanced.js +825 -0
- package/dist/advanced.js.map +1 -0
- package/dist/chunk-64SVRWW7.js +758 -0
- package/dist/chunk-64SVRWW7.js.map +1 -0
- package/dist/chunk-BNBV24WM.js +479 -0
- package/dist/chunk-BNBV24WM.js.map +1 -0
- package/dist/chunk-ELGVOHFD.js +32 -0
- package/dist/chunk-ELGVOHFD.js.map +1 -0
- package/dist/chunk-GTTD5HPO.js +36 -0
- package/dist/chunk-GTTD5HPO.js.map +1 -0
- package/dist/chunk-JASMM2YC.js +243 -0
- package/dist/chunk-JASMM2YC.js.map +1 -0
- package/dist/chunk-PM3B4WYV.js +12 -0
- package/dist/chunk-PM3B4WYV.js.map +1 -0
- package/dist/chunk-QKXKZ27D.js +1189 -0
- package/dist/chunk-QKXKZ27D.js.map +1 -0
- package/dist/chunk-QZG7SNUG.js +463 -0
- package/dist/chunk-QZG7SNUG.js.map +1 -0
- package/dist/chunk-REV5AUXD.js +25 -0
- package/dist/chunk-REV5AUXD.js.map +1 -0
- package/dist/chunk-XE2SKNLH.js +1641 -0
- package/dist/chunk-XE2SKNLH.js.map +1 -0
- package/dist/create-grist-emulator-C_0I4wax.d.ts +80 -0
- package/dist/create-grist-emulator-DBS6gmiI.d.cts +80 -0
- package/dist/emulator/devtools.cjs +1367 -0
- package/dist/emulator/devtools.cjs.map +1 -0
- package/dist/emulator/devtools.d.cts +46 -0
- package/dist/emulator/devtools.d.ts +46 -0
- package/dist/emulator/devtools.js +480 -0
- package/dist/emulator/devtools.js.map +1 -0
- package/dist/emulator/testing.cjs +2061 -0
- package/dist/emulator/testing.cjs.map +1 -0
- package/dist/emulator/testing.d.cts +50 -0
- package/dist/emulator/testing.d.ts +50 -0
- package/dist/emulator/testing.js +124 -0
- package/dist/emulator/testing.js.map +1 -0
- package/dist/emulator/transports.cjs +1132 -0
- package/dist/emulator/transports.cjs.map +1 -0
- package/dist/emulator/transports.d.cts +37 -0
- package/dist/emulator/transports.d.ts +37 -0
- package/dist/emulator/transports.js +12 -0
- package/dist/emulator/transports.js.map +1 -0
- package/dist/emulator.cjs +1796 -0
- package/dist/emulator.cjs.map +1 -0
- package/dist/emulator.d.cts +107 -0
- package/dist/emulator.d.ts +107 -0
- package/dist/emulator.js +26 -0
- package/dist/emulator.js.map +1 -0
- package/dist/index.cjs +5511 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +825 -0
- package/dist/index.d.ts +825 -0
- package/dist/index.js +2447 -0
- package/dist/index.js.map +1 -0
- package/dist/inline-CRwQQ-GW.d.cts +311 -0
- package/dist/inline-QitZwXKP.d.ts +311 -0
- package/dist/templates-BC7FoSdW.d.ts +24 -0
- package/dist/templates-DBq719O_.d.cts +24 -0
- package/dist/types-Fb2wplkv.d.cts +228 -0
- package/dist/types-Fb2wplkv.d.ts +228 -0
- package/dist/use-grist-widget-options-from-context-BFhQBGRY.d.ts +522 -0
- package/dist/use-grist-widget-options-from-context-qpFMdoBJ.d.cts +522 -0
- package/package.json +131 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { c as GristCursorPos, y as GristWidgetInteraction, w as GristWidgetColumnMap, j as GristReplicaColumn, p as GristRowRecord, m as GristReplicaSelection, x as GristWidgetConfiguration, i as GristReadyOptions, f as GristFetchSelectedOptions, s as GristTableOperations } from './types-Fb2wplkv.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tiny pub-sub used to broadcast every emulator state change to:
|
|
5
|
+
* - the transports (which translate events into protocol messages),
|
|
6
|
+
* - the React DevTools inspector (timeline view),
|
|
7
|
+
* - the testing harness (assertions on emitted events).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
type EmulatorEvent = {
|
|
11
|
+
type: "data-change";
|
|
12
|
+
tableId: string;
|
|
13
|
+
} | {
|
|
14
|
+
type: "cursor-change";
|
|
15
|
+
tableId: string;
|
|
16
|
+
rowId: number | "new" | null;
|
|
17
|
+
cursor?: GristCursorPos;
|
|
18
|
+
} | {
|
|
19
|
+
type: "options-change";
|
|
20
|
+
options: Record<string, unknown>;
|
|
21
|
+
interaction: GristWidgetInteraction;
|
|
22
|
+
} | {
|
|
23
|
+
type: "theme-change";
|
|
24
|
+
message: Record<string, unknown>;
|
|
25
|
+
} | {
|
|
26
|
+
type: "ready";
|
|
27
|
+
configuration: unknown;
|
|
28
|
+
} | {
|
|
29
|
+
type: "user-action";
|
|
30
|
+
action: unknown[];
|
|
31
|
+
} | {
|
|
32
|
+
type: "mappings-change";
|
|
33
|
+
mappings: GristWidgetColumnMap | null;
|
|
34
|
+
};
|
|
35
|
+
type EmulatorEventListener = (event: EmulatorEvent) => void;
|
|
36
|
+
type EventBus = {
|
|
37
|
+
emit: (event: EmulatorEvent) => void;
|
|
38
|
+
on: (listener: EmulatorEventListener) => () => void;
|
|
39
|
+
history: () => readonly EmulatorEvent[];
|
|
40
|
+
clear: () => void;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Core types for the in-memory Grist emulator document model.
|
|
45
|
+
*
|
|
46
|
+
* The emulator is a faithful, in-process re-implementation of Grist's
|
|
47
|
+
* server-side contract — it owns a typed document and derives every event
|
|
48
|
+
* the widget receives (`onRecord`, `onRecords`, `onOptions`, `onNewRecord`)
|
|
49
|
+
* from mutations applied to that document.
|
|
50
|
+
*
|
|
51
|
+
* No DOM, no `window`, no React in this layer.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
type EmulatorRowId = number;
|
|
55
|
+
/**
|
|
56
|
+
* Column metadata for the emulator: {@link GristReplicaColumn} plus in-process
|
|
57
|
+
* `formulaFn` (never serialized in replica JSON).
|
|
58
|
+
*
|
|
59
|
+
* - **`formula`** — original Grist formula text from the replica (when present).
|
|
60
|
+
* - **`inferredFormula`** — emulator-only companion string: explicit value, else mirrors
|
|
61
|
+
* `formula`, else a stable placeholder when only `formulaFn` is provided.
|
|
62
|
+
*/
|
|
63
|
+
type EmulatorColumn = GristReplicaColumn & {
|
|
64
|
+
formulaFn?: (row: GristRowRecord, ctx: EmulatorFormulaContext) => unknown;
|
|
65
|
+
/** Emulator-only; not part of {@link GristReplicaColumn} JSON. See type JSDoc above. */
|
|
66
|
+
inferredFormula?: string;
|
|
67
|
+
};
|
|
68
|
+
type EmulatorFormulaContext = {
|
|
69
|
+
table: EmulatorTable;
|
|
70
|
+
document: EmulatorDocumentState;
|
|
71
|
+
};
|
|
72
|
+
type EmulatorTable = {
|
|
73
|
+
tableId: string;
|
|
74
|
+
/** Optional human-readable label (used by the inspector). */
|
|
75
|
+
label?: string;
|
|
76
|
+
columns: Record<string, EmulatorColumn>;
|
|
77
|
+
rows: GristRowRecord[];
|
|
78
|
+
};
|
|
79
|
+
type EmulatorDocumentState = {
|
|
80
|
+
/** Logical doc name returned by `getDocName()`. */
|
|
81
|
+
docName: string;
|
|
82
|
+
/**
|
|
83
|
+
* `tableId` → `EmulatorTable`. The order of insertion is preserved for
|
|
84
|
+
* `listTables()` (Grist's behaviour).
|
|
85
|
+
*/
|
|
86
|
+
tables: Map<string, EmulatorTable>;
|
|
87
|
+
/** Current widget selection (which table, which row). */
|
|
88
|
+
selection: GristReplicaSelection;
|
|
89
|
+
/** Persisted widget options, exposed via `WidgetAPI.{get,set}Option`. */
|
|
90
|
+
widgetOptions: Record<string, unknown>;
|
|
91
|
+
/**
|
|
92
|
+
* Widget interaction info passed alongside `onOptions`. Lets tests assert
|
|
93
|
+
* `access_level`, theme, etc.
|
|
94
|
+
*/
|
|
95
|
+
widgetInteraction: GristWidgetInteraction;
|
|
96
|
+
/** Last `ready()` payload received from the widget. */
|
|
97
|
+
configuration: GristWidgetConfiguration | null;
|
|
98
|
+
/**
|
|
99
|
+
* Mock REST access token returned by `getAccessToken()`. Set to `null` to
|
|
100
|
+
* simulate unauthenticated widgets (the call will reject).
|
|
101
|
+
*/
|
|
102
|
+
accessToken: {
|
|
103
|
+
token: string;
|
|
104
|
+
baseUrl: string;
|
|
105
|
+
ttlMsecs: number;
|
|
106
|
+
} | null;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Type-only signatures of the Grist plugin interfaces that the emulator
|
|
111
|
+
* exposes to the widget (under both inline and iframe transports).
|
|
112
|
+
*
|
|
113
|
+
* These mirror the upstream `grist-plugin-api` contracts. We re-declare them
|
|
114
|
+
* here instead of `import`ing the upstream types because:
|
|
115
|
+
* - we don't depend on `grist-plugin-api` runtime,
|
|
116
|
+
* - the emulator is the single source of truth for what's testable.
|
|
117
|
+
*/
|
|
118
|
+
type GristViewImpl = {
|
|
119
|
+
/**
|
|
120
|
+
* Always returns Grist's **columnar** wire shape. The `format` option is
|
|
121
|
+
* accepted for protocol fidelity (the production `grist-plugin-api.js`
|
|
122
|
+
* always forwards it) but the host stub MUST ignore it: the wrapper inside
|
|
123
|
+
* the plugin script is the one that does the rows transposition. See
|
|
124
|
+
* `api/grist-view.ts` and `transports/inline.ts` for the consumer side.
|
|
125
|
+
*/
|
|
126
|
+
fetchSelectedTable: (options?: {
|
|
127
|
+
keepEncoded?: boolean;
|
|
128
|
+
format?: "rows" | "columns";
|
|
129
|
+
}) => Promise<Record<string, unknown[]>>;
|
|
130
|
+
fetchSelectedRecord: (rowId: number, options?: {
|
|
131
|
+
keepEncoded?: boolean;
|
|
132
|
+
}) => Promise<Record<string, unknown>>;
|
|
133
|
+
allowSelectBy: () => Promise<boolean>;
|
|
134
|
+
setSelectedRows: (rowIds: number[] | null) => Promise<void>;
|
|
135
|
+
setCursorPos: (cursor: {
|
|
136
|
+
rowId?: number;
|
|
137
|
+
field?: string;
|
|
138
|
+
sectionId?: number;
|
|
139
|
+
}) => Promise<void>;
|
|
140
|
+
};
|
|
141
|
+
type GristDocApiImpl = {
|
|
142
|
+
getDocName: () => Promise<string>;
|
|
143
|
+
listTables: () => Promise<string[]>;
|
|
144
|
+
fetchTable: (tableId: string) => Promise<Record<string, unknown[]>>;
|
|
145
|
+
applyUserActions: (actions: unknown[][], options?: {
|
|
146
|
+
desc?: string;
|
|
147
|
+
}) => Promise<{
|
|
148
|
+
retValues: unknown[];
|
|
149
|
+
}>;
|
|
150
|
+
getAccessToken: (options?: {
|
|
151
|
+
readOnly?: boolean;
|
|
152
|
+
}) => Promise<{
|
|
153
|
+
token: string;
|
|
154
|
+
baseUrl: string;
|
|
155
|
+
ttlMsecs: number;
|
|
156
|
+
}>;
|
|
157
|
+
};
|
|
158
|
+
type WidgetApiImpl = {
|
|
159
|
+
getOption: (key: string) => Promise<unknown>;
|
|
160
|
+
setOption: (key: string, value: unknown) => Promise<void>;
|
|
161
|
+
getOptions: () => Promise<Record<string, unknown> | null>;
|
|
162
|
+
setOptions: (options: Record<string, unknown>) => Promise<void>;
|
|
163
|
+
clearOptions: () => Promise<void>;
|
|
164
|
+
};
|
|
165
|
+
type CustomSectionApiImpl = {
|
|
166
|
+
configure: (settings: Record<string, unknown>) => Promise<void>;
|
|
167
|
+
mappings: () => Promise<Record<string, string | string[] | undefined> | null>;
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* Top-level Grist plugin RPC (the `_grist_api` interface). Used by the
|
|
171
|
+
* widget to subscribe to events such as "onMessage" relays. Keeps a minimal
|
|
172
|
+
* surface in v1 — extend as widget needs grow.
|
|
173
|
+
*/
|
|
174
|
+
type GristApiImpl = {
|
|
175
|
+
/** No-op echo helpful for protocol-level health checks. */
|
|
176
|
+
ping: () => Promise<"pong">;
|
|
177
|
+
};
|
|
178
|
+
type CommandApiImpl = {
|
|
179
|
+
/** Runs a registered command by name. The emulator currently no-ops. */
|
|
180
|
+
run: (name: string) => Promise<void>;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Bundles the six host-side Grist API implementations into one record so
|
|
185
|
+
* transports (iframe, inline) can wire them up uniformly.
|
|
186
|
+
*/
|
|
187
|
+
|
|
188
|
+
type EmulatorApiBundle = {
|
|
189
|
+
GristView: GristViewImpl;
|
|
190
|
+
GristDocAPI: GristDocApiImpl;
|
|
191
|
+
WidgetAPI: WidgetApiImpl;
|
|
192
|
+
CustomSectionAPI: CustomSectionApiImpl;
|
|
193
|
+
GristAPI: GristApiImpl;
|
|
194
|
+
CommandAPI: CommandApiImpl;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
type EmulatorTransport = {
|
|
198
|
+
/** Tear the transport down. Idempotent. */
|
|
199
|
+
dispose: () => void;
|
|
200
|
+
/** Push a "data refreshed" hint to the widget. */
|
|
201
|
+
fireDataChange: (tableId?: string) => void;
|
|
202
|
+
/** Push a cursor change to the widget (number rowId, "new", or null). */
|
|
203
|
+
fireCursor: (rowId?: number | "new" | null) => void;
|
|
204
|
+
/** Push the current options + interaction to the widget. */
|
|
205
|
+
fireOptions: () => void;
|
|
206
|
+
/** Push a host theme message (`grist.on("message")` / `msg.theme`). */
|
|
207
|
+
fireTheme: (message: Record<string, unknown>) => void;
|
|
208
|
+
/** Replays the post-ready bootstrap (options + cursor + data) as if the widget had just connected. */
|
|
209
|
+
emitInitialState: () => void;
|
|
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
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Inline transport — synthesises a `window.grist` global wired directly to
|
|
234
|
+
* the emulator state. No iframe, no postMessage, no serialization.
|
|
235
|
+
*
|
|
236
|
+
* Used for:
|
|
237
|
+
* - SDK unit tests in jsdom (`renderWithGrist(...)`),
|
|
238
|
+
* - IDE-style live previews where widget code lives in the same window
|
|
239
|
+
* as the emulator host.
|
|
240
|
+
*
|
|
241
|
+
* The synthesised `grist` object exposes the **same** typed surface as the
|
|
242
|
+
* production `grist-plugin-api.js` (see `hooks/grist-global.d.ts`). Hooks
|
|
243
|
+
* call `grist.docApi.fetchTable(...)` etc. and get the answer directly from
|
|
244
|
+
* the action engine.
|
|
245
|
+
*/
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Shape of the synthesised global. Matches `globalThis.grist` from
|
|
249
|
+
* `grist-global.d.ts`.
|
|
250
|
+
*/
|
|
251
|
+
type InlineGristGlobal = {
|
|
252
|
+
ready: (options?: GristReadyOptions) => Promise<void>;
|
|
253
|
+
onRecord: (cb: (record: Record<string, unknown> | null, mappings?: GristWidgetColumnMap | null) => void, options?: GristFetchSelectedOptions) => void;
|
|
254
|
+
onRecords: (cb: (records: Record<string, unknown>[], mappings?: GristWidgetColumnMap | null) => void, options?: GristFetchSelectedOptions) => void;
|
|
255
|
+
onOptions: (cb: (options: Record<string, unknown> | null, interaction: GristWidgetInteraction) => void) => void;
|
|
256
|
+
onNewRecord: (cb: (mappings: GristWidgetColumnMap | null) => void) => void;
|
|
257
|
+
mapColumnNames: (data: Record<string, unknown>) => Record<string, unknown> | null;
|
|
258
|
+
mapColumnNamesBack: (data: Record<string, unknown>) => Record<string, unknown>;
|
|
259
|
+
getOptions: () => Promise<Record<string, unknown> | null>;
|
|
260
|
+
getOption: (key: string) => Promise<unknown>;
|
|
261
|
+
setOption: (key: string, value: unknown) => Promise<void>;
|
|
262
|
+
setOptions: (options: Record<string, unknown>) => Promise<void>;
|
|
263
|
+
clearOptions: () => Promise<void>;
|
|
264
|
+
setCursorPos: (pos: GristCursorPos) => Promise<void>;
|
|
265
|
+
setSelectedRows: (rowIds: number[] | null) => Promise<void>;
|
|
266
|
+
fetchSelectedTable: (options?: GristFetchSelectedOptions) => Promise<Record<string, unknown[]>>;
|
|
267
|
+
fetchSelectedRecord: (rowId: number, options?: GristFetchSelectedOptions) => Promise<Record<string, unknown>>;
|
|
268
|
+
docApi: {
|
|
269
|
+
getDocName: () => Promise<string>;
|
|
270
|
+
listTables: () => Promise<string[]>;
|
|
271
|
+
fetchTable: (tableId: string) => Promise<Record<string, unknown[]>>;
|
|
272
|
+
applyUserActions: (actions: unknown[][], options?: {
|
|
273
|
+
desc?: string;
|
|
274
|
+
}) => Promise<{
|
|
275
|
+
retValues: unknown[];
|
|
276
|
+
}>;
|
|
277
|
+
getAccessToken?: (options?: {
|
|
278
|
+
readOnly?: boolean;
|
|
279
|
+
}) => Promise<{
|
|
280
|
+
token: string;
|
|
281
|
+
baseUrl: string;
|
|
282
|
+
}>;
|
|
283
|
+
fetchSelectedTable: (options?: GristFetchSelectedOptions) => Promise<Record<string, unknown[]>>;
|
|
284
|
+
fetchSelectedRecord: (rowId: number, options?: GristFetchSelectedOptions) => Promise<Record<string, unknown>>;
|
|
285
|
+
};
|
|
286
|
+
selectedTable: GristTableOperations;
|
|
287
|
+
getTable: (tableId?: string) => GristTableOperations;
|
|
288
|
+
sectionApi: {
|
|
289
|
+
configure: (settings: GristReadyOptions) => Promise<void>;
|
|
290
|
+
mappings: () => Promise<GristWidgetColumnMap | null>;
|
|
291
|
+
};
|
|
292
|
+
on: (eventName: string, listener: (...args: unknown[]) => void) => () => void;
|
|
293
|
+
};
|
|
294
|
+
type InlineTransportOptions = TransportContext & {
|
|
295
|
+
/**
|
|
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.
|
|
299
|
+
*/
|
|
300
|
+
target?: Window & typeof globalThis;
|
|
301
|
+
};
|
|
302
|
+
/**
|
|
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.
|
|
306
|
+
*/
|
|
307
|
+
declare function createInlineTransport(options: InlineTransportOptions): EmulatorTransport & {
|
|
308
|
+
grist: InlineGristGlobal;
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
export { type EmulatorApiBundle as E, type InlineGristGlobal as I, type TransportContext as T, type EmulatorColumn as a, type EmulatorDocumentState as b, type EmulatorEvent as c, type EmulatorRowId as d, type EmulatorTable as e, type EmulatorTransport as f, type EventBus as g, type InlineTransportOptions as h, createInlineTransport as i };
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { c as GristCursorPos, y as GristWidgetInteraction, w as GristWidgetColumnMap, j as GristReplicaColumn, p as GristRowRecord, m as GristReplicaSelection, x as GristWidgetConfiguration, i as GristReadyOptions, f as GristFetchSelectedOptions, s as GristTableOperations } from './types-Fb2wplkv.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tiny pub-sub used to broadcast every emulator state change to:
|
|
5
|
+
* - the transports (which translate events into protocol messages),
|
|
6
|
+
* - the React DevTools inspector (timeline view),
|
|
7
|
+
* - the testing harness (assertions on emitted events).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
type EmulatorEvent = {
|
|
11
|
+
type: "data-change";
|
|
12
|
+
tableId: string;
|
|
13
|
+
} | {
|
|
14
|
+
type: "cursor-change";
|
|
15
|
+
tableId: string;
|
|
16
|
+
rowId: number | "new" | null;
|
|
17
|
+
cursor?: GristCursorPos;
|
|
18
|
+
} | {
|
|
19
|
+
type: "options-change";
|
|
20
|
+
options: Record<string, unknown>;
|
|
21
|
+
interaction: GristWidgetInteraction;
|
|
22
|
+
} | {
|
|
23
|
+
type: "theme-change";
|
|
24
|
+
message: Record<string, unknown>;
|
|
25
|
+
} | {
|
|
26
|
+
type: "ready";
|
|
27
|
+
configuration: unknown;
|
|
28
|
+
} | {
|
|
29
|
+
type: "user-action";
|
|
30
|
+
action: unknown[];
|
|
31
|
+
} | {
|
|
32
|
+
type: "mappings-change";
|
|
33
|
+
mappings: GristWidgetColumnMap | null;
|
|
34
|
+
};
|
|
35
|
+
type EmulatorEventListener = (event: EmulatorEvent) => void;
|
|
36
|
+
type EventBus = {
|
|
37
|
+
emit: (event: EmulatorEvent) => void;
|
|
38
|
+
on: (listener: EmulatorEventListener) => () => void;
|
|
39
|
+
history: () => readonly EmulatorEvent[];
|
|
40
|
+
clear: () => void;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Core types for the in-memory Grist emulator document model.
|
|
45
|
+
*
|
|
46
|
+
* The emulator is a faithful, in-process re-implementation of Grist's
|
|
47
|
+
* server-side contract — it owns a typed document and derives every event
|
|
48
|
+
* the widget receives (`onRecord`, `onRecords`, `onOptions`, `onNewRecord`)
|
|
49
|
+
* from mutations applied to that document.
|
|
50
|
+
*
|
|
51
|
+
* No DOM, no `window`, no React in this layer.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
type EmulatorRowId = number;
|
|
55
|
+
/**
|
|
56
|
+
* Column metadata for the emulator: {@link GristReplicaColumn} plus in-process
|
|
57
|
+
* `formulaFn` (never serialized in replica JSON).
|
|
58
|
+
*
|
|
59
|
+
* - **`formula`** — original Grist formula text from the replica (when present).
|
|
60
|
+
* - **`inferredFormula`** — emulator-only companion string: explicit value, else mirrors
|
|
61
|
+
* `formula`, else a stable placeholder when only `formulaFn` is provided.
|
|
62
|
+
*/
|
|
63
|
+
type EmulatorColumn = GristReplicaColumn & {
|
|
64
|
+
formulaFn?: (row: GristRowRecord, ctx: EmulatorFormulaContext) => unknown;
|
|
65
|
+
/** Emulator-only; not part of {@link GristReplicaColumn} JSON. See type JSDoc above. */
|
|
66
|
+
inferredFormula?: string;
|
|
67
|
+
};
|
|
68
|
+
type EmulatorFormulaContext = {
|
|
69
|
+
table: EmulatorTable;
|
|
70
|
+
document: EmulatorDocumentState;
|
|
71
|
+
};
|
|
72
|
+
type EmulatorTable = {
|
|
73
|
+
tableId: string;
|
|
74
|
+
/** Optional human-readable label (used by the inspector). */
|
|
75
|
+
label?: string;
|
|
76
|
+
columns: Record<string, EmulatorColumn>;
|
|
77
|
+
rows: GristRowRecord[];
|
|
78
|
+
};
|
|
79
|
+
type EmulatorDocumentState = {
|
|
80
|
+
/** Logical doc name returned by `getDocName()`. */
|
|
81
|
+
docName: string;
|
|
82
|
+
/**
|
|
83
|
+
* `tableId` → `EmulatorTable`. The order of insertion is preserved for
|
|
84
|
+
* `listTables()` (Grist's behaviour).
|
|
85
|
+
*/
|
|
86
|
+
tables: Map<string, EmulatorTable>;
|
|
87
|
+
/** Current widget selection (which table, which row). */
|
|
88
|
+
selection: GristReplicaSelection;
|
|
89
|
+
/** Persisted widget options, exposed via `WidgetAPI.{get,set}Option`. */
|
|
90
|
+
widgetOptions: Record<string, unknown>;
|
|
91
|
+
/**
|
|
92
|
+
* Widget interaction info passed alongside `onOptions`. Lets tests assert
|
|
93
|
+
* `access_level`, theme, etc.
|
|
94
|
+
*/
|
|
95
|
+
widgetInteraction: GristWidgetInteraction;
|
|
96
|
+
/** Last `ready()` payload received from the widget. */
|
|
97
|
+
configuration: GristWidgetConfiguration | null;
|
|
98
|
+
/**
|
|
99
|
+
* Mock REST access token returned by `getAccessToken()`. Set to `null` to
|
|
100
|
+
* simulate unauthenticated widgets (the call will reject).
|
|
101
|
+
*/
|
|
102
|
+
accessToken: {
|
|
103
|
+
token: string;
|
|
104
|
+
baseUrl: string;
|
|
105
|
+
ttlMsecs: number;
|
|
106
|
+
} | null;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Type-only signatures of the Grist plugin interfaces that the emulator
|
|
111
|
+
* exposes to the widget (under both inline and iframe transports).
|
|
112
|
+
*
|
|
113
|
+
* These mirror the upstream `grist-plugin-api` contracts. We re-declare them
|
|
114
|
+
* here instead of `import`ing the upstream types because:
|
|
115
|
+
* - we don't depend on `grist-plugin-api` runtime,
|
|
116
|
+
* - the emulator is the single source of truth for what's testable.
|
|
117
|
+
*/
|
|
118
|
+
type GristViewImpl = {
|
|
119
|
+
/**
|
|
120
|
+
* Always returns Grist's **columnar** wire shape. The `format` option is
|
|
121
|
+
* accepted for protocol fidelity (the production `grist-plugin-api.js`
|
|
122
|
+
* always forwards it) but the host stub MUST ignore it: the wrapper inside
|
|
123
|
+
* the plugin script is the one that does the rows transposition. See
|
|
124
|
+
* `api/grist-view.ts` and `transports/inline.ts` for the consumer side.
|
|
125
|
+
*/
|
|
126
|
+
fetchSelectedTable: (options?: {
|
|
127
|
+
keepEncoded?: boolean;
|
|
128
|
+
format?: "rows" | "columns";
|
|
129
|
+
}) => Promise<Record<string, unknown[]>>;
|
|
130
|
+
fetchSelectedRecord: (rowId: number, options?: {
|
|
131
|
+
keepEncoded?: boolean;
|
|
132
|
+
}) => Promise<Record<string, unknown>>;
|
|
133
|
+
allowSelectBy: () => Promise<boolean>;
|
|
134
|
+
setSelectedRows: (rowIds: number[] | null) => Promise<void>;
|
|
135
|
+
setCursorPos: (cursor: {
|
|
136
|
+
rowId?: number;
|
|
137
|
+
field?: string;
|
|
138
|
+
sectionId?: number;
|
|
139
|
+
}) => Promise<void>;
|
|
140
|
+
};
|
|
141
|
+
type GristDocApiImpl = {
|
|
142
|
+
getDocName: () => Promise<string>;
|
|
143
|
+
listTables: () => Promise<string[]>;
|
|
144
|
+
fetchTable: (tableId: string) => Promise<Record<string, unknown[]>>;
|
|
145
|
+
applyUserActions: (actions: unknown[][], options?: {
|
|
146
|
+
desc?: string;
|
|
147
|
+
}) => Promise<{
|
|
148
|
+
retValues: unknown[];
|
|
149
|
+
}>;
|
|
150
|
+
getAccessToken: (options?: {
|
|
151
|
+
readOnly?: boolean;
|
|
152
|
+
}) => Promise<{
|
|
153
|
+
token: string;
|
|
154
|
+
baseUrl: string;
|
|
155
|
+
ttlMsecs: number;
|
|
156
|
+
}>;
|
|
157
|
+
};
|
|
158
|
+
type WidgetApiImpl = {
|
|
159
|
+
getOption: (key: string) => Promise<unknown>;
|
|
160
|
+
setOption: (key: string, value: unknown) => Promise<void>;
|
|
161
|
+
getOptions: () => Promise<Record<string, unknown> | null>;
|
|
162
|
+
setOptions: (options: Record<string, unknown>) => Promise<void>;
|
|
163
|
+
clearOptions: () => Promise<void>;
|
|
164
|
+
};
|
|
165
|
+
type CustomSectionApiImpl = {
|
|
166
|
+
configure: (settings: Record<string, unknown>) => Promise<void>;
|
|
167
|
+
mappings: () => Promise<Record<string, string | string[] | undefined> | null>;
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* Top-level Grist plugin RPC (the `_grist_api` interface). Used by the
|
|
171
|
+
* widget to subscribe to events such as "onMessage" relays. Keeps a minimal
|
|
172
|
+
* surface in v1 — extend as widget needs grow.
|
|
173
|
+
*/
|
|
174
|
+
type GristApiImpl = {
|
|
175
|
+
/** No-op echo helpful for protocol-level health checks. */
|
|
176
|
+
ping: () => Promise<"pong">;
|
|
177
|
+
};
|
|
178
|
+
type CommandApiImpl = {
|
|
179
|
+
/** Runs a registered command by name. The emulator currently no-ops. */
|
|
180
|
+
run: (name: string) => Promise<void>;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Bundles the six host-side Grist API implementations into one record so
|
|
185
|
+
* transports (iframe, inline) can wire them up uniformly.
|
|
186
|
+
*/
|
|
187
|
+
|
|
188
|
+
type EmulatorApiBundle = {
|
|
189
|
+
GristView: GristViewImpl;
|
|
190
|
+
GristDocAPI: GristDocApiImpl;
|
|
191
|
+
WidgetAPI: WidgetApiImpl;
|
|
192
|
+
CustomSectionAPI: CustomSectionApiImpl;
|
|
193
|
+
GristAPI: GristApiImpl;
|
|
194
|
+
CommandAPI: CommandApiImpl;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
type EmulatorTransport = {
|
|
198
|
+
/** Tear the transport down. Idempotent. */
|
|
199
|
+
dispose: () => void;
|
|
200
|
+
/** Push a "data refreshed" hint to the widget. */
|
|
201
|
+
fireDataChange: (tableId?: string) => void;
|
|
202
|
+
/** Push a cursor change to the widget (number rowId, "new", or null). */
|
|
203
|
+
fireCursor: (rowId?: number | "new" | null) => void;
|
|
204
|
+
/** Push the current options + interaction to the widget. */
|
|
205
|
+
fireOptions: () => void;
|
|
206
|
+
/** Push a host theme message (`grist.on("message")` / `msg.theme`). */
|
|
207
|
+
fireTheme: (message: Record<string, unknown>) => void;
|
|
208
|
+
/** Replays the post-ready bootstrap (options + cursor + data) as if the widget had just connected. */
|
|
209
|
+
emitInitialState: () => void;
|
|
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
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Inline transport — synthesises a `window.grist` global wired directly to
|
|
234
|
+
* the emulator state. No iframe, no postMessage, no serialization.
|
|
235
|
+
*
|
|
236
|
+
* Used for:
|
|
237
|
+
* - SDK unit tests in jsdom (`renderWithGrist(...)`),
|
|
238
|
+
* - IDE-style live previews where widget code lives in the same window
|
|
239
|
+
* as the emulator host.
|
|
240
|
+
*
|
|
241
|
+
* The synthesised `grist` object exposes the **same** typed surface as the
|
|
242
|
+
* production `grist-plugin-api.js` (see `hooks/grist-global.d.ts`). Hooks
|
|
243
|
+
* call `grist.docApi.fetchTable(...)` etc. and get the answer directly from
|
|
244
|
+
* the action engine.
|
|
245
|
+
*/
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Shape of the synthesised global. Matches `globalThis.grist` from
|
|
249
|
+
* `grist-global.d.ts`.
|
|
250
|
+
*/
|
|
251
|
+
type InlineGristGlobal = {
|
|
252
|
+
ready: (options?: GristReadyOptions) => Promise<void>;
|
|
253
|
+
onRecord: (cb: (record: Record<string, unknown> | null, mappings?: GristWidgetColumnMap | null) => void, options?: GristFetchSelectedOptions) => void;
|
|
254
|
+
onRecords: (cb: (records: Record<string, unknown>[], mappings?: GristWidgetColumnMap | null) => void, options?: GristFetchSelectedOptions) => void;
|
|
255
|
+
onOptions: (cb: (options: Record<string, unknown> | null, interaction: GristWidgetInteraction) => void) => void;
|
|
256
|
+
onNewRecord: (cb: (mappings: GristWidgetColumnMap | null) => void) => void;
|
|
257
|
+
mapColumnNames: (data: Record<string, unknown>) => Record<string, unknown> | null;
|
|
258
|
+
mapColumnNamesBack: (data: Record<string, unknown>) => Record<string, unknown>;
|
|
259
|
+
getOptions: () => Promise<Record<string, unknown> | null>;
|
|
260
|
+
getOption: (key: string) => Promise<unknown>;
|
|
261
|
+
setOption: (key: string, value: unknown) => Promise<void>;
|
|
262
|
+
setOptions: (options: Record<string, unknown>) => Promise<void>;
|
|
263
|
+
clearOptions: () => Promise<void>;
|
|
264
|
+
setCursorPos: (pos: GristCursorPos) => Promise<void>;
|
|
265
|
+
setSelectedRows: (rowIds: number[] | null) => Promise<void>;
|
|
266
|
+
fetchSelectedTable: (options?: GristFetchSelectedOptions) => Promise<Record<string, unknown[]>>;
|
|
267
|
+
fetchSelectedRecord: (rowId: number, options?: GristFetchSelectedOptions) => Promise<Record<string, unknown>>;
|
|
268
|
+
docApi: {
|
|
269
|
+
getDocName: () => Promise<string>;
|
|
270
|
+
listTables: () => Promise<string[]>;
|
|
271
|
+
fetchTable: (tableId: string) => Promise<Record<string, unknown[]>>;
|
|
272
|
+
applyUserActions: (actions: unknown[][], options?: {
|
|
273
|
+
desc?: string;
|
|
274
|
+
}) => Promise<{
|
|
275
|
+
retValues: unknown[];
|
|
276
|
+
}>;
|
|
277
|
+
getAccessToken?: (options?: {
|
|
278
|
+
readOnly?: boolean;
|
|
279
|
+
}) => Promise<{
|
|
280
|
+
token: string;
|
|
281
|
+
baseUrl: string;
|
|
282
|
+
}>;
|
|
283
|
+
fetchSelectedTable: (options?: GristFetchSelectedOptions) => Promise<Record<string, unknown[]>>;
|
|
284
|
+
fetchSelectedRecord: (rowId: number, options?: GristFetchSelectedOptions) => Promise<Record<string, unknown>>;
|
|
285
|
+
};
|
|
286
|
+
selectedTable: GristTableOperations;
|
|
287
|
+
getTable: (tableId?: string) => GristTableOperations;
|
|
288
|
+
sectionApi: {
|
|
289
|
+
configure: (settings: GristReadyOptions) => Promise<void>;
|
|
290
|
+
mappings: () => Promise<GristWidgetColumnMap | null>;
|
|
291
|
+
};
|
|
292
|
+
on: (eventName: string, listener: (...args: unknown[]) => void) => () => void;
|
|
293
|
+
};
|
|
294
|
+
type InlineTransportOptions = TransportContext & {
|
|
295
|
+
/**
|
|
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.
|
|
299
|
+
*/
|
|
300
|
+
target?: Window & typeof globalThis;
|
|
301
|
+
};
|
|
302
|
+
/**
|
|
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.
|
|
306
|
+
*/
|
|
307
|
+
declare function createInlineTransport(options: InlineTransportOptions): EmulatorTransport & {
|
|
308
|
+
grist: InlineGristGlobal;
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
export { type EmulatorApiBundle as E, type InlineGristGlobal as I, type TransportContext as T, type EmulatorColumn as a, type EmulatorDocumentState as b, type EmulatorEvent as c, type EmulatorRowId as d, type EmulatorTable as e, type EmulatorTransport as f, type EventBus as g, type InlineTransportOptions as h, createInlineTransport as i };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { k as GristReplicaDocument } from './types-Fb2wplkv.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Document templates exposed at the top of the emulator API.
|
|
5
|
+
*
|
|
6
|
+
* Templates return plain data — they don't allocate state, the caller passes
|
|
7
|
+
* the returned value into `createGristEmulator(...)` which performs the allocation.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Useful templates for quick demos and tests.
|
|
12
|
+
*/
|
|
13
|
+
declare const presets: {
|
|
14
|
+
/**
|
|
15
|
+
* Default starter fixture for demos / sandboxes that need a neutral document
|
|
16
|
+
* without choosing a domain-specific preset.
|
|
17
|
+
*/
|
|
18
|
+
defaultTemplate(): GristReplicaDocument;
|
|
19
|
+
blank(): GristReplicaDocument;
|
|
20
|
+
todoList(): GristReplicaDocument;
|
|
21
|
+
contacts(): GristReplicaDocument;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export { presets as p };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { k as GristReplicaDocument } from './types-Fb2wplkv.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Document templates exposed at the top of the emulator API.
|
|
5
|
+
*
|
|
6
|
+
* Templates return plain data — they don't allocate state, the caller passes
|
|
7
|
+
* the returned value into `createGristEmulator(...)` which performs the allocation.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Useful templates for quick demos and tests.
|
|
12
|
+
*/
|
|
13
|
+
declare const presets: {
|
|
14
|
+
/**
|
|
15
|
+
* Default starter fixture for demos / sandboxes that need a neutral document
|
|
16
|
+
* without choosing a domain-specific preset.
|
|
17
|
+
*/
|
|
18
|
+
defaultTemplate(): GristReplicaDocument;
|
|
19
|
+
blank(): GristReplicaDocument;
|
|
20
|
+
todoList(): GristReplicaDocument;
|
|
21
|
+
contacts(): GristReplicaDocument;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export { presets as p };
|