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.
Files changed (70) hide show
  1. package/CHANGELOG.md +730 -0
  2. package/LICENSE +15 -0
  3. package/README.md +249 -0
  4. package/dist/advanced.cjs +2749 -0
  5. package/dist/advanced.cjs.map +1 -0
  6. package/dist/advanced.d.cts +790 -0
  7. package/dist/advanced.d.ts +790 -0
  8. package/dist/advanced.js +825 -0
  9. package/dist/advanced.js.map +1 -0
  10. package/dist/chunk-64SVRWW7.js +758 -0
  11. package/dist/chunk-64SVRWW7.js.map +1 -0
  12. package/dist/chunk-BNBV24WM.js +479 -0
  13. package/dist/chunk-BNBV24WM.js.map +1 -0
  14. package/dist/chunk-ELGVOHFD.js +32 -0
  15. package/dist/chunk-ELGVOHFD.js.map +1 -0
  16. package/dist/chunk-GTTD5HPO.js +36 -0
  17. package/dist/chunk-GTTD5HPO.js.map +1 -0
  18. package/dist/chunk-JASMM2YC.js +243 -0
  19. package/dist/chunk-JASMM2YC.js.map +1 -0
  20. package/dist/chunk-PM3B4WYV.js +12 -0
  21. package/dist/chunk-PM3B4WYV.js.map +1 -0
  22. package/dist/chunk-QKXKZ27D.js +1189 -0
  23. package/dist/chunk-QKXKZ27D.js.map +1 -0
  24. package/dist/chunk-QZG7SNUG.js +463 -0
  25. package/dist/chunk-QZG7SNUG.js.map +1 -0
  26. package/dist/chunk-REV5AUXD.js +25 -0
  27. package/dist/chunk-REV5AUXD.js.map +1 -0
  28. package/dist/chunk-XE2SKNLH.js +1641 -0
  29. package/dist/chunk-XE2SKNLH.js.map +1 -0
  30. package/dist/create-grist-emulator-C_0I4wax.d.ts +80 -0
  31. package/dist/create-grist-emulator-DBS6gmiI.d.cts +80 -0
  32. package/dist/emulator/devtools.cjs +1367 -0
  33. package/dist/emulator/devtools.cjs.map +1 -0
  34. package/dist/emulator/devtools.d.cts +46 -0
  35. package/dist/emulator/devtools.d.ts +46 -0
  36. package/dist/emulator/devtools.js +480 -0
  37. package/dist/emulator/devtools.js.map +1 -0
  38. package/dist/emulator/testing.cjs +2061 -0
  39. package/dist/emulator/testing.cjs.map +1 -0
  40. package/dist/emulator/testing.d.cts +50 -0
  41. package/dist/emulator/testing.d.ts +50 -0
  42. package/dist/emulator/testing.js +124 -0
  43. package/dist/emulator/testing.js.map +1 -0
  44. package/dist/emulator/transports.cjs +1132 -0
  45. package/dist/emulator/transports.cjs.map +1 -0
  46. package/dist/emulator/transports.d.cts +37 -0
  47. package/dist/emulator/transports.d.ts +37 -0
  48. package/dist/emulator/transports.js +12 -0
  49. package/dist/emulator/transports.js.map +1 -0
  50. package/dist/emulator.cjs +1796 -0
  51. package/dist/emulator.cjs.map +1 -0
  52. package/dist/emulator.d.cts +107 -0
  53. package/dist/emulator.d.ts +107 -0
  54. package/dist/emulator.js +26 -0
  55. package/dist/emulator.js.map +1 -0
  56. package/dist/index.cjs +5511 -0
  57. package/dist/index.cjs.map +1 -0
  58. package/dist/index.d.cts +825 -0
  59. package/dist/index.d.ts +825 -0
  60. package/dist/index.js +2447 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/inline-CRwQQ-GW.d.cts +311 -0
  63. package/dist/inline-QitZwXKP.d.ts +311 -0
  64. package/dist/templates-BC7FoSdW.d.ts +24 -0
  65. package/dist/templates-DBq719O_.d.cts +24 -0
  66. package/dist/types-Fb2wplkv.d.cts +228 -0
  67. package/dist/types-Fb2wplkv.d.ts +228 -0
  68. package/dist/use-grist-widget-options-from-context-BFhQBGRY.d.ts +522 -0
  69. package/dist/use-grist-widget-options-from-context-qpFMdoBJ.d.cts +522 -0
  70. package/package.json +131 -0
@@ -0,0 +1,1189 @@
1
+ import {
2
+ normalizeReplicaDocumentInput
3
+ } from "./chunk-GTTD5HPO.js";
4
+ import {
5
+ resolveMappings
6
+ } from "./chunk-PM3B4WYV.js";
7
+ import {
8
+ coerceGristDocApiRefRowId,
9
+ gristAddRecordAction,
10
+ gristBulkAddRecordAction,
11
+ gristBulkRemoveRecordAction,
12
+ gristBulkUpdateRecordAction,
13
+ gristRemoveRecordAction,
14
+ gristUpdateRecordAction,
15
+ inferDisplayHelpersByRef
16
+ } from "./chunk-64SVRWW7.js";
17
+
18
+ // src/emulator/transports/iframe.ts
19
+ import { MsgType, Rpc } from "grain-rpc";
20
+
21
+ // src/emulator/theme-message.ts
22
+ function buildGristThemeHostMessage(appearance, options) {
23
+ return {
24
+ theme: {
25
+ appearance,
26
+ name: appearance,
27
+ colors: {}
28
+ },
29
+ ...options?.fromReady ? { fromReady: true } : {}
30
+ };
31
+ }
32
+
33
+ // src/emulator/transports/iframe.ts
34
+ function createIframeTransport(options) {
35
+ const { iframe, state, bus, api, targetOrigin = "*" } = options;
36
+ const timers = /* @__PURE__ */ new Set();
37
+ const rpc = new Rpc({
38
+ sendMessage: (msg) => {
39
+ const target = iframe.contentWindow;
40
+ if (!target) return;
41
+ target.postMessage(msg, targetOrigin);
42
+ },
43
+ logger: { info: () => {
44
+ }, warn: () => {
45
+ } }
46
+ });
47
+ rpc.queueOutgoingUntilReadyMessage();
48
+ const receiveFromWidget = rpc.receiveMessage.bind(rpc);
49
+ rpc.receiveMessage = (msg) => {
50
+ if (msg && typeof msg === "object" && msg.mtype === MsgType.RpcCall && msg.mdest === "grist") {
51
+ const copy = { ...msg };
52
+ delete copy.mdest;
53
+ receiveFromWidget(copy);
54
+ return;
55
+ }
56
+ receiveFromWidget(msg);
57
+ };
58
+ const onMessage = (event) => {
59
+ if (event.source !== iframe.contentWindow) return;
60
+ if (!isRpcEnvelope(event.data)) return;
61
+ rpc.receiveMessage(event.data);
62
+ };
63
+ window.addEventListener("message", onMessage);
64
+ rpc.registerImpl("GristView", api.GristView);
65
+ rpc.registerImpl("GristDocAPI", api.GristDocAPI);
66
+ rpc.registerImpl("WidgetAPI", api.WidgetAPI);
67
+ rpc.registerImpl("CustomSectionAPI", api.CustomSectionAPI);
68
+ rpc.registerImpl("_grist_api", api.GristAPI);
69
+ rpc.registerImpl("CommandAPI", api.CommandAPI);
70
+ const buildInteraction = options.buildInteraction ?? defaultInteraction(state);
71
+ const cursorOf = options.cursor ?? (() => state.selection.cursor);
72
+ const initialCursorOf = options.initialCursor ?? (() => state.selection.rowId);
73
+ function fireDataChange(tableId, opts) {
74
+ safePostMessage({
75
+ tableId: tableId ?? state.selection.tableId,
76
+ dataChange: true,
77
+ ...opts?.mappingsChange ? { mappingsChange: true } : {}
78
+ });
79
+ }
80
+ function fireCursor(rowId, opts) {
81
+ const id = rowId === void 0 ? initialCursorOf() : rowId;
82
+ if (id === null) return;
83
+ safePostMessage({
84
+ tableId: state.selection.tableId,
85
+ rowId: id,
86
+ cursor: cursorOf(),
87
+ ...opts?.mappingsChange ? { mappingsChange: true } : {}
88
+ });
89
+ }
90
+ function fireOptions() {
91
+ safePostMessage({
92
+ options: hasOptions(state.widgetOptions) ? { ...state.widgetOptions } : null,
93
+ settings: { ...buildInteraction() }
94
+ });
95
+ }
96
+ function fireTheme(message) {
97
+ safePostMessage(message);
98
+ }
99
+ function emitInitialState(opts) {
100
+ fireOptions();
101
+ if (options.initialTheme) {
102
+ fireTheme(buildGristThemeHostMessage(options.initialTheme, { fromReady: true }));
103
+ }
104
+ fireDataChange(void 0, opts);
105
+ fireCursor(void 0, opts);
106
+ }
107
+ function emitInitialStateWithReplay(opts) {
108
+ emitInitialState(opts);
109
+ queueMicrotask(() => emitInitialState(opts));
110
+ const timer = setTimeout(() => {
111
+ timers.delete(timer);
112
+ emitInitialState(opts);
113
+ }, 0);
114
+ timers.add(timer);
115
+ }
116
+ const offBus = bus.on((event) => {
117
+ switch (event.type) {
118
+ case "ready":
119
+ emitInitialStateWithReplay();
120
+ break;
121
+ case "data-change":
122
+ fireDataChange(event.tableId);
123
+ break;
124
+ case "cursor-change": {
125
+ const rowId = event.rowId === null ? state.selection.rowId : event.rowId;
126
+ if (rowId !== null) {
127
+ safePostMessage({
128
+ tableId: event.tableId,
129
+ rowId,
130
+ cursor: cursorOf()
131
+ });
132
+ }
133
+ fireDataChange(event.tableId);
134
+ break;
135
+ }
136
+ case "options-change":
137
+ fireOptions();
138
+ break;
139
+ case "mappings-change":
140
+ emitInitialStateWithReplay({ mappingsChange: true });
141
+ break;
142
+ case "theme-change":
143
+ fireTheme(event.message);
144
+ break;
145
+ }
146
+ });
147
+ rpc.processIncoming();
148
+ function safePostMessage(payload) {
149
+ try {
150
+ rpc.postMessage(payload);
151
+ } catch (err) {
152
+ console.warn("[grist-emulator/iframe] failed to deliver postMessage", err);
153
+ }
154
+ }
155
+ return {
156
+ dispose() {
157
+ window.removeEventListener("message", onMessage);
158
+ offBus();
159
+ for (const timer of timers) clearTimeout(timer);
160
+ timers.clear();
161
+ },
162
+ fireDataChange,
163
+ fireCursor,
164
+ fireOptions,
165
+ fireTheme,
166
+ emitInitialState
167
+ };
168
+ }
169
+ function hasOptions(options) {
170
+ return Object.keys(options).length > 0;
171
+ }
172
+ function isRpcEnvelope(data) {
173
+ if (!data || typeof data !== "object") return false;
174
+ return typeof data.mtype === "number";
175
+ }
176
+ function defaultInteraction(state) {
177
+ return () => ({
178
+ access_level: state.configuration?.requiredAccess ?? "read table",
179
+ ...state.widgetInteraction
180
+ });
181
+ }
182
+
183
+ // src/emulator/core/document.ts
184
+ var SYSTEM_COLUMN = "id";
185
+ var EMULATOR_JS_FORMULA_PLACEHOLDER = "(emulator JavaScript formula)";
186
+ function enrichEmulatorColumnMetadata(col) {
187
+ const originalTrimmed = col.formula?.trim() ?? "";
188
+ const explicitInferred = col.inferredFormula?.trim() ?? "";
189
+ if (col.formulaFn) {
190
+ const inferredFormula = explicitInferred || originalTrimmed || EMULATOR_JS_FORMULA_PLACEHOLDER;
191
+ return {
192
+ ...col,
193
+ ...originalTrimmed ? { formula: col.formula } : {},
194
+ inferredFormula
195
+ };
196
+ }
197
+ if (explicitInferred || originalTrimmed) {
198
+ return {
199
+ ...col,
200
+ inferredFormula: explicitInferred || originalTrimmed || void 0
201
+ };
202
+ }
203
+ return col;
204
+ }
205
+ function sortEmulatorColumnsByParentPos(columns) {
206
+ const entries = Object.entries(columns);
207
+ entries.sort(([ka, a], [kb, b]) => {
208
+ const pa = a.parentPos;
209
+ const pb = b.parentPos;
210
+ if (pa !== void 0 && pb !== void 0 && pa !== pb) return pa - pb;
211
+ if (pa !== void 0 && pb === void 0) return -1;
212
+ if (pa === void 0 && pb !== void 0) return 1;
213
+ return ka.localeCompare(kb);
214
+ });
215
+ return Object.fromEntries(entries);
216
+ }
217
+ function inferColumnsFromRows(rows) {
218
+ const columns = {};
219
+ for (const row of rows) {
220
+ for (const key of Object.keys(row)) {
221
+ if (key === SYSTEM_COLUMN) continue;
222
+ if (!columns[key]) columns[key] = inferColumnType(row[key]);
223
+ }
224
+ }
225
+ return columns;
226
+ }
227
+ function inferColumnType(sample) {
228
+ switch (typeof sample) {
229
+ case "number":
230
+ return { type: "Numeric" };
231
+ case "boolean":
232
+ return { type: "Bool" };
233
+ case "string":
234
+ return { type: "Text" };
235
+ default:
236
+ return { type: "Any" };
237
+ }
238
+ }
239
+ function buildTable(tableId, input) {
240
+ const rows = input.rows ?? [];
241
+ const inferred = inferColumnsFromRows(rows);
242
+ const merged = {};
243
+ for (const [colId, col] of Object.entries({ ...inferred, ...input.columns ?? {} })) {
244
+ merged[colId] = enrichEmulatorColumnMetadata(col);
245
+ }
246
+ const columns = sortEmulatorColumnsByParentPos(merged);
247
+ return {
248
+ tableId,
249
+ label: input.label,
250
+ columns,
251
+ rows: rows.map((row) => ({ ...row }))
252
+ };
253
+ }
254
+ function createDocument(input) {
255
+ if (!input.tables || Object.keys(input.tables).length === 0) {
256
+ throw new Error("createDocument: at least one table is required");
257
+ }
258
+ const normalized = normalizeReplicaDocumentInput(input);
259
+ const tables = /* @__PURE__ */ new Map();
260
+ for (const [tableId, tableInput] of Object.entries(normalized.tables)) {
261
+ tables.set(tableId, buildTable(tableId, tableInput));
262
+ }
263
+ const firstTableId = tables.keys().next().value;
264
+ const requestedTableId = normalized.selection?.tableId;
265
+ const selectedTableId = requestedTableId && tables.has(requestedTableId) ? requestedTableId : firstTableId;
266
+ const selectedTable = tables.get(selectedTableId);
267
+ const fallbackRowId = typeof selectedTable.rows[0]?.id === "number" ? selectedTable.rows[0].id : null;
268
+ const selection = {
269
+ tableId: selectedTableId,
270
+ rowId: normalized.selection?.rowId ?? fallbackRowId,
271
+ mappings: normalized.selection?.mappings,
272
+ cursor: normalized.selection?.cursor
273
+ };
274
+ return {
275
+ docName: normalized.docName ?? "Untitled emulator document",
276
+ tables,
277
+ selection,
278
+ widgetOptions: { ...normalized.widgetOptions ?? {} },
279
+ widgetInteraction: { ...normalized.widgetInteraction ?? {} },
280
+ configuration: normalized.configuration ?? null,
281
+ accessToken: normalized.accessToken ?? null
282
+ };
283
+ }
284
+ function getTable(state, tableId) {
285
+ const table = state.tables.get(tableId);
286
+ if (!table) throw new Error(`Unknown table: ${tableId}`);
287
+ return table;
288
+ }
289
+ function getSelectedTable(state) {
290
+ return getTable(state, state.selection.tableId);
291
+ }
292
+ function nextRowId(table) {
293
+ let max = 0;
294
+ for (const row of table.rows) {
295
+ if (typeof row.id === "number" && row.id > max) max = row.id;
296
+ }
297
+ return max + 1;
298
+ }
299
+ function findRow(table, rowId) {
300
+ return table.rows.find((r) => r.id === rowId);
301
+ }
302
+ function findRowIndex(table, rowId) {
303
+ return table.rows.findIndex((r) => r.id === rowId);
304
+ }
305
+ function evaluateRowFormulas(state, table, row) {
306
+ for (const [colId, col] of Object.entries(table.columns)) {
307
+ if (col.formulaFn) {
308
+ try {
309
+ row[colId] = col.formulaFn(row, { table, document: state });
310
+ } catch (err) {
311
+ row[colId] = formulaError(err);
312
+ }
313
+ }
314
+ }
315
+ }
316
+ function escapeRegExp(s) {
317
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
318
+ }
319
+ function extractRefListIdsFromWire(raw) {
320
+ if (raw == null) return [];
321
+ if (!Array.isArray(raw)) return [];
322
+ const items = raw[0] === "L" ? raw.slice(1) : raw;
323
+ const out = [];
324
+ for (const item of items) {
325
+ if (typeof item === "number" && Number.isFinite(item) && item > 0) {
326
+ out.push(Math.trunc(item));
327
+ continue;
328
+ }
329
+ if (item && typeof item === "object" && "rowId" in item) {
330
+ const r = item.rowId;
331
+ if (typeof r === "number" && Number.isFinite(r) && r > 0) {
332
+ out.push(Math.trunc(r));
333
+ continue;
334
+ }
335
+ }
336
+ const t = coerceGristDocApiRefRowId(item);
337
+ if (t !== void 0 && t > 0) out.push(Math.trunc(t));
338
+ }
339
+ return Array.from(new Set(out));
340
+ }
341
+ function fieldFromRefListHelperFormula(formula, refColId) {
342
+ if (!formula) return null;
343
+ const re = new RegExp(`\\$${escapeRegExp(refColId)}\\.([A-Za-z_]\\w*)`);
344
+ const m = formula.match(re);
345
+ return m?.[1] ?? null;
346
+ }
347
+ function firstTextLikeColumnId(columns) {
348
+ let best = null;
349
+ for (const [colId, col] of Object.entries(columns)) {
350
+ if (colId === "id" || colId.startsWith("gristHelper_")) continue;
351
+ const t = col.type;
352
+ if (t !== "Text" && t !== "Choice") continue;
353
+ const pos = col.parentPos ?? 99999;
354
+ if (!best || pos < best.pos) best = { id: colId, pos };
355
+ }
356
+ return best?.id ?? null;
357
+ }
358
+ function formatLabelFromCell(value) {
359
+ if (value == null) return "";
360
+ if (typeof value === "string") return value.trim();
361
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
362
+ if (value instanceof Date) return value.toISOString();
363
+ if (Array.isArray(value) && value[0] === "E") return "";
364
+ if (typeof value === "object" && value && "__ref" in value) {
365
+ const r = value.rowId;
366
+ return typeof r === "number" && Number.isFinite(r) ? String(r) : "";
367
+ }
368
+ return "";
369
+ }
370
+ function syncRefListDisplayHelpers(state, table, row) {
371
+ const displayHelpersByRef = inferDisplayHelpersByRef(table.columns);
372
+ for (const [colId, col] of Object.entries(table.columns)) {
373
+ const type = col.type;
374
+ if (typeof type !== "string" || !type.startsWith("RefList:")) continue;
375
+ const helperColId = displayHelpersByRef[colId];
376
+ if (!helperColId) continue;
377
+ const helperMeta = table.columns[helperColId];
378
+ if (!helperMeta || helperMeta.formulaFn) continue;
379
+ const targetTableId = type.slice("RefList:".length);
380
+ const targetTable = state.tables.get(targetTableId);
381
+ if (!targetTable) continue;
382
+ const ids = extractRefListIdsFromWire(row[colId]);
383
+ if (ids.length === 0) {
384
+ row[helperColId] = [];
385
+ continue;
386
+ }
387
+ const fieldId = fieldFromRefListHelperFormula(helperMeta.formula, colId) ?? firstTextLikeColumnId(targetTable.columns);
388
+ const labels = ids.map((rid) => {
389
+ if (!fieldId) return String(rid);
390
+ const trow = findRow(targetTable, rid);
391
+ if (!trow) return String(rid);
392
+ const lbl = formatLabelFromCell(trow[fieldId]);
393
+ return lbl !== "" ? lbl : String(rid);
394
+ });
395
+ row[helperColId] = labels;
396
+ }
397
+ }
398
+ function formulaError(err) {
399
+ const message = err instanceof Error ? err.message : String(err);
400
+ return ["E", "Error", message];
401
+ }
402
+ function synthesizeTablesMeta(state) {
403
+ const ids = [];
404
+ const tableIds = [];
405
+ const labels = [];
406
+ let nextId = 1;
407
+ for (const [tableId, table] of state.tables.entries()) {
408
+ ids.push(nextId);
409
+ tableIds.push(tableId);
410
+ labels.push(table.label ?? null);
411
+ nextId += 1;
412
+ }
413
+ return {
414
+ id: ids,
415
+ tableId: tableIds,
416
+ // Optional human-readable label; real Grist keeps more fields, but these
417
+ // are sufficient for `buildReplicaDocumentFromDocApi` to resolve table ids.
418
+ label: labels
419
+ };
420
+ }
421
+ function synthesizeTablesColumnMeta(state) {
422
+ const parentIds = [];
423
+ const colIds = [];
424
+ const columnIds = [];
425
+ const types = [];
426
+ const labels = [];
427
+ const parentPos = [];
428
+ const isFormula = [];
429
+ const formulas = [];
430
+ const descriptions = [];
431
+ const widgetOptions = [];
432
+ const displayCol = [];
433
+ const visibleCol = [];
434
+ const tableIdToInternalId = /* @__PURE__ */ new Map();
435
+ {
436
+ let nextId = 1;
437
+ for (const [tableId] of state.tables.entries()) {
438
+ tableIdToInternalId.set(tableId, nextId);
439
+ nextId += 1;
440
+ }
441
+ }
442
+ for (const [tableId, table] of state.tables.entries()) {
443
+ const parentInternalId = tableIdToInternalId.get(tableId);
444
+ if (parentInternalId === void 0) continue;
445
+ for (const [colId, col] of Object.entries(table.columns)) {
446
+ parentIds.push(parentInternalId);
447
+ colIds.push(colId);
448
+ const ref = typeof col.colRef === "number" ? col.colRef : columnIds.length + 1;
449
+ columnIds.push(ref);
450
+ types.push(col.type ?? null);
451
+ labels.push(col.label ?? null);
452
+ parentPos.push(col.parentPos ?? null);
453
+ isFormula.push(Boolean(col.isFormula));
454
+ const formula = typeof col.formula === "string" ? col.formula : null;
455
+ formulas.push(formula);
456
+ descriptions.push(col.description ?? null);
457
+ widgetOptions.push(col.widgetOptions ?? null);
458
+ displayCol.push(typeof col.displayColRef === "number" ? col.displayColRef : null);
459
+ visibleCol.push(typeof col.visibleColRef === "number" ? col.visibleColRef : null);
460
+ }
461
+ }
462
+ return {
463
+ parentId: parentIds,
464
+ colId: colIds,
465
+ id: columnIds,
466
+ type: types,
467
+ label: labels,
468
+ parentPos,
469
+ isFormula,
470
+ formula: formulas,
471
+ description: descriptions,
472
+ // Also expose legacy `descr` for robustness.
473
+ descr: descriptions,
474
+ widgetOptions,
475
+ displayCol,
476
+ visibleCol
477
+ };
478
+ }
479
+
480
+ // src/emulator/core/action-engine.ts
481
+ var EmulatorActionError = class extends Error {
482
+ constructor(action, message) {
483
+ super(message);
484
+ this.action = action;
485
+ this.name = "EmulatorActionError";
486
+ }
487
+ };
488
+ function applyUserActions(state, bus, actions) {
489
+ const retValues = [];
490
+ const changed = /* @__PURE__ */ new Set();
491
+ for (const action of actions) {
492
+ bus.emit({ type: "user-action", action });
493
+ const ret = applySingle(state, action, changed);
494
+ retValues.push(ret);
495
+ }
496
+ for (const tableId of changed) {
497
+ bus.emit({ type: "data-change", tableId });
498
+ }
499
+ return { retValues, changedTableIds: [...changed] };
500
+ }
501
+ function applySingle(state, action, changed) {
502
+ const [name, ...args] = action;
503
+ switch (name) {
504
+ case "AddRecord":
505
+ return addRecord(state, args, changed);
506
+ case "BulkAddRecord":
507
+ return bulkAddRecord(state, args, changed);
508
+ case "UpdateRecord":
509
+ return updateRecord(state, args, changed);
510
+ case "BulkUpdateRecord":
511
+ return bulkUpdateRecord(state, args, changed);
512
+ case "RemoveRecord":
513
+ return removeRecord(state, args, changed);
514
+ case "BulkRemoveRecord":
515
+ return bulkRemoveRecord(state, args, changed);
516
+ case "AddColumn":
517
+ return addColumn(state, args);
518
+ case "AddVisibleColumn":
519
+ return addColumn(state, args);
520
+ case "RemoveColumn":
521
+ return removeColumn(state, args, changed);
522
+ case "RenameColumn":
523
+ return renameColumn(state, args, changed);
524
+ case "ModifyColumn":
525
+ return modifyColumn(state, args, changed);
526
+ case "AddTable":
527
+ return addTable(state, args);
528
+ case "RemoveTable":
529
+ return removeTable(state, args);
530
+ case "RenameTable":
531
+ return renameTable(state, args);
532
+ default:
533
+ throw new EmulatorActionError(action, `Unsupported user action: ${String(name)}`);
534
+ }
535
+ }
536
+ function addRecord(state, [tableId, rowId, values], changed) {
537
+ const table = getTable(state, requireString(tableId, "AddRecord.tableId"));
538
+ const id = typeof rowId === "number" ? rowId : nextRowId(table);
539
+ const normalizedValues = normalizeValuesForTable(
540
+ table,
541
+ values ?? {}
542
+ );
543
+ const row = { id, ...normalizedValues };
544
+ ensureColumns(table, normalizedValues);
545
+ evaluateRowFormulas(state, table, row);
546
+ syncRefListDisplayHelpers(state, table, row);
547
+ table.rows.push(row);
548
+ changed.add(table.tableId);
549
+ return id;
550
+ }
551
+ function bulkAddRecord(state, [tableId, rowIds, columnar], changed) {
552
+ const table = getTable(state, requireString(tableId, "BulkAddRecord.tableId"));
553
+ const cols = normalizeColumnarForTable(
554
+ table,
555
+ columnar ?? {}
556
+ );
557
+ const ids = Array.isArray(rowIds) ? rowIds : [];
558
+ const length = ids.length || lengthOf(cols);
559
+ const out = [];
560
+ ensureColumns(table, cols, true);
561
+ for (let i = 0; i < length; i++) {
562
+ const id = typeof ids[i] === "number" ? ids[i] : nextRowId(table);
563
+ const row = { id };
564
+ for (const colId of Object.keys(cols)) row[colId] = cols[colId][i];
565
+ evaluateRowFormulas(state, table, row);
566
+ syncRefListDisplayHelpers(state, table, row);
567
+ table.rows.push(row);
568
+ out.push(id);
569
+ }
570
+ changed.add(table.tableId);
571
+ return out;
572
+ }
573
+ function updateRecord(state, [tableId, rowId, values], changed) {
574
+ const table = getTable(state, requireString(tableId, "UpdateRecord.tableId"));
575
+ const id = requireNumber(rowId, "UpdateRecord.rowId");
576
+ const row = findRow(table, id);
577
+ if (!row) throw new EmulatorActionError(["UpdateRecord", tableId, rowId, values], `Row ${id} not found in ${table.tableId}`);
578
+ const normalizedValues = normalizeValuesForTable(
579
+ table,
580
+ values ?? {}
581
+ );
582
+ ensureColumns(table, normalizedValues);
583
+ Object.assign(row, normalizedValues);
584
+ evaluateRowFormulas(state, table, row);
585
+ syncRefListDisplayHelpers(state, table, row);
586
+ changed.add(table.tableId);
587
+ return null;
588
+ }
589
+ function bulkUpdateRecord(state, [tableId, rowIds, columnar], changed) {
590
+ const table = getTable(state, requireString(tableId, "BulkUpdateRecord.tableId"));
591
+ const ids = rowIds;
592
+ const cols = normalizeColumnarForTable(
593
+ table,
594
+ columnar ?? {}
595
+ );
596
+ ensureColumns(table, cols, true);
597
+ ids.forEach((id, idx) => {
598
+ const row = findRow(table, id);
599
+ if (!row) throw new EmulatorActionError(["BulkUpdateRecord", tableId, ids, cols], `Row ${id} not found in ${table.tableId}`);
600
+ for (const colId of Object.keys(cols)) row[colId] = cols[colId][idx];
601
+ evaluateRowFormulas(state, table, row);
602
+ syncRefListDisplayHelpers(state, table, row);
603
+ });
604
+ changed.add(table.tableId);
605
+ return null;
606
+ }
607
+ function removeRecord(state, [tableId, rowId], changed) {
608
+ const table = getTable(state, requireString(tableId, "RemoveRecord.tableId"));
609
+ const id = requireNumber(rowId, "RemoveRecord.rowId");
610
+ const idx = findRowIndex(table, id);
611
+ if (idx >= 0) table.rows.splice(idx, 1);
612
+ if (state.selection.tableId === table.tableId && state.selection.rowId === id) {
613
+ state.selection.rowId = table.rows[0]?.id ?? null;
614
+ }
615
+ changed.add(table.tableId);
616
+ return null;
617
+ }
618
+ function bulkRemoveRecord(state, [tableId, rowIds], changed) {
619
+ const table = getTable(state, requireString(tableId, "BulkRemoveRecord.tableId"));
620
+ const ids = new Set(rowIds);
621
+ table.rows = table.rows.filter((r) => !ids.has(r.id));
622
+ if (state.selection.tableId === table.tableId && typeof state.selection.rowId === "number" && ids.has(state.selection.rowId)) {
623
+ state.selection.rowId = table.rows[0]?.id ?? null;
624
+ }
625
+ changed.add(table.tableId);
626
+ return null;
627
+ }
628
+ function addColumn(state, [tableId, colId, info]) {
629
+ const table = getTable(state, requireString(tableId, "AddColumn.tableId"));
630
+ const col = requireString(colId, "AddColumn.colId");
631
+ if (table.columns[col]) throw new EmulatorActionError(["AddColumn", tableId, colId], `Column ${col} already exists`);
632
+ table.columns[col] = info ?? {};
633
+ for (const row of table.rows) {
634
+ if (!(col in row)) row[col] = null;
635
+ }
636
+ return null;
637
+ }
638
+ function removeColumn(state, [tableId, colId], changed) {
639
+ const table = getTable(state, requireString(tableId, "RemoveColumn.tableId"));
640
+ const col = requireString(colId, "RemoveColumn.colId");
641
+ delete table.columns[col];
642
+ for (const row of table.rows) delete row[col];
643
+ changed.add(table.tableId);
644
+ return null;
645
+ }
646
+ function renameColumn(state, [tableId, colId, newColId], changed) {
647
+ const table = getTable(state, requireString(tableId, "RenameColumn.tableId"));
648
+ const from = requireString(colId, "RenameColumn.colId");
649
+ const to = requireString(newColId, "RenameColumn.newColId");
650
+ if (!table.columns[from]) throw new EmulatorActionError(["RenameColumn", tableId, from, to], `Column ${from} not found`);
651
+ if (from === to) return null;
652
+ table.columns[to] = table.columns[from];
653
+ delete table.columns[from];
654
+ for (const row of table.rows) {
655
+ if (from in row) {
656
+ row[to] = row[from];
657
+ delete row[from];
658
+ }
659
+ }
660
+ changed.add(table.tableId);
661
+ return null;
662
+ }
663
+ function modifyColumn(state, [tableId, colId, info], changed) {
664
+ const table = getTable(state, requireString(tableId, "ModifyColumn.tableId"));
665
+ const col = requireString(colId, "ModifyColumn.colId");
666
+ if (!table.columns[col]) throw new EmulatorActionError(["ModifyColumn", tableId, colId], `Column ${col} not found`);
667
+ table.columns[col] = { ...table.columns[col], ...info };
668
+ changed.add(table.tableId);
669
+ return null;
670
+ }
671
+ function addTable(state, [tableId, columns]) {
672
+ const id = requireString(tableId, "AddTable.tableId");
673
+ if (state.tables.has(id)) throw new EmulatorActionError(["AddTable", tableId], `Table ${id} already exists`);
674
+ const cols = {};
675
+ for (const c of columns ?? []) {
676
+ cols[c.id] = { ...c };
677
+ }
678
+ state.tables.set(id, { tableId: id, columns: cols, rows: [] });
679
+ return id;
680
+ }
681
+ function removeTable(state, [tableId]) {
682
+ const id = requireString(tableId, "RemoveTable.tableId");
683
+ state.tables.delete(id);
684
+ if (state.selection.tableId === id) {
685
+ const fallback = state.tables.keys().next().value;
686
+ if (fallback) {
687
+ state.selection.tableId = fallback;
688
+ state.selection.rowId = null;
689
+ }
690
+ }
691
+ return null;
692
+ }
693
+ function renameTable(state, [from, to]) {
694
+ const fromId = requireString(from, "RenameTable.from");
695
+ const toId = requireString(to, "RenameTable.to");
696
+ const table = state.tables.get(fromId);
697
+ if (!table) throw new EmulatorActionError(["RenameTable", fromId], `Table ${fromId} not found`);
698
+ state.tables.delete(fromId);
699
+ table.tableId = toId;
700
+ state.tables.set(toId, table);
701
+ if (state.selection.tableId === fromId) state.selection.tableId = toId;
702
+ return null;
703
+ }
704
+ function ensureColumns(table, values, bulk = false) {
705
+ for (const col of Object.keys(values)) {
706
+ if (!table.columns[col]) {
707
+ const sample = bulk ? values[col]?.[0] : values[col];
708
+ table.columns[col] = inferType(sample);
709
+ }
710
+ }
711
+ }
712
+ function inferType(sample) {
713
+ switch (typeof sample) {
714
+ case "number":
715
+ return { type: "Numeric" };
716
+ case "boolean":
717
+ return { type: "Bool" };
718
+ case "string":
719
+ return { type: "Text" };
720
+ default:
721
+ return { type: "Any" };
722
+ }
723
+ }
724
+ function lengthOf(cols) {
725
+ for (const arr of Object.values(cols)) if (Array.isArray(arr)) return arr.length;
726
+ return 0;
727
+ }
728
+ function requireString(value, name) {
729
+ if (typeof value !== "string" || !value) throw new Error(`Expected non-empty string for ${name}`);
730
+ return value;
731
+ }
732
+ function requireNumber(value, name) {
733
+ if (typeof value !== "number" || Number.isNaN(value)) throw new Error(`Expected number for ${name}`);
734
+ return value;
735
+ }
736
+ function normalizeValuesForTable(table, values) {
737
+ const out = {};
738
+ for (const [colId, value] of Object.entries(values)) {
739
+ out[colId] = normalizeCellValueForType(value, table.columns[colId]?.type);
740
+ }
741
+ return out;
742
+ }
743
+ function normalizeColumnarForTable(table, columns) {
744
+ const out = {};
745
+ for (const [colId, values] of Object.entries(columns)) {
746
+ const type = table.columns[colId]?.type;
747
+ out[colId] = Array.isArray(values) ? values.map((value) => normalizeCellValueForType(value, type)) : [];
748
+ }
749
+ return out;
750
+ }
751
+ function normalizeCellValueForType(value, type) {
752
+ if (value === null || value === void 0 || typeof type !== "string") return value;
753
+ if (type.startsWith("Ref:")) return normalizeRefValue(value);
754
+ if (type.startsWith("RefList:")) return normalizeRefListValue(value);
755
+ return value;
756
+ }
757
+ function extractRefRowId(value) {
758
+ if (value === null || value === void 0) return null;
759
+ if (value && typeof value === "object" && "__ref" in value) {
760
+ const inner = value.rowId;
761
+ return extractRefRowId(inner);
762
+ }
763
+ if (value && typeof value === "object" && "rowId" in value) {
764
+ return extractRefRowId(value.rowId);
765
+ }
766
+ const fromTuple = coerceGristDocApiRefRowId(value);
767
+ if (fromTuple !== void 0 && fromTuple > 0) return Math.trunc(fromTuple);
768
+ return toPositiveInt(value);
769
+ }
770
+ function normalizeRefValue(value) {
771
+ return extractRefRowId(value);
772
+ }
773
+ function normalizeRefListValue(value) {
774
+ const toIdList = (items) => Array.from(
775
+ new Set(items.map((item) => extractRefRowId(item)).filter((item) => item !== null))
776
+ );
777
+ if (Array.isArray(value) && value[0] === "L") {
778
+ return ["L", ...toIdList(value.slice(1))];
779
+ }
780
+ if (Array.isArray(value)) {
781
+ return ["L", ...toIdList(value)];
782
+ }
783
+ if (typeof value === "string") {
784
+ const tokens = value.split(",").map((t) => t.trim()).filter(Boolean);
785
+ return ["L", ...toIdList(tokens)];
786
+ }
787
+ if (value && typeof value === "object" && Array.isArray(value.rowIds)) {
788
+ return ["L", ...toIdList(value.rowIds)];
789
+ }
790
+ return ["L"];
791
+ }
792
+ function toPositiveInt(value) {
793
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) return Math.trunc(value);
794
+ if (typeof value === "string") {
795
+ const n = Number.parseInt(value.trim(), 10);
796
+ if (Number.isFinite(n) && n > 0) return n;
797
+ }
798
+ return null;
799
+ }
800
+
801
+ // src/emulator/transports/inline.ts
802
+ function createInlineTransport(options) {
803
+ const { state, bus, api } = options;
804
+ const target = options.target ?? globalThis;
805
+ installFakeIframeContext(target);
806
+ const recordCbs = /* @__PURE__ */ new Map();
807
+ const recordsCbs = /* @__PURE__ */ new Map();
808
+ const optionsCbs = /* @__PURE__ */ new Set();
809
+ const newRecordCbs = /* @__PURE__ */ new Set();
810
+ const generic = /* @__PURE__ */ new Map();
811
+ let lastRecord = null;
812
+ let lastRecords = null;
813
+ let lastOptions = null;
814
+ let lastNewRecord = void 0;
815
+ const buildInteraction = options.buildInteraction ?? (() => ({
816
+ access_level: state.configuration?.requiredAccess ?? "read table",
817
+ ...state.widgetInteraction
818
+ }));
819
+ async function pushRecord(rowId) {
820
+ const mappings = resolveMappings(state);
821
+ if (rowId === "new") {
822
+ lastNewRecord = mappings;
823
+ for (const cb of newRecordCbs) {
824
+ try {
825
+ cb(mappings);
826
+ } catch (err) {
827
+ logErr(err);
828
+ }
829
+ }
830
+ lastRecord = { record: null, mappings };
831
+ for (const [cb] of recordCbs) {
832
+ try {
833
+ cb(null, mappings);
834
+ } catch (err) {
835
+ logErr(err);
836
+ }
837
+ }
838
+ return;
839
+ }
840
+ if (rowId === null) {
841
+ lastRecord = { record: null, mappings };
842
+ for (const [cb] of recordCbs) {
843
+ try {
844
+ cb(null, mappings);
845
+ } catch (err) {
846
+ logErr(err);
847
+ }
848
+ }
849
+ return;
850
+ }
851
+ let record = null;
852
+ try {
853
+ record = await api.GristView.fetchSelectedRecord(rowId);
854
+ } catch (err) {
855
+ logErr(err);
856
+ }
857
+ lastRecord = { record, mappings };
858
+ for (const [cb, opts] of recordCbs) {
859
+ try {
860
+ const r = opts?.keepEncoded ? await api.GristView.fetchSelectedRecord(rowId, { keepEncoded: true }) : record;
861
+ cb(r, mappings);
862
+ } catch (err) {
863
+ logErr(err);
864
+ }
865
+ }
866
+ }
867
+ async function pushRecords() {
868
+ const mappings = resolveMappings(state);
869
+ let baseRows = [];
870
+ try {
871
+ const columnar = await api.GristView.fetchSelectedTable({});
872
+ baseRows = columnsToRows(columnar);
873
+ } catch (err) {
874
+ logErr(err);
875
+ }
876
+ lastRecords = { records: baseRows, mappings };
877
+ for (const [cb, opts] of recordsCbs) {
878
+ try {
879
+ const fetchOptions = coerceFetchOptions(opts);
880
+ const rows = fetchOptions?.keepEncoded ? columnsToRows(await api.GristView.fetchSelectedTable({ keepEncoded: true })) : baseRows;
881
+ cb(rows, mappings);
882
+ } catch (err) {
883
+ logErr(err);
884
+ }
885
+ }
886
+ }
887
+ async function fetchSelectedTableShaped(opts) {
888
+ const fetchOptions = coerceFetchOptions(opts);
889
+ const columnar = await api.GristView.fetchSelectedTable(
890
+ fetchOptions ? { keepEncoded: fetchOptions.keepEncoded } : {}
891
+ );
892
+ if (fetchOptions?.format === "rows") return columnsToRows(columnar);
893
+ return columnar;
894
+ }
895
+ function pushOptions() {
896
+ const opts = Object.keys(state.widgetOptions).length > 0 ? { ...state.widgetOptions } : null;
897
+ const interaction = buildInteraction();
898
+ lastOptions = { options: opts, interaction };
899
+ for (const cb of optionsCbs) {
900
+ try {
901
+ cb(opts, interaction);
902
+ } catch (err) {
903
+ logErr(err);
904
+ }
905
+ }
906
+ }
907
+ const offBus = bus.on(async (event) => {
908
+ switch (event.type) {
909
+ case "data-change":
910
+ await pushRecords();
911
+ await pushRecord(state.selection.rowId);
912
+ break;
913
+ case "cursor-change":
914
+ await pushRecord(event.rowId);
915
+ break;
916
+ case "options-change":
917
+ pushOptions();
918
+ break;
919
+ case "ready":
920
+ pushOptions();
921
+ if (options.initialTheme) {
922
+ notify("message", buildGristThemeHostMessage(options.initialTheme, { fromReady: true }));
923
+ }
924
+ await pushRecords();
925
+ await pushRecord(state.selection.rowId);
926
+ break;
927
+ case "mappings-change":
928
+ await pushRecords();
929
+ await pushRecord(state.selection.rowId);
930
+ break;
931
+ case "theme-change":
932
+ notify("message", event.message);
933
+ break;
934
+ }
935
+ });
936
+ function makeTableOps(tableId) {
937
+ return {
938
+ async getTableId() {
939
+ return tableId;
940
+ },
941
+ async create(records, options2) {
942
+ const list = Array.isArray(records) ? records : [records];
943
+ const ids = [];
944
+ for (const r of list) {
945
+ const result = applyUserActions(state, bus, [
946
+ gristAddRecordAction(tableId, parseFields(r.fields, options2))
947
+ ]);
948
+ ids.push({ id: result.retValues[0] });
949
+ }
950
+ return Array.isArray(records) ? ids : ids[0];
951
+ },
952
+ async update(records, options2) {
953
+ const list = Array.isArray(records) ? records : [records];
954
+ const actions = list.map((r) => gristUpdateRecordAction(tableId, r.id, parseFields(r.fields, options2)));
955
+ applyUserActions(state, bus, actions);
956
+ },
957
+ async upsert(records, options2) {
958
+ const list = Array.isArray(records) ? records : [records];
959
+ const allowAdd = options2?.add !== false;
960
+ const allowUpdate = options2?.update !== false;
961
+ const table = getTable(state, tableId);
962
+ const actions = [];
963
+ for (const r of list) {
964
+ const matches = table.rows.filter(
965
+ (row) => Object.entries(r.require).every(([k, v]) => row[k] === v)
966
+ );
967
+ if (matches.length > 0) {
968
+ if (!allowUpdate) continue;
969
+ const fields = parseFields(r.fields ?? {}, options2);
970
+ for (const m of matches) actions.push(gristUpdateRecordAction(tableId, m.id, fields));
971
+ } else if (allowAdd) {
972
+ actions.push(gristAddRecordAction(tableId, { ...r.require, ...parseFields(r.fields ?? {}, options2) }));
973
+ }
974
+ }
975
+ if (actions.length > 0) applyUserActions(state, bus, actions);
976
+ },
977
+ async destroy(recordIds) {
978
+ if (Array.isArray(recordIds)) {
979
+ applyUserActions(state, bus, [gristBulkRemoveRecordAction(tableId, recordIds)]);
980
+ } else {
981
+ applyUserActions(state, bus, [gristRemoveRecordAction(tableId, recordIds)]);
982
+ }
983
+ void gristBulkAddRecordAction;
984
+ void gristBulkUpdateRecordAction;
985
+ }
986
+ };
987
+ }
988
+ function notify(event, ...args) {
989
+ const set = generic.get(event);
990
+ if (!set) return;
991
+ for (const fn of set) {
992
+ try {
993
+ fn(...args);
994
+ } catch (err) {
995
+ logErr(err);
996
+ }
997
+ }
998
+ }
999
+ const grist = {
1000
+ async ready(opts) {
1001
+ await api.CustomSectionAPI.configure(opts ? { ...opts, onEditOptions: void 0 } : {});
1002
+ },
1003
+ onRecord(cb, opts) {
1004
+ recordCbs.set(cb, opts);
1005
+ if (lastRecord) {
1006
+ try {
1007
+ cb(lastRecord.record, lastRecord.mappings);
1008
+ } catch (err) {
1009
+ logErr(err);
1010
+ }
1011
+ }
1012
+ },
1013
+ onRecords(cb, opts) {
1014
+ recordsCbs.set(cb, opts);
1015
+ if (lastRecords) {
1016
+ try {
1017
+ cb(lastRecords.records, lastRecords.mappings);
1018
+ } catch (err) {
1019
+ logErr(err);
1020
+ }
1021
+ }
1022
+ },
1023
+ onOptions(cb) {
1024
+ optionsCbs.add(cb);
1025
+ if (lastOptions) {
1026
+ try {
1027
+ cb(lastOptions.options, lastOptions.interaction);
1028
+ } catch (err) {
1029
+ logErr(err);
1030
+ }
1031
+ }
1032
+ },
1033
+ onNewRecord(cb) {
1034
+ newRecordCbs.add(cb);
1035
+ if (lastNewRecord !== void 0) {
1036
+ try {
1037
+ cb(lastNewRecord);
1038
+ } catch (err) {
1039
+ logErr(err);
1040
+ }
1041
+ }
1042
+ },
1043
+ mapColumnNames(data) {
1044
+ const mappings = resolveMappings(state);
1045
+ if (!mappings) return null;
1046
+ const out = {};
1047
+ let allMapped = true;
1048
+ for (const [logical, real] of Object.entries(mappings)) {
1049
+ if (Array.isArray(real)) {
1050
+ out[logical] = real.map((r) => data[r]);
1051
+ } else if (typeof real === "string") {
1052
+ out[logical] = data[real];
1053
+ } else {
1054
+ allMapped = false;
1055
+ }
1056
+ }
1057
+ return allMapped ? out : null;
1058
+ },
1059
+ mapColumnNamesBack(data) {
1060
+ const mappings = resolveMappings(state);
1061
+ if (!mappings) return data;
1062
+ const out = {};
1063
+ for (const [logical, real] of Object.entries(mappings)) {
1064
+ if (typeof real === "string" && logical in data) {
1065
+ out[real] = data[logical];
1066
+ }
1067
+ }
1068
+ return out;
1069
+ },
1070
+ getOption: (k) => api.WidgetAPI.getOption(k),
1071
+ setOption: (k, v) => api.WidgetAPI.setOption(k, v),
1072
+ getOptions: () => api.WidgetAPI.getOptions(),
1073
+ setOptions: (o) => api.WidgetAPI.setOptions(o),
1074
+ clearOptions: () => api.WidgetAPI.clearOptions(),
1075
+ setCursorPos: (pos) => api.GristView.setCursorPos(pos),
1076
+ setSelectedRows: (rowIds) => api.GristView.setSelectedRows(rowIds),
1077
+ fetchSelectedTable: (opts) => fetchSelectedTableShaped(opts),
1078
+ fetchSelectedRecord: (rowId, opts) => api.GristView.fetchSelectedRecord(rowId, coerceFetchOptions(opts)),
1079
+ docApi: {
1080
+ getDocName: () => api.GristDocAPI.getDocName(),
1081
+ listTables: () => api.GristDocAPI.listTables(),
1082
+ fetchTable: (id) => api.GristDocAPI.fetchTable(id),
1083
+ applyUserActions: (actions, options2) => api.GristDocAPI.applyUserActions(actions, options2),
1084
+ getAccessToken: (opts) => api.GristDocAPI.getAccessToken(opts),
1085
+ fetchSelectedTable: (opts) => fetchSelectedTableShaped(opts),
1086
+ fetchSelectedRecord: (rowId, opts) => api.GristView.fetchSelectedRecord(rowId, coerceFetchOptions(opts))
1087
+ },
1088
+ get selectedTable() {
1089
+ return makeTableOps(state.selection.tableId);
1090
+ },
1091
+ getTable(tableId) {
1092
+ return makeTableOps(tableId ?? state.selection.tableId);
1093
+ },
1094
+ sectionApi: {
1095
+ configure: (settings) => api.CustomSectionAPI.configure(settings),
1096
+ mappings: () => api.CustomSectionAPI.mappings()
1097
+ },
1098
+ on(eventName, listener) {
1099
+ let set = generic.get(eventName);
1100
+ if (!set) generic.set(eventName, set = /* @__PURE__ */ new Set());
1101
+ set.add(listener);
1102
+ return () => set.delete(listener);
1103
+ }
1104
+ };
1105
+ target.grist = grist;
1106
+ return {
1107
+ grist,
1108
+ dispose() {
1109
+ offBus();
1110
+ const t = target;
1111
+ if (t.grist === grist) delete t.grist;
1112
+ recordCbs.clear();
1113
+ recordsCbs.clear();
1114
+ optionsCbs.clear();
1115
+ newRecordCbs.clear();
1116
+ generic.clear();
1117
+ lastRecord = null;
1118
+ lastRecords = null;
1119
+ lastOptions = null;
1120
+ lastNewRecord = void 0;
1121
+ },
1122
+ fireDataChange(tableId) {
1123
+ bus.emit({ type: "data-change", tableId: tableId ?? state.selection.tableId });
1124
+ },
1125
+ fireCursor(rowId) {
1126
+ const id = rowId === void 0 ? state.selection.rowId : rowId;
1127
+ bus.emit({ type: "cursor-change", tableId: state.selection.tableId, rowId: id });
1128
+ },
1129
+ fireOptions() {
1130
+ pushOptions();
1131
+ },
1132
+ fireTheme(message) {
1133
+ bus.emit({ type: "theme-change", message });
1134
+ },
1135
+ emitInitialState() {
1136
+ pushOptions();
1137
+ void pushRecords().then(() => pushRecord(state.selection.rowId));
1138
+ }
1139
+ };
1140
+ }
1141
+ function columnsToRows(columnar) {
1142
+ const cols = Object.keys(columnar);
1143
+ if (cols.length === 0) return [];
1144
+ const length = (columnar[cols[0]] ?? []).length;
1145
+ const rows = [];
1146
+ for (let i = 0; i < length; i++) {
1147
+ const row = {};
1148
+ for (const c of cols) row[c] = columnar[c][i];
1149
+ rows.push(row);
1150
+ }
1151
+ return rows;
1152
+ }
1153
+ function parseFields(fields, _options) {
1154
+ return { ...fields };
1155
+ }
1156
+ function coerceFetchOptions(opts) {
1157
+ if (!opts) return void 0;
1158
+ const format = opts.format === "rows" || opts.format === "columns" ? opts.format : void 0;
1159
+ return { keepEncoded: opts.keepEncoded, format };
1160
+ }
1161
+ function logErr(err) {
1162
+ console.error("[grist-emulator/inline]", err);
1163
+ }
1164
+ function installFakeIframeContext(target) {
1165
+ try {
1166
+ if (target.self !== target.top) return;
1167
+ Object.defineProperty(target, "top", {
1168
+ configurable: true,
1169
+ get: () => ({})
1170
+ });
1171
+ } catch {
1172
+ }
1173
+ }
1174
+
1175
+ export {
1176
+ enrichEmulatorColumnMetadata,
1177
+ createDocument,
1178
+ getTable,
1179
+ getSelectedTable,
1180
+ findRow,
1181
+ synthesizeTablesMeta,
1182
+ synthesizeTablesColumnMeta,
1183
+ EmulatorActionError,
1184
+ applyUserActions,
1185
+ buildGristThemeHostMessage,
1186
+ createIframeTransport,
1187
+ createInlineTransport
1188
+ };
1189
+ //# sourceMappingURL=chunk-QKXKZ27D.js.map