grist-widget-sdk 0.2.1 → 0.2.15
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 +13 -0
- package/dist/advanced.cjs +191 -132
- package/dist/advanced.cjs.map +1 -1
- package/dist/advanced.d.cts +30 -1
- package/dist/advanced.d.ts +30 -1
- package/dist/advanced.js +60 -11
- package/dist/advanced.js.map +1 -1
- package/dist/{chunk-BNBV24WM.js → chunk-ER6UWHZA.js} +2 -2
- package/dist/{chunk-XE2SKNLH.js → chunk-KOVYBNPJ.js} +16 -4
- package/dist/chunk-KOVYBNPJ.js.map +1 -0
- package/dist/emulator/testing.cjs +2 -0
- package/dist/emulator/testing.cjs.map +1 -1
- package/dist/emulator/testing.js +1 -1
- package/dist/index.cjs +15 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-XE2SKNLH.js.map +0 -1
- /package/dist/{chunk-BNBV24WM.js.map → chunk-ER6UWHZA.js.map} +0 -0
package/dist/advanced.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sdk/advanced/index.ts","../src/sdk/internal/hooks/use-grist-core.ts","../src/sdk/internal/handshake/types.ts","../src/sdk/internal/handshake/actions.ts","../src/sdk/helpers/grist-mappings.ts","../src/sdk/internal/handshake/mapping-resolver.ts","../src/sdk/internal/handshake/access.ts","../src/sdk/internal/handshake/reducer.ts","../src/sdk/internal/handshake/derive.ts","../src/sdk/internal/handshake-manager-context.ts","../src/sdk/internal/ready-singleton.ts","../src/sdk/internal/build-manager-options.ts","../src/sdk/internal/handshake/effects/detect.ts","../src/sdk/internal/handshake/effects/negotiate.ts","../src/sdk/helpers/grist-widget-interaction.ts","../src/sdk/internal/subscriptions.ts","../src/sdk/internal/handshake/effects/subscriptions.ts","../src/sdk/internal/handshake/effects/mappings.ts","../src/sdk/internal/handshake/effects/heartbeat.ts","../src/sdk/internal/handshake/authz-from-options-cache.ts","../src/sdk/internal/handshake/environment.ts","../src/sdk/internal/handshake/manager.ts","../src/sdk/internal/embedded-handshake-manager.ts","../src/sdk/low-level-hooks/use-grist-availability.ts","../src/sdk/low-level-hooks/use-grist-ready.ts","../src/sdk/low-level-hooks/use-grist-handshake.ts","../src/sdk/components/grist-handshake-provider.tsx","../src/sdk/low-level-hooks/use-grist-record.ts","../src/sdk/low-level-hooks/use-grist-records.ts","../src/sdk/low-level-hooks/use-grist-new-record.ts","../src/sdk/mid-level-hooks/use-grist-actions.ts","../src/sdk/helpers/grist-user-actions.ts","../src/sdk/internal/hooks/use-rpc-heartbeat-coalesce.ts","../src/sdk/mid-level-hooks/use-grist-mapped-record.ts","../src/sdk/mid-level-hooks/use-grist-table-ops.ts","../src/sdk/mid-level-hooks/use-grist-rows-from-table.ts","../src/sdk/helpers/grist-table-data.ts","../src/sdk/internal/contexts.tsx","../src/sdk/mid-level-hooks/use-grist-access-token.ts","../src/sdk/helpers/grist-fetch-with-auth.ts","../src/sdk/mid-level-hooks/use-grist-section.ts","../src/sdk/mid-level-hooks/use-grist-widget-options.ts","../src/sdk/mid-level-hooks/use-grist-widget-options-from-context.ts","../src/sdk/use-grist-table.ts"],"sourcesContent":["/**\n * Advanced hooks for power users / internal tooling.\n *\n * ⚠️ For day-to-day widget code, agents and humans should use the primary\n * surface from `@/lib/grist`:\n *\n * ```tsx\n * import { GristWidgetProvider, GristBoundary, useGrist, useGristSchema } from \"grist-widget-sdk\"\n * ```\n *\n * The hooks re-exported below are lower-level building blocks that overlap\n * with `useGrist()` — mixing them is a source of bugs (stacked `ready` calls,\n * divergent state). Only reach for them when you need a capability that the\n * primary hook does not expose.\n */\n\n// Low-level: availability / ready-handshake / raw subscriptions\nexport { useGristAvailability } from \"../low-level-hooks/use-grist-availability\"\nexport {\n useGristReady,\n type UseGristReadyOptions,\n type UseGristReadyResult,\n} from \"../low-level-hooks/use-grist-ready\"\n\n// Handshake state machine (SOTA) — opt-in foundation for widgets that want\n// fine-grained snapshot + capabilities + reload/restart control.\nexport {\n useGristHandshake,\n useGristCapabilities,\n type UseGristHandshakeOptions,\n type UseGristHandshakeResult,\n} from \"../low-level-hooks/use-grist-handshake\"\nexport {\n GristHandshakeProvider,\n useGristHandshakeContext,\n useGristHandshakeContextOptional,\n type GristHandshakeContextValue,\n type GristHandshakeProviderProps,\n} from \"../components/grist-handshake-provider\"\nexport type {\n GristAuthz,\n GristCapabilities,\n GristConfig,\n GristCurrentTableState,\n GristGeneration,\n GristLifecycle,\n GristLifecyclePhase,\n GristLink,\n GristLinkState,\n GristMappingComplete,\n GristMappingIncomplete,\n GristMappingSource,\n GristMappingState,\n GristStreamFreshness,\n GristSync,\n GristTerminationReason,\n GristWidgetSnapshot,\n} from \"../internal/handshake\"\nexport {\n useGristRecord,\n type UseGristRecordSubscriptionResult,\n} from \"../low-level-hooks/use-grist-record\"\nexport { useGristRecords } from \"../low-level-hooks/use-grist-records\"\nexport {\n useGristNewRecord,\n type UseGristNewRecordResult,\n} from \"../low-level-hooks/use-grist-new-record\"\n\n// Mid-level: focused single-responsibility hooks\nexport {\n useGristActions,\n type GristActionsStatus,\n type UseGristActionsOptions,\n type UseGristActionsResult,\n} from \"../mid-level-hooks/use-grist-actions\"\nexport {\n useGristMappedRecord,\n type UseGristMappedRecordResult,\n} from \"../mid-level-hooks/use-grist-mapped-record\"\nexport {\n useGristTableOps,\n type GristTableOpsStatus,\n type UseGristTableOpsOptions,\n type UseGristTableOpsResult,\n} from \"../mid-level-hooks/use-grist-table-ops\"\nexport {\n useGristRowsFromTable,\n type UseGristRowsFromTableOptions,\n type UseGristRowsFromTableResult,\n} from \"../mid-level-hooks/use-grist-rows-from-table\"\nexport {\n useGristAccessToken,\n type UseGristAccessTokenOptions,\n type UseGristAccessTokenResult,\n} from \"../mid-level-hooks/use-grist-access-token\"\nexport {\n useGristSection,\n type UseGristSectionResult,\n} from \"../mid-level-hooks/use-grist-section\"\nexport {\n useGristWidgetOptions,\n type UseGristWidgetOptionsConfig,\n type UseGristWidgetOptionsResult,\n} from \"../mid-level-hooks/use-grist-widget-options\"\nexport {\n useGristWidgetOptionsFromContext,\n type UseGristWidgetOptionsFromContextConfig,\n type UseGristWidgetOptionsFromContextResult,\n} from \"../mid-level-hooks/use-grist-widget-options-from-context\"\n\nexport type {\n ListTablesOptions,\n ListColumnsOptions,\n GristColumnInfo,\n} from \"../internal/hooks/use-grist-reads\"\n\n// Focused \"just a table handle\" top-level hook (redundant with useGristWidget\n// for most cases). Prefer `useGrist().currentTableId` + `useGrist().fetchTable()`.\nexport {\n useGristTable,\n type UseGristTableOptions,\n type UseGristTableResult,\n} from \"../use-grist-table\"\n","import { useCallback, useMemo, useSyncExternalStore } from \"react\"\n\nimport type { GristDocApi, GristReadyOptions } from \"../../types\"\nimport {\n deriveCapabilities,\n deriveErrorMessage,\n deriveStatus,\n initialGristSnapshot,\n type GristCapabilities,\n type GristWidgetSnapshot,\n type GristWidgetStatus as HandshakeStatus,\n} from \"../handshake\"\nimport type { ManagerControls } from \"../handshake/manager\"\nimport { useGristManager } from \"../handshake-manager-context\"\n\n/** Canonical 4-state status — projected from the handshake snapshot. */\nexport type GristWidgetStatus = HandshakeStatus\n\nexport type UseGristCoreOptions = GristReadyOptions & {\n availabilityMaxAttempts?: number\n availabilityIntervalMs?: number\n logWhenNotEmbedded?: boolean\n}\n\nexport type UseGristCoreResult = {\n status: GristWidgetStatus\n isAvailable: boolean | null\n isReady: boolean\n error: string | null\n docApi: GristDocApi | null\n /** Derived from the handshake snapshot — same values as `useGristCapabilities()` on `/advanced`. */\n capabilities: GristCapabilities\n reload: () => Promise<void>\n}\n\n/** Project handshake availability for legacy `isAvailable` consumers. */\nexport function computeAvailabilityFromSnapshot(\n snapshot: GristWidgetSnapshot\n): boolean | null {\n const { phase } = snapshot.lifecycle\n if (phase === \"idle\" || phase === \"detecting\") return null\n if (phase === \"terminated\") {\n const reason = snapshot.lifecycle.reason.kind\n if (reason === \"not_embedded\" || reason === \"script_timeout\") return false\n return true\n }\n return true\n}\n\n/**\n * Core connectivity hook — always backed by {@link GristHandshakeManager}.\n */\nexport function useGristCore(options?: UseGristCoreOptions): UseGristCoreResult {\n const manager = useGristManager(options)\n return useGristCoreFromManager(manager)\n}\n\n/**\n * Subscribe to an existing manager and project its snapshot into\n * {@link UseGristCoreResult}.\n */\nexport function useGristCoreFromManager(manager: ManagerControls): UseGristCoreResult {\n const snapshot = useSyncExternalStore(\n manager.subscribe,\n manager.getSnapshot,\n initialGristSnapshot\n )\n const status = deriveStatus(snapshot)\n const error = deriveErrorMessage(snapshot)\n const docApi = useMemo<GristDocApi | null>(() => {\n if (snapshot.lifecycle.phase !== \"online\") return null\n if (typeof grist === \"undefined\") return null\n return grist.docApi ?? null\n }, [snapshot.lifecycle.phase, snapshot.generation])\n const isAvailable = computeAvailabilityFromSnapshot(snapshot)\n const reload = useCallback(async () => {\n manager.reload()\n }, [manager])\n const capabilities = useMemo(() => deriveCapabilities(snapshot), [snapshot])\n return useMemo(\n () => ({\n status,\n isAvailable,\n isReady: status === \"ready\",\n error,\n docApi,\n capabilities,\n reload,\n }),\n [status, isAvailable, error, docApi, capabilities, reload]\n )\n}\n","/**\n * Pure type contracts for the handshake / synchronization state machine.\n *\n * The machine models five orthogonal axes that together describe the widget's\n * relationship with the Grist host:\n *\n * 1. `LIFECYCLE` — protocol handshake (one-shot via `grist.ready`).\n * 2. `LINK` — RPC channel health (continuous, post-online).\n * 3. `AUTHZ` — effective access level granted by the host.\n * 4. `CONFIG` — column mapping contract with the end-user.\n * 5. `SYNC` — freshness of each event stream and ancillary RPCs.\n *\n * The legacy public `status` (\"booting\" / \"ready\" / \"unavailable\" / \"error\")\n * is a **pure projection** of the snapshot, computed in `./derive.ts`.\n *\n * No runtime side effects in this file — types only.\n */\n\nimport type { GristColumnsToMap, GristRequiredAccess, GristWidgetColumnMap } from \"../../types\"\n\n// ---------------------------------------------------------------------------\n// Branded primitives\n// ---------------------------------------------------------------------------\n\n/** Monotone counter bumped on reload / restart / link recovery / access widening. */\nexport type GristGeneration = number & { readonly __brand: \"GristGeneration\" }\n\nexport const ZERO_GENERATION = 0 as GristGeneration\n\nexport function nextGeneration(current: GristGeneration): GristGeneration {\n return (current + 1) as GristGeneration\n}\n\n// ---------------------------------------------------------------------------\n// 1. LIFECYCLE — handshake protocol\n// ---------------------------------------------------------------------------\n\nexport type GristLifecyclePhase =\n | \"idle\"\n | \"detecting\"\n | \"negotiating\"\n | \"online\"\n | \"terminated\"\n\nexport type GristTerminationReason =\n | { kind: \"script_timeout\"; attempts: number }\n | { kind: \"not_embedded\" }\n | { kind: \"ready_failed\"; message: string }\n | { kind: \"ready_timeout\"; afterMs: number }\n | { kind: \"host_rejected\"; message: string }\n | { kind: \"manual_terminate\" }\n\nexport type GristLifecycle =\n | { phase: \"idle\" }\n | { phase: \"detecting\"; attempts: number; startedAtMs: number }\n | { phase: \"negotiating\"; access: GristRequiredAccess; startedAtMs: number }\n | { phase: \"online\"; sinceMs: number; declaredAccess: GristRequiredAccess }\n | { phase: \"terminated\"; reason: GristTerminationReason; atMs: number }\n\n// ---------------------------------------------------------------------------\n// 2. LINK — RPC channel health\n// ---------------------------------------------------------------------------\n\nexport type GristLinkState = \"connected\" | \"stale\" | \"lost\"\n\nexport type GristLink = {\n state: GristLinkState\n /** ms timestamp of last successful RPC (or heartbeat). `null` before any. */\n lastSuccessMs: number | null\n /** Consecutive RPC failures since the last success. */\n missedPings: number\n}\n\n// ---------------------------------------------------------------------------\n// 3. AUTHZ — effective access\n// ---------------------------------------------------------------------------\n\nexport type GristAuthz =\n | { state: \"pending\"; declared: GristRequiredAccess }\n | {\n state: \"granted\"\n /** Effective level granted by the host. May differ from `declared`. */\n level: GristRequiredAccess\n declared: GristRequiredAccess\n /** True if granted level >= a previously granted level for the same widget. */\n widened: boolean\n }\n | { state: \"denied\"; declared: GristRequiredAccess; message?: string }\n\n// ---------------------------------------------------------------------------\n// 4. CONFIG — column-mapping contract\n// ---------------------------------------------------------------------------\n\n/**\n * Source that produced a mapping payload, used for deterministic merge by\n * {@link MappingResolver} (see `./mapping-resolver.ts`).\n */\nexport type GristMappingSource =\n | \"section_api\"\n | \"stream_record\"\n | \"stream_records\"\n | \"stream_new_record\"\n\n/**\n * Static priority order for mapping sources (lower index = higher priority).\n * `section_api` is the host's source-of-truth when available; stream payloads\n * are reactive fallbacks.\n */\nexport const MAPPING_SOURCE_PRIORITY: readonly GristMappingSource[] = [\n \"section_api\",\n \"stream_record\",\n \"stream_records\",\n \"stream_new_record\",\n] as const\n\nexport type GristMappingComplete = {\n state: \"complete\"\n declared: GristColumnsToMap\n mappings: GristWidgetColumnMap\n /** `\"merged\"` is used internally when multiple sources contributed. */\n source: GristMappingSource | \"merged\"\n reportedAtMs: number\n}\n\nexport type GristMappingIncomplete = {\n state: \"incomplete\"\n declared: GristColumnsToMap\n mappings: GristWidgetColumnMap\n missing: readonly string[]\n emptyMultiples: readonly string[]\n source: GristMappingSource | \"merged\"\n reportedAtMs: number\n}\n\nexport type GristMappingState =\n /** Widget did not declare any `columns` in `grist.ready`. */\n | { state: \"not_declared\" }\n /** Columns declared but no host report yet received. */\n | { state: \"pending\"; declared: GristColumnsToMap; declaredAtMs: number }\n | GristMappingIncomplete\n | GristMappingComplete\n /** User just modified the mapping; previous payload preserved to avoid UI flicker. */\n | {\n state: \"invalidating\"\n previous: GristMappingIncomplete | GristMappingComplete\n sinceMs: number\n }\n /** Declared but no host report after a timeout. Soft state — payload may still arrive. */\n | { state: \"unreported\"; declared: GristColumnsToMap; afterMs: number }\n\nexport type GristConfig = {\n mappings: GristMappingState\n}\n\n// ---------------------------------------------------------------------------\n// 5. SYNC — stream freshness\n// ---------------------------------------------------------------------------\n\nexport type GristStreamFreshness =\n | { state: \"cold\" }\n | { state: \"warming\"; sinceMs: number }\n | { state: \"hot\"; lastEventMs: number; generation: GristGeneration; payloadHash: string }\n | {\n state: \"stale\"\n lastEventMs: number\n reason: \"link_lost\" | \"tab_hidden\" | \"host_quiet\"\n }\n\nexport type GristCurrentTableState =\n | { state: \"unknown\" }\n | { state: \"known\"; tableId: string; refreshing: boolean }\n /** Local error (e.g. `selectedTable.getTableId()` rejected) — does NOT propagate to global status. */\n | { state: \"errored\"; message: string; sinceMs: number }\n\nexport type GristSync = {\n record: GristStreamFreshness\n records: GristStreamFreshness\n options: GristStreamFreshness\n newRecord: GristStreamFreshness\n currentTable: GristCurrentTableState\n}\n\n// ---------------------------------------------------------------------------\n// SNAPSHOT (aggregate)\n// ---------------------------------------------------------------------------\n\nexport type GristWidgetSnapshot = {\n generation: GristGeneration\n lifecycle: GristLifecycle\n link: GristLink\n authz: GristAuthz\n config: GristConfig\n sync: GristSync\n}\n\n// ---------------------------------------------------------------------------\n// PROJECTION — legacy 4-state status + capabilities\n// ---------------------------------------------------------------------------\n\n/** Mirrors the long-standing public type, kept for compat. */\nexport type GristWidgetStatus = \"booting\" | \"ready\" | \"unavailable\" | \"error\"\n\n/**\n * Pre-computed booleans that widget code should consume directly instead of\n * re-deriving from `status` + mapping flags + writes flags.\n *\n * Every field is a **pure function of the snapshot** and is referentially\n * stable across renders that don't change the snapshot's relevant slices.\n */\nexport type GristCapabilities = {\n /** `lifecycle.online` + `link != lost` + `authz.state === \"granted\"` with read or higher. */\n canRead: boolean\n /** `canRead` + (`mappings.state` is `complete` or `not_declared`). */\n canRender: boolean\n /** `canRender` + `authz.level === \"full\"`. Required before any write. */\n canWriteRecords: boolean\n /** `canWriteRecords` + `sync.currentTable.state === \"known\"`. */\n canWriteSchema: boolean\n /** `canRead` + `link === \"connected\"`. */\n canFetchTable: boolean\n /** `canRead` + `sync.record.state === \"hot\"` + mappings not invalidating. */\n hasFreshSelection: boolean\n /** `mappings.state === \"complete\" || \"not_declared\"`. */\n hasUsableMappings: boolean\n /** Logical column names still missing from user mapping (incomplete only). */\n missingMappings: readonly string[]\n /** `allowMultiple` columns mapped to `[]` (incomplete only). */\n emptyMultipleMappings: readonly string[]\n}\n\n// ---------------------------------------------------------------------------\n// Initial snapshot factory\n// ---------------------------------------------------------------------------\n\nexport function initialGristSnapshot(): GristWidgetSnapshot {\n return {\n generation: ZERO_GENERATION,\n lifecycle: { phase: \"idle\" },\n link: { state: \"connected\", lastSuccessMs: null, missedPings: 0 },\n authz: { state: \"pending\", declared: \"read table\" },\n config: { mappings: { state: \"not_declared\" } },\n sync: {\n record: { state: \"cold\" },\n records: { state: \"cold\" },\n options: { state: \"cold\" },\n newRecord: { state: \"cold\" },\n currentTable: { state: \"unknown\" },\n },\n }\n}\n","/**\n * Action union for the handshake state machine.\n *\n * Every action that can mutate the snapshot is listed here as a discriminated\n * union — exhaustiveness checks in the reducer rely on it.\n *\n * Actions either:\n * - target a single axis (e.g. `RPC_SUCCESS` → link), OR\n * - target multiple axes via a single transition (e.g. `NEGOTIATE_SUCCESS`\n * advances lifecycle AND authz at once).\n *\n * Generation-carrying actions can be **dropped** by the reducer if their\n * `generation` is older than the snapshot's current generation; this is the\n * primary defence against stale events arriving after a reload/restart.\n */\n\nimport type { GristColumnsToMap, GristRequiredAccess, GristWidgetColumnMap } from \"../../types\"\nimport type {\n GristGeneration,\n GristMappingSource,\n GristTerminationReason,\n} from \"./types\"\n\n// ---------------------------------------------------------------------------\n// Lifecycle actions\n// ---------------------------------------------------------------------------\n\nexport type GristLifecycleAction =\n | { type: \"DETECT_START\"; atMs: number }\n | { type: \"DETECT_TICK\"; attempts: number; atMs: number }\n | { type: \"DETECT_AVAILABLE\"; atMs: number }\n | { type: \"DETECT_TIMEOUT\"; attempts: number; atMs: number }\n | { type: \"NOT_EMBEDDED\"; atMs: number }\n | { type: \"NEGOTIATE_START\"; access: GristRequiredAccess; atMs: number }\n | {\n type: \"NEGOTIATE_SUCCESS\"\n access: GristRequiredAccess\n grantedLevel: GristRequiredAccess\n atMs: number\n }\n | { type: \"NEGOTIATE_FAIL\"; reason: GristTerminationReason; atMs: number }\n | { type: \"TERMINATE\"; reason: GristTerminationReason; atMs: number }\n\n// ---------------------------------------------------------------------------\n// Link actions\n// ---------------------------------------------------------------------------\n\nexport type GristLinkAction =\n | { type: \"RPC_SUCCESS\"; atMs: number }\n | { type: \"RPC_FAILURE\"; atMs: number; kind: \"timeout\" | \"rejected\" | \"other\" }\n | { type: \"HEARTBEAT_OK\"; atMs: number }\n | { type: \"HEARTBEAT_FAIL\"; atMs: number }\n\n// ---------------------------------------------------------------------------\n// Authz actions\n// ---------------------------------------------------------------------------\n\nexport type GristAuthzAction = {\n type: \"AUTHZ_REPORT\"\n /** Effective access level reported by Grist (`onOptions` / `interaction.access_level`). */\n level: GristRequiredAccess\n atMs: number\n generation: GristGeneration\n}\n\n// ---------------------------------------------------------------------------\n// Generation control\n// ---------------------------------------------------------------------------\n\nexport type GristGenerationAction = {\n type: \"BUMP_GENERATION\"\n atMs: number\n reason: \"reload\" | \"restart\" | \"link_recovery\" | \"widening\"\n}\n\n// ---------------------------------------------------------------------------\n// Config / mapping actions\n// ---------------------------------------------------------------------------\n\nexport type GristMappingAction =\n | {\n type: \"MAPPING_DECLARE\"\n declared: GristColumnsToMap\n atMs: number\n generation: GristGeneration\n }\n | {\n type: \"MAPPING_REPORT\"\n source: GristMappingSource\n payload: GristWidgetColumnMap | null\n atMs: number\n generation: GristGeneration\n }\n | { type: \"MAPPING_INVALIDATE\"; atMs: number; generation: GristGeneration }\n | { type: \"MAPPING_INVALIDATION_RESOLVE\"; atMs: number; generation: GristGeneration }\n | { type: \"MAPPING_TIMEOUT\"; atMs: number; generation: GristGeneration }\n\n// ---------------------------------------------------------------------------\n// Sync actions\n// ---------------------------------------------------------------------------\n\nexport type GristSyncStream = \"record\" | \"records\" | \"options\" | \"newRecord\"\n\nexport type GristSyncAction =\n | {\n type: \"STREAM_RECEIVED\"\n stream: GristSyncStream\n payloadHash: string\n atMs: number\n generation: GristGeneration\n }\n | { type: \"STREAM_WARMING\"; stream: GristSyncStream; atMs: number }\n | {\n type: \"STREAM_STALE\"\n stream: GristSyncStream\n atMs: number\n reason: \"link_lost\" | \"tab_hidden\" | \"host_quiet\"\n }\n | { type: \"CURRENT_TABLE_KNOWN\"; tableId: string; atMs: number }\n | { type: \"CURRENT_TABLE_REFRESH_START\"; atMs: number }\n | { type: \"CURRENT_TABLE_ERROR\"; message: string; atMs: number }\n\n// ---------------------------------------------------------------------------\n// Top-level union\n// ---------------------------------------------------------------------------\n\nexport type GristAction =\n | GristLifecycleAction\n | GristLinkAction\n | GristAuthzAction\n | GristGenerationAction\n | GristMappingAction\n | GristSyncAction\n\n/**\n * Type guard: actions that carry a `generation` field and can therefore be\n * dropped by the reducer if `action.generation < snapshot.generation`.\n */\nexport function hasGenerationStamp(\n action: GristAction\n): action is Extract<GristAction, { generation: GristGeneration }> {\n return \"generation\" in action && typeof action.generation === \"number\"\n}\n","import type { GristColumnsToMap, GristColumnToMap, GristWidgetColumnMap } from \"../types\"\n\nexport type ValidateColumnMappingsResult = {\n ok: boolean\n /**\n * `true` while the host has not yet reported column mappings (e.g. between\n * `grist.ready()` resolving and the first `onRecord` / `onRecords` callback,\n * or before `sectionApi.mappings()` returns). Callers should not gate UI or\n * emit mapping alerts while pending.\n */\n pending?: boolean\n /** Logical names of required columns the user has not yet mapped. */\n missing: string[]\n /** Logical names of `allowMultiple` columns mapped to an empty list. */\n emptyMultiples: string[]\n}\n\n/**\n * Merges mapping payloads from `onRecord`, `onRecords`, and `sectionApi.mappings()`.\n * Later sources win; `undefined` sources are skipped; `null` means \"host reported\n * no mappings\" but still counts as a known (empty) payload.\n */\nexport function mergeWidgetColumnMaps(\n ...sources: (GristWidgetColumnMap | null | undefined)[]\n): GristWidgetColumnMap | null | undefined {\n let merged: GristWidgetColumnMap = {}\n let sawDefined = false\n for (const src of sources) {\n if (src === undefined) continue\n sawDefined = true\n if (src === null) continue\n merged = { ...merged, ...src }\n }\n if (!sawDefined) return undefined\n return merged\n}\n\nfunction asColumnSpec(entry: string | GristColumnToMap): GristColumnToMap {\n return typeof entry === \"string\" ? { name: entry } : entry\n}\n\n/**\n * Verifies that the current Grist mappings cover every required column declared\n * in `grist.ready({ columns })`.\n *\n * Use this before reading `mappedRecord` so you can render a \"please configure\n * the widget\" prompt instead of a confusing `null`.\n */\nexport function validateColumnMappings(\n spec: GristColumnsToMap | undefined | null,\n mappings: GristWidgetColumnMap | null | undefined\n): ValidateColumnMappingsResult {\n const missing: string[] = []\n const emptyMultiples: string[] = []\n if (!spec) return { ok: true, pending: false, missing, emptyMultiples }\n\n const m = mappings ?? {}\n for (const raw of spec) {\n const col = asColumnSpec(raw)\n if (col.optional) continue\n const value = m[col.name]\n if (col.allowMultiple) {\n if (value === undefined) {\n missing.push(col.name)\n } else if (!Array.isArray(value) || value.length === 0) {\n emptyMultiples.push(col.name)\n }\n } else {\n if (value === undefined || value === null || value === \"\") missing.push(col.name)\n }\n }\n return {\n ok: missing.length === 0 && emptyMultiples.length === 0,\n pending: false,\n missing,\n emptyMultiples,\n }\n}\n\n/** Status while waiting for the host to report mappings (see `pending`). */\nexport const pendingColumnMappingStatus: ValidateColumnMappingsResult = {\n ok: true,\n pending: true,\n missing: [],\n emptyMultiples: [],\n}\n","/**\n * Deterministic merge of column-mapping payloads across multiple sources.\n *\n * Why this exists\n * ---------------\n * Grist reports column mappings from several places, possibly in different\n * orders depending on network timing and event scheduling:\n *\n * - `grist.sectionApi.mappings()` (one-shot promise),\n * - `grist.onRecord` (stream),\n * - `grist.onRecords` (stream, occasionally diverges from `onRecord`),\n * - `grist.onNewRecord` (stream, only on the \"new\" placeholder row).\n *\n * A \"later wins\" merge would let arrival order dictate the final mappings —\n * unstable and surprising. This resolver applies a fixed priority order\n * (see {@link MAPPING_SOURCE_PRIORITY}) so the resolution is a **pure function\n * of the set of received payloads**, independent of the order in which they\n * arrived.\n *\n * Generation safety\n * -----------------\n * Each payload is ingested with the {@link GristGeneration} that was active\n * when the source produced it. Payloads with `generation` strictly less than\n * the resolver's current generation are silently dropped — this is how the\n * machine recovers from reloads / restarts without ever applying a stale\n * mapping over a fresh one.\n */\n\nimport { validateColumnMappings } from \"../../helpers/grist-mappings\"\nimport type { GristColumnsToMap, GristWidgetColumnMap } from \"../../types\"\nimport {\n MAPPING_SOURCE_PRIORITY,\n type GristGeneration,\n type GristMappingComplete,\n type GristMappingIncomplete,\n type GristMappingSource,\n type GristMappingState,\n} from \"./types\"\n\nexport type MappingPayload = {\n source: GristMappingSource\n /** `null` is a known empty payload (host explicitly reported no mappings). */\n payload: GristWidgetColumnMap | null\n receivedAtMs: number\n generation: GristGeneration\n}\n\nexport type ResolveOutcome =\n | {\n kind: \"no_change\"\n reason: \"stale_generation\" | \"duplicate_payload\"\n }\n | {\n kind: \"resolved\"\n state: GristMappingComplete | GristMappingIncomplete\n }\n | { kind: \"still_pending\" }\n\n/**\n * Stateful resolver. One instance per declaration of `columns` (i.e. per\n * widget lifetime, modulo `reset()` on declare-change or restart).\n *\n * The resolver does NOT own the higher-level {@link GristMappingState} —\n * it returns concrete `complete` / `incomplete` payloads that the reducer\n * folds into the snapshot.\n */\nexport class MappingResolver {\n private declared: GristColumnsToMap\n private currentGeneration: GristGeneration\n /** Latest accepted payload per source. */\n private payloads = new Map<GristMappingSource, MappingPayload>()\n /** Last resolved state, used to detect duplicate ingests. */\n private lastResolvedHash: string | null = null\n\n constructor(declared: GristColumnsToMap, generation: GristGeneration) {\n this.declared = declared\n this.currentGeneration = generation\n }\n\n /**\n * Replace the declared columns (e.g. user passed a new `columns` array to\n * the provider). Clears accumulated payloads — they were validated against\n * the previous declaration.\n */\n redeclare(declared: GristColumnsToMap, atGeneration: GristGeneration): void {\n this.declared = declared\n this.currentGeneration = atGeneration\n this.payloads.clear()\n this.lastResolvedHash = null\n }\n\n /** Bump the active generation. Payloads from older generations are dropped. */\n bumpGeneration(next: GristGeneration): void {\n if (next <= this.currentGeneration) return\n this.currentGeneration = next\n // Drop any cached payloads from older generations.\n for (const [src, p] of this.payloads) {\n if (p.generation < next) this.payloads.delete(src)\n }\n this.lastResolvedHash = null\n }\n\n /** Forget every payload and reset the hash. The declaration is preserved. */\n reset(): void {\n this.payloads.clear()\n this.lastResolvedHash = null\n }\n\n /** Cheap, deterministic snapshot for tests/debug. */\n knownSources(): readonly GristMappingSource[] {\n return [...this.payloads.keys()]\n }\n\n /**\n * Ingest one payload. Returns a {@link ResolveOutcome}:\n * - `still_pending` — no payloads accepted yet.\n * - `no_change/stale_*` — input generation is older than current.\n * - `no_change/duplicate_payload` — input did not move the resolution.\n * - `resolved` — new resolution differs from the previous one.\n *\n * Call {@link current} to read the latest resolution without affecting\n * the duplicate-detection hash.\n */\n ingest(input: MappingPayload): ResolveOutcome {\n if (input.generation < this.currentGeneration) {\n return { kind: \"no_change\", reason: \"stale_generation\" }\n }\n if (input.generation > this.currentGeneration) {\n this.bumpGeneration(input.generation)\n }\n this.payloads.set(input.source, input)\n\n const computed = this.compute()\n if (computed === null) return { kind: \"still_pending\" }\n\n const hash = computeMappingHash(\n computed.mappings,\n computed.state === \"complete\",\n computed.source\n )\n if (hash === this.lastResolvedHash) {\n return { kind: \"no_change\", reason: \"duplicate_payload\" }\n }\n this.lastResolvedHash = hash\n return { kind: \"resolved\", state: computed }\n }\n\n /**\n * Return the resolver's current resolution (pure read). Equivalent to\n * `ingest(...)` without mutating any state — used by tests and by the\n * reducer when re-applying after `MAPPING_INVALIDATION_RESOLVE`.\n */\n current(): ResolveOutcome {\n const computed = this.compute()\n if (computed === null) return { kind: \"still_pending\" }\n return { kind: \"resolved\", state: computed }\n }\n\n /**\n * Compute the current best mapping from accumulated payloads (pure).\n *\n * Priority: higher-priority sources **override** specific keys from\n * lower-priority sources. Keys absent from a higher-priority source fall\n * back to lower-priority sources. This matches the production behaviour\n * where `sectionApi.mappings()` is authoritative for the keys it reports,\n * but stream payloads can fill in keys the section_api hasn't reported yet.\n */\n private compute(): GristMappingComplete | GristMappingIncomplete | null {\n if (this.payloads.size === 0) return null\n\n const merged: GristWidgetColumnMap = {}\n let latestReportedAt = 0\n const contributors: GristMappingSource[] = []\n\n for (const src of [...MAPPING_SOURCE_PRIORITY].reverse()) {\n const p = this.payloads.get(src)\n if (!p) continue\n contributors.push(src)\n if (p.receivedAtMs > latestReportedAt) latestReportedAt = p.receivedAtMs\n if (p.payload === null) continue\n for (const [k, v] of Object.entries(p.payload)) {\n merged[k] = v\n }\n }\n\n if (contributors.length === 0) return null\n\n const validation = validateColumnMappings(this.declared, merged)\n const source: GristMappingSource | \"merged\" =\n contributors.length === 1 ? (contributors[0] as GristMappingSource) : \"merged\"\n\n if (validation.ok) {\n return {\n state: \"complete\",\n declared: this.declared,\n mappings: merged,\n source,\n reportedAtMs: latestReportedAt,\n }\n }\n\n return {\n state: \"incomplete\",\n declared: this.declared,\n mappings: merged,\n missing: validation.missing,\n emptyMultiples: validation.emptyMultiples,\n source,\n reportedAtMs: latestReportedAt,\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Stable hash for a {complete|incomplete} mapping payload. Deterministic\n * across re-resolutions of identical inputs so the resolver can de-duplicate.\n */\nexport function computeMappingHash(\n mappings: GristWidgetColumnMap,\n ok: boolean,\n source: GristMappingSource | \"merged\"\n): string {\n const keys = Object.keys(mappings).sort()\n const body = keys.map((k) => {\n const v = mappings[k]\n if (Array.isArray(v)) return `${k}=[${v.join(\",\")}]`\n return `${k}=${v ?? \"\"}`\n }).join(\"|\")\n return `${ok ? \"OK\" : \"KO\"}::${source}::${body}`\n}\n\n/**\n * Decide whether to treat a newly arrived payload as a graceful invalidation\n * of the previous mapping (user just edited the section's mapping settings).\n *\n * Returns `true` if `previous` was `complete`/`incomplete` and `next` differs\n * by at least one key. Pure function, no side effects.\n */\nexport function shouldInvalidate(\n previous: GristMappingState,\n nextMappings: GristWidgetColumnMap\n): boolean {\n if (previous.state !== \"complete\" && previous.state !== \"incomplete\") return false\n const a = previous.mappings\n const aKeys = Object.keys(a)\n const bKeys = Object.keys(nextMappings)\n if (aKeys.length !== bKeys.length) return true\n for (const k of aKeys) {\n const av = a[k]\n const bv = nextMappings[k]\n if (Array.isArray(av) && Array.isArray(bv)) {\n if (av.length !== bv.length) return true\n for (let i = 0; i < av.length; i++) if (av[i] !== bv[i]) return true\n } else if (av !== bv) {\n return true\n }\n }\n return false\n}\n","/**\n * Access-level helpers used by the handshake state machine.\n *\n * Kept in a tiny dedicated file because the same ranking is needed by the\n * reducer (`widened` flag, future re-negotiation logic) and potentially by\n * effects (decide whether to re-issue `grist.ready`).\n */\n\nimport type { GristRequiredAccess } from \"../../types\"\n\n/** Strictly-ordered ranking. Higher rank = stronger access. */\nexport const ACCESS_RANK: Record<GristRequiredAccess, number> = {\n none: 0,\n \"read table\": 1,\n full: 2,\n}\n\nconst GRIST_ACCESS_LEVELS: readonly GristRequiredAccess[] = [\"none\", \"read table\", \"full\"]\n\n/** Parse `interaction.access_level` / `accessLevel` from Grist `onOptions`. */\nexport function parseGristRequiredAccess(\n raw: string | undefined\n): GristRequiredAccess | null {\n if (!raw) return null\n return (GRIST_ACCESS_LEVELS as readonly string[]).includes(raw)\n ? (raw as GristRequiredAccess)\n : null\n}\n\n/** True when the host-granted level meets what the widget declared in `grist.ready`. */\nexport function isAccessSufficient(\n granted: GristRequiredAccess,\n required: GristRequiredAccess\n): boolean {\n return ACCESS_RANK[granted] >= ACCESS_RANK[required]\n}\n\n/** Creator-panel label for a Grist access level (for user-facing copy). */\nexport function gristAccessLevelLabel(level: GristRequiredAccess): string {\n switch (level) {\n case \"none\":\n return \"No document access\"\n case \"read table\":\n return \"Read selected table\"\n case \"full\":\n return \"Full document access\"\n }\n}\n\nexport function formatInsufficientAccessMessage(\n required: GristRequiredAccess,\n _granted: GristRequiredAccess\n): string {\n const need = gristAccessLevelLabel(required)\n return `Set Access level to at least \"${need}\" in the Custom widget settings.`\n}\n\nexport function isAccessWidening(\n previous: GristRequiredAccess | null,\n next: GristRequiredAccess\n): boolean {\n if (previous === null) return true\n return ACCESS_RANK[next] > ACCESS_RANK[previous]\n}\n","/**\n * Pure reducer for the handshake state machine.\n *\n * Contract\n * --------\n * - **Pure**: `(snapshot, action, ctx) => nextSnapshot`. No I/O, no globals,\n * no Date.now() — callers pass timestamps via `action.atMs` and (optionally)\n * a context object for the {@link MappingResolver}.\n * - **Total**: every (snapshot.lifecycle.phase × action.type) pair returns\n * either an updated snapshot or the identity snapshot.\n * - **Idempotent for replays**: same generation-stamped action applied twice\n * leaves the snapshot unchanged (`payloadHash` short-circuit on streams,\n * resolver hash short-circuit on mappings).\n * - **Generation-safe**: any action carrying a `generation` lower than the\n * current snapshot generation is dropped silently — this is what makes\n * the SDK robust to late events after reload/restart.\n *\n * The reducer never throws. Invariant violations are surfaced via\n * `./invariants.ts` if the caller opts in.\n */\n\nimport {\n hasGenerationStamp,\n type GristAction,\n type GristMappingAction,\n type GristSyncAction,\n type GristSyncStream,\n} from \"./actions\"\nimport { MappingResolver } from \"./mapping-resolver\"\nimport {\n ZERO_GENERATION,\n nextGeneration,\n type GristGeneration,\n type GristLifecycle,\n type GristLink,\n type GristMappingComplete,\n type GristMappingIncomplete,\n type GristMappingState,\n type GristStreamFreshness,\n type GristSync,\n type GristTerminationReason,\n type GristWidgetSnapshot,\n} from \"./types\"\nimport {\n ACCESS_RANK,\n formatInsufficientAccessMessage,\n isAccessSufficient,\n} from \"./access\"\nimport type { GristRequiredAccess } from \"../../types\"\n\n/**\n * Per-machine mutable companion to the snapshot. The reducer reads from it\n * (and lets the resolver mutate it in-place) so the snapshot itself can stay\n * a deeply-frozen, immutable value.\n */\nexport type GristReducerContext = {\n resolver: MappingResolver | null\n}\n\nexport function createReducerContext(): GristReducerContext {\n return { resolver: null }\n}\n\n/**\n * Tuning knobs. Pass `Partial<ReducerConfig>` to override defaults; values\n * are read by the reducer at each step.\n */\nexport type ReducerConfig = {\n /** Stale-pings threshold before link transitions to `stale`. */\n staleAfterMissed: number\n /** Stale-pings threshold before link transitions to `lost`. */\n lostAfterMissed: number\n}\n\nexport const DEFAULT_REDUCER_CONFIG: ReducerConfig = {\n staleAfterMissed: 1,\n lostAfterMissed: 3,\n}\n\n// ---------------------------------------------------------------------------\n// Reducer entry point\n// ---------------------------------------------------------------------------\n\nexport function gristReducer(\n snapshot: GristWidgetSnapshot,\n action: GristAction,\n ctx: GristReducerContext,\n config: ReducerConfig = DEFAULT_REDUCER_CONFIG\n): GristWidgetSnapshot {\n // Generation gate — applied to every action that carries a generation.\n if (hasGenerationStamp(action) && action.generation < snapshot.generation) {\n return snapshot\n }\n\n switch (action.type) {\n // -- Lifecycle --------------------------------------------------------\n case \"DETECT_START\":\n return reduceDetectStart(snapshot, action.atMs)\n case \"DETECT_TICK\":\n return reduceDetectTick(snapshot, action.attempts)\n case \"DETECT_AVAILABLE\":\n return reduceDetectAvailable(snapshot, action.atMs)\n case \"DETECT_TIMEOUT\":\n return reduceTerminate(snapshot, {\n kind: \"script_timeout\",\n attempts: action.attempts,\n }, action.atMs)\n case \"NOT_EMBEDDED\":\n return reduceTerminate(snapshot, { kind: \"not_embedded\" }, action.atMs)\n case \"NEGOTIATE_START\":\n return reduceNegotiateStart(snapshot, action.access, action.atMs)\n case \"NEGOTIATE_SUCCESS\":\n return reduceNegotiateSuccess(snapshot, action.access, action.grantedLevel, action.atMs)\n case \"NEGOTIATE_FAIL\":\n return reduceTerminate(snapshot, action.reason, action.atMs)\n case \"TERMINATE\":\n return reduceTerminate(snapshot, action.reason, action.atMs)\n case \"AUTHZ_REPORT\":\n return reduceAuthzReport(snapshot, action.level, action.atMs)\n\n // -- Link -------------------------------------------------------------\n case \"RPC_SUCCESS\":\n case \"HEARTBEAT_OK\":\n return reduceLinkSuccess(snapshot, action.atMs)\n case \"RPC_FAILURE\":\n case \"HEARTBEAT_FAIL\":\n return reduceLinkFailure(snapshot, action.atMs, config)\n\n // -- Generation -------------------------------------------------------\n case \"BUMP_GENERATION\":\n return reduceBumpGeneration(snapshot, ctx)\n\n // -- Config / mappings -----------------------------------------------\n case \"MAPPING_DECLARE\":\n case \"MAPPING_REPORT\":\n case \"MAPPING_INVALIDATE\":\n case \"MAPPING_INVALIDATION_RESOLVE\":\n case \"MAPPING_TIMEOUT\":\n return reduceMapping(snapshot, action, ctx)\n\n // -- Sync -------------------------------------------------------------\n case \"STREAM_RECEIVED\":\n case \"STREAM_WARMING\":\n case \"STREAM_STALE\":\n return reduceStream(snapshot, action)\n case \"CURRENT_TABLE_KNOWN\":\n return updateSync(snapshot, {\n ...snapshot.sync,\n currentTable: { state: \"known\", tableId: action.tableId, refreshing: false },\n })\n case \"CURRENT_TABLE_REFRESH_START\":\n if (snapshot.sync.currentTable.state !== \"known\") return snapshot\n return updateSync(snapshot, {\n ...snapshot.sync,\n currentTable: { ...snapshot.sync.currentTable, refreshing: true },\n })\n case \"CURRENT_TABLE_ERROR\":\n return updateSync(snapshot, {\n ...snapshot.sync,\n currentTable: { state: \"errored\", message: action.message, sinceMs: action.atMs },\n })\n\n default: {\n // Exhaustiveness check — TypeScript guarantees this is `never`.\n const _exhaustive: never = action\n void _exhaustive\n return snapshot\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Lifecycle transitions\n// ---------------------------------------------------------------------------\n\nfunction reduceDetectStart(snapshot: GristWidgetSnapshot, atMs: number): GristWidgetSnapshot {\n if (snapshot.lifecycle.phase !== \"idle\") return snapshot\n return {\n ...snapshot,\n lifecycle: { phase: \"detecting\", attempts: 0, startedAtMs: atMs },\n }\n}\n\nfunction reduceDetectTick(snapshot: GristWidgetSnapshot, attempts: number): GristWidgetSnapshot {\n if (snapshot.lifecycle.phase !== \"detecting\") return snapshot\n if (attempts === snapshot.lifecycle.attempts) return snapshot\n return {\n ...snapshot,\n lifecycle: { ...snapshot.lifecycle, attempts },\n }\n}\n\nfunction reduceDetectAvailable(\n snapshot: GristWidgetSnapshot,\n atMs: number\n): GristWidgetSnapshot {\n if (snapshot.lifecycle.phase !== \"detecting\" && snapshot.lifecycle.phase !== \"idle\") {\n return snapshot\n }\n return {\n ...snapshot,\n lifecycle: {\n phase: \"negotiating\",\n access: snapshot.authz.declared,\n startedAtMs: atMs,\n },\n }\n}\n\nfunction reduceNegotiateStart(\n snapshot: GristWidgetSnapshot,\n access: GristWidgetSnapshot[\"authz\"][\"declared\"],\n atMs: number\n): GristWidgetSnapshot {\n // Allowed from `detecting` (first negotiate), `negotiating` (re-arm),\n // or `online` (access widening triggers re-negotiation in place).\n if (snapshot.lifecycle.phase === \"terminated\") return snapshot\n return {\n ...snapshot,\n authz: { state: \"pending\", declared: access },\n lifecycle:\n snapshot.lifecycle.phase === \"online\"\n ? snapshot.lifecycle\n : { phase: \"negotiating\", access, startedAtMs: atMs },\n }\n}\n\nfunction authzDeclared(snapshot: GristWidgetSnapshot): GristRequiredAccess {\n return snapshot.authz.declared\n}\n\nfunction reduceAuthzReport(\n snapshot: GristWidgetSnapshot,\n level: GristRequiredAccess,\n _atMs: number\n): GristWidgetSnapshot {\n if (snapshot.lifecycle.phase !== \"online\") return snapshot\n\n const declared = authzDeclared(snapshot)\n\n if (isAccessSufficient(level, declared)) {\n const previousLevel =\n snapshot.authz.state === \"granted\" ? snapshot.authz.level : null\n const widened =\n previousLevel !== null && ACCESS_RANK[level] > ACCESS_RANK[previousLevel]\n const nextAuthz = {\n state: \"granted\" as const,\n level,\n declared,\n widened,\n }\n if (\n snapshot.authz.state === \"granted\" &&\n snapshot.authz.level === nextAuthz.level &&\n snapshot.authz.declared === nextAuthz.declared &&\n snapshot.authz.widened === nextAuthz.widened\n ) {\n return snapshot\n }\n return { ...snapshot, authz: nextAuthz }\n }\n\n const message = formatInsufficientAccessMessage(declared, level)\n const nextAuthz = { state: \"denied\" as const, declared, message }\n if (\n snapshot.authz.state === \"denied\" &&\n snapshot.authz.declared === nextAuthz.declared &&\n snapshot.authz.message === nextAuthz.message\n ) {\n return snapshot\n }\n return { ...snapshot, authz: nextAuthz }\n}\n\nfunction reduceNegotiateSuccess(\n snapshot: GristWidgetSnapshot,\n access: GristWidgetSnapshot[\"authz\"][\"declared\"],\n grantedLevel: GristWidgetSnapshot[\"authz\"][\"declared\"],\n atMs: number\n): GristWidgetSnapshot {\n if (snapshot.lifecycle.phase === \"terminated\") return snapshot\n\n const previousLevel =\n snapshot.authz.state === \"granted\" ? snapshot.authz.level : null\n const widened =\n previousLevel !== null && ACCESS_RANK[grantedLevel] > ACCESS_RANK[previousLevel]\n\n return {\n ...snapshot,\n authz: {\n state: \"granted\",\n level: grantedLevel,\n declared: access,\n widened,\n },\n lifecycle:\n snapshot.lifecycle.phase === \"online\"\n ? snapshot.lifecycle\n : { phase: \"online\", sinceMs: atMs, declaredAccess: access },\n link: { state: \"connected\", lastSuccessMs: atMs, missedPings: 0 },\n }\n}\n\nfunction reduceTerminate(\n snapshot: GristWidgetSnapshot,\n reason: GristTerminationReason,\n atMs: number\n): GristWidgetSnapshot {\n if (snapshot.lifecycle.phase === \"terminated\") return snapshot\n const lifecycle: GristLifecycle = { phase: \"terminated\", reason, atMs }\n return { ...snapshot, lifecycle }\n}\n\n// ---------------------------------------------------------------------------\n// Link transitions\n// ---------------------------------------------------------------------------\n\nfunction reduceLinkSuccess(snapshot: GristWidgetSnapshot, atMs: number): GristWidgetSnapshot {\n if (snapshot.lifecycle.phase !== \"online\") return snapshot\n const link: GristLink = { state: \"connected\", lastSuccessMs: atMs, missedPings: 0 }\n if (\n snapshot.link.state === link.state &&\n snapshot.link.lastSuccessMs === link.lastSuccessMs &&\n snapshot.link.missedPings === link.missedPings\n ) {\n return snapshot\n }\n return { ...snapshot, link }\n}\n\nfunction reduceLinkFailure(\n snapshot: GristWidgetSnapshot,\n _atMs: number,\n config: ReducerConfig\n): GristWidgetSnapshot {\n if (snapshot.lifecycle.phase !== \"online\") return snapshot\n const missed = snapshot.link.missedPings + 1\n let state: GristLink[\"state\"] = snapshot.link.state\n if (missed >= config.lostAfterMissed) state = \"lost\"\n else if (missed >= config.staleAfterMissed) state = \"stale\"\n return {\n ...snapshot,\n link: { state, lastSuccessMs: snapshot.link.lastSuccessMs, missedPings: missed },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Generation\n// ---------------------------------------------------------------------------\n\nfunction reduceBumpGeneration(\n snapshot: GristWidgetSnapshot,\n ctx: GristReducerContext\n): GristWidgetSnapshot {\n const next = nextGeneration(snapshot.generation)\n ctx.resolver?.bumpGeneration(next)\n return {\n ...snapshot,\n generation: next,\n // Marking streams as warming again ensures hooks treat the data as\n // \"in transit\" until a fresh event arrives under the new generation.\n sync: {\n ...snapshot.sync,\n record: degradeForRefresh(snapshot.sync.record, snapshot.generation),\n records: degradeForRefresh(snapshot.sync.records, snapshot.generation),\n options: degradeForRefresh(snapshot.sync.options, snapshot.generation),\n newRecord: degradeForRefresh(snapshot.sync.newRecord, snapshot.generation),\n },\n }\n}\n\nfunction degradeForRefresh(\n freshness: GristStreamFreshness,\n prevGeneration: GristGeneration\n): GristStreamFreshness {\n if (freshness.state === \"hot\" && freshness.generation === prevGeneration) {\n return { state: \"stale\", lastEventMs: freshness.lastEventMs, reason: \"host_quiet\" }\n }\n return freshness\n}\n\n// ---------------------------------------------------------------------------\n// Mappings (config)\n// ---------------------------------------------------------------------------\n\nfunction reduceMapping(\n snapshot: GristWidgetSnapshot,\n action: GristMappingAction,\n ctx: GristReducerContext\n): GristWidgetSnapshot {\n switch (action.type) {\n case \"MAPPING_DECLARE\": {\n if (action.declared.length === 0) {\n // Empty declaration → revert to `not_declared`.\n if (snapshot.config.mappings.state === \"not_declared\") return snapshot\n ctx.resolver = null\n return {\n ...snapshot,\n config: { mappings: { state: \"not_declared\" } },\n }\n }\n if (!ctx.resolver) {\n ctx.resolver = new MappingResolver(action.declared, snapshot.generation)\n } else {\n ctx.resolver.redeclare(action.declared, snapshot.generation)\n }\n return {\n ...snapshot,\n config: {\n mappings: {\n state: \"pending\",\n declared: action.declared,\n declaredAtMs: action.atMs,\n },\n },\n }\n }\n case \"MAPPING_REPORT\": {\n if (!ctx.resolver) return snapshot\n const outcome = ctx.resolver.ingest({\n source: action.source,\n payload: action.payload,\n receivedAtMs: action.atMs,\n generation: action.generation,\n })\n if (outcome.kind !== \"resolved\") return snapshot\n return applyResolved(snapshot, outcome.state)\n }\n case \"MAPPING_INVALIDATE\": {\n const previous = snapshot.config.mappings\n if (previous.state !== \"complete\" && previous.state !== \"incomplete\") {\n return snapshot\n }\n return {\n ...snapshot,\n config: {\n mappings: {\n state: \"invalidating\",\n previous: previous as GristMappingComplete | GristMappingIncomplete,\n sinceMs: action.atMs,\n },\n },\n }\n }\n case \"MAPPING_INVALIDATION_RESOLVE\": {\n if (snapshot.config.mappings.state !== \"invalidating\") return snapshot\n if (!ctx.resolver) {\n // Resolver lost (e.g. declaration cleared) — fall back to `previous`.\n return {\n ...snapshot,\n config: { mappings: snapshot.config.mappings.previous },\n }\n }\n const outcome = ctx.resolver.current()\n if (outcome.kind !== \"resolved\") {\n return {\n ...snapshot,\n config: { mappings: snapshot.config.mappings.previous },\n }\n }\n return applyResolved(snapshot, outcome.state)\n }\n case \"MAPPING_TIMEOUT\": {\n const previous = snapshot.config.mappings\n if (previous.state !== \"pending\") return snapshot\n return {\n ...snapshot,\n config: {\n mappings: {\n state: \"unreported\",\n declared: previous.declared,\n afterMs: action.atMs - previous.declaredAtMs,\n },\n },\n }\n }\n }\n}\n\nfunction applyResolved(\n snapshot: GristWidgetSnapshot,\n resolved: GristMappingComplete | GristMappingIncomplete\n): GristWidgetSnapshot {\n const existing = snapshot.config.mappings\n if (\n (existing.state === \"complete\" || existing.state === \"incomplete\") &&\n sameResolved(existing, resolved)\n ) {\n return snapshot\n }\n return { ...snapshot, config: { mappings: resolved } }\n}\n\nfunction sameResolved(\n a: GristMappingState,\n b: GristMappingComplete | GristMappingIncomplete\n): boolean {\n if (a.state !== b.state) return false\n if (a.state !== \"complete\" && a.state !== \"incomplete\") return false\n if (a.source !== b.source) return false\n const ak = Object.keys(a.mappings)\n const bk = Object.keys(b.mappings)\n if (ak.length !== bk.length) return false\n for (const k of ak) {\n const av = a.mappings[k]\n const bv = b.mappings[k]\n if (Array.isArray(av) && Array.isArray(bv)) {\n if (av.length !== bv.length) return false\n for (let i = 0; i < av.length; i++) if (av[i] !== bv[i]) return false\n } else if (av !== bv) {\n return false\n }\n }\n return true\n}\n\n// ---------------------------------------------------------------------------\n// Streams (sync)\n// ---------------------------------------------------------------------------\n\nfunction reduceStream(snapshot: GristWidgetSnapshot, action: GristSyncAction): GristWidgetSnapshot {\n if (action.type === \"CURRENT_TABLE_KNOWN\" || action.type === \"CURRENT_TABLE_REFRESH_START\" || action.type === \"CURRENT_TABLE_ERROR\") {\n return snapshot\n }\n const stream = action.stream\n const current = snapshot.sync[stream]\n\n if (action.type === \"STREAM_WARMING\") {\n if (current.state === \"warming\" || current.state === \"hot\") return snapshot\n return updateStream(snapshot, stream, { state: \"warming\", sinceMs: action.atMs })\n }\n\n if (action.type === \"STREAM_STALE\") {\n if (current.state !== \"hot\") return snapshot\n return updateStream(snapshot, stream, {\n state: \"stale\",\n lastEventMs: current.lastEventMs,\n reason: action.reason,\n })\n }\n\n // STREAM_RECEIVED — idempotent on (generation, hash).\n if (\n current.state === \"hot\" &&\n current.generation === action.generation &&\n current.payloadHash === action.payloadHash\n ) {\n return snapshot\n }\n return updateStream(snapshot, stream, {\n state: \"hot\",\n lastEventMs: action.atMs,\n generation: action.generation,\n payloadHash: action.payloadHash,\n })\n}\n\nfunction updateStream(\n snapshot: GristWidgetSnapshot,\n stream: GristSyncStream,\n next: GristStreamFreshness\n): GristWidgetSnapshot {\n return {\n ...snapshot,\n sync: { ...snapshot.sync, [stream]: next },\n }\n}\n\nfunction updateSync(snapshot: GristWidgetSnapshot, sync: GristSync): GristWidgetSnapshot {\n return { ...snapshot, sync }\n}\n\n// ---------------------------------------------------------------------------\n// Re-exports (convenience)\n// ---------------------------------------------------------------------------\n\nexport { ZERO_GENERATION, nextGeneration }\n","/**\n * Pure projections from a {@link GristWidgetSnapshot}:\n *\n * - {@link deriveStatus} → legacy 4-state `status` (compat).\n * - {@link deriveCapabilities} → ready-to-bind booleans for widget UI.\n *\n * Both are deterministic and side-effect free. Callers (hooks, Boundary)\n * memoize them by reference equality of `snapshot`.\n */\n\nimport { ACCESS_RANK } from \"./access\"\nimport {\n pendingColumnMappingStatus,\n type ValidateColumnMappingsResult,\n} from \"../../helpers/grist-mappings\"\nimport type { GristColumnsToMap, GristWidgetColumnMap } from \"../../types\"\nimport type {\n GristCapabilities,\n GristMappingState,\n GristWidgetSnapshot,\n GristWidgetStatus,\n} from \"./types\"\n\n// ---------------------------------------------------------------------------\n// Status projection\n// ---------------------------------------------------------------------------\n\nexport function deriveStatus(snapshot: GristWidgetSnapshot): GristWidgetStatus {\n const { lifecycle, authz, link } = snapshot\n\n if (lifecycle.phase === \"idle\" || lifecycle.phase === \"detecting\") return \"booting\"\n\n if (lifecycle.phase === \"negotiating\") return \"booting\"\n\n if (lifecycle.phase === \"terminated\") {\n switch (lifecycle.reason.kind) {\n case \"script_timeout\":\n case \"not_embedded\":\n return \"unavailable\"\n case \"host_rejected\":\n case \"ready_failed\":\n case \"ready_timeout\":\n case \"manual_terminate\":\n return \"error\"\n }\n }\n\n // lifecycle.phase === \"online\"\n if (authz.state === \"denied\") return \"error\"\n if (link.state === \"lost\") return \"error\"\n return \"ready\"\n}\n\n/** Extract a single human-readable error message, if any, from the snapshot. */\nexport function deriveErrorMessage(snapshot: GristWidgetSnapshot): string | null {\n if (snapshot.lifecycle.phase === \"terminated\") {\n switch (snapshot.lifecycle.reason.kind) {\n case \"ready_failed\":\n return snapshot.lifecycle.reason.message\n case \"ready_timeout\":\n return `Grist handshake timed out after ${snapshot.lifecycle.reason.afterMs}ms`\n case \"host_rejected\":\n return snapshot.lifecycle.reason.message\n case \"script_timeout\":\n return `Grist plugin API never loaded (${snapshot.lifecycle.reason.attempts} attempts)`\n case \"not_embedded\":\n return \"This page is not embedded in a Grist iframe\"\n case \"manual_terminate\":\n return null\n }\n }\n if (snapshot.authz.state === \"denied\") {\n return (\n snapshot.authz.message ??\n `Grist denied requested access (${snapshot.authz.declared})`\n )\n }\n if (snapshot.lifecycle.phase === \"online\" && snapshot.link.state === \"lost\") {\n return `Grist link lost (${snapshot.link.missedPings} consecutive failures)`\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// Capabilities derivation\n// ---------------------------------------------------------------------------\n\nexport function deriveCapabilities(snapshot: GristWidgetSnapshot): GristCapabilities {\n const { lifecycle, authz, link, config, sync } = snapshot\n const online = lifecycle.phase === \"online\"\n const granted = authz.state === \"granted\"\n const grantedReadOrMore =\n granted && ACCESS_RANK[authz.level] >= ACCESS_RANK[\"read table\"]\n const grantedFull = granted && authz.level === \"full\"\n const linkUsable = link.state !== \"lost\"\n\n const canRead = online && linkUsable && grantedReadOrMore\n const canFetchTable = canRead && link.state === \"connected\"\n\n const mappingsUsable = hasUsableMappings(config.mappings)\n const missing = mappingsMissing(config.mappings)\n const emptyMultiples = mappingsEmptyMultiples(config.mappings)\n\n const canRender = canRead && mappingsUsable\n const canWriteRecords = canRender && grantedFull\n const canWriteSchema =\n canWriteRecords && sync.currentTable.state === \"known\"\n\n const hasFreshSelection =\n canRead &&\n sync.record.state === \"hot\" &&\n config.mappings.state !== \"invalidating\"\n\n return {\n canRead,\n canRender,\n canWriteRecords,\n canWriteSchema,\n canFetchTable,\n hasFreshSelection,\n hasUsableMappings: mappingsUsable,\n missingMappings: missing,\n emptyMultipleMappings: emptyMultiples,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Mapping helpers\n// ---------------------------------------------------------------------------\n\nfunction hasUsableMappings(state: GristMappingState): boolean {\n if (state.state === \"not_declared\") return true\n if (state.state === \"complete\") return true\n if (state.state === \"invalidating\") {\n return state.previous.state === \"complete\"\n }\n return false\n}\n\nfunction mappingsMissing(state: GristMappingState): readonly string[] {\n if (state.state === \"incomplete\") return state.missing\n if (state.state === \"invalidating\" && state.previous.state === \"incomplete\") {\n return state.previous.missing\n }\n return EMPTY\n}\n\nfunction mappingsEmptyMultiples(state: GristMappingState): readonly string[] {\n if (state.state === \"incomplete\") return state.emptyMultiples\n if (state.state === \"invalidating\" && state.previous.state === \"incomplete\") {\n return state.previous.emptyMultiples\n }\n return EMPTY\n}\n\nconst EMPTY: readonly string[] = Object.freeze([])\n\n// ---------------------------------------------------------------------------\n// Column-mapping projection (public `columnMappingStatus` shape)\n// ---------------------------------------------------------------------------\n\n/** Resolved mappings from the FSM config axis, if any. */\nexport function extractResolvedMappings(\n state: GristMappingState\n): GristWidgetColumnMap | null {\n switch (state.state) {\n case \"complete\":\n case \"incomplete\":\n return state.mappings\n case \"invalidating\":\n if (state.previous.state === \"complete\" || state.previous.state === \"incomplete\") {\n return state.previous.mappings\n }\n return null\n default:\n return null\n }\n}\n\n/**\n * Project {@link GristMappingState} into the legacy\n * {@link ValidateColumnMappingsResult} consumed by `useGrist().columnMappingStatus`.\n */\nexport function deriveColumnMappingStatus(\n mappingState: GristMappingState,\n columns?: GristColumnsToMap\n): ValidateColumnMappingsResult {\n if (!columns?.length) {\n return { ok: true, pending: false, missing: [], emptyMultiples: [] }\n }\n switch (mappingState.state) {\n case \"not_declared\":\n return { ok: true, pending: false, missing: [], emptyMultiples: [] }\n case \"pending\":\n return pendingColumnMappingStatus\n case \"unreported\":\n return pendingColumnMappingStatus\n case \"complete\":\n return { ok: true, pending: false, missing: [], emptyMultiples: [] }\n case \"incomplete\":\n return {\n ok: false,\n pending: false,\n missing: [...mappingState.missing],\n emptyMultiples: [...mappingState.emptyMultiples],\n }\n case \"invalidating\":\n return deriveColumnMappingStatus(mappingState.previous, columns)\n }\n}\n","/**\n * Internal context that hosts the shared {@link GristHandshakeManager} for a\n * widget subtree.\n *\n * - `<GristWidgetProvider>` publishes the manager via\n * {@link GristHandshakeManagerContext}.\n * - Standalone `useGrist()` / mid-level hooks without a provider acquire the\n * page-level embedded manager ({@link acquireEmbeddedHandshakeManager}).\n *\n * All SDK hooks call {@link useGristManager} — there is no pre-FSM fallback.\n */\n\nimport { createContext, useContext, useEffect, useRef } from \"react\"\n\nimport { buildManagerOptionsFromGristOptions, type BuildManagerOptionsInput } from \"./build-manager-options\"\nimport {\n acquireEmbeddedHandshakeManager,\n releaseEmbeddedHandshakeManager,\n} from \"./embedded-handshake-manager\"\nimport type { ManagerControls } from \"./handshake/manager\"\n\nexport const GristHandshakeManagerContext = createContext<ManagerControls | null>(null)\n\n/** Read the manager from context. Returns `null` outside a provider. */\nexport function useGristManagerOptional(): ManagerControls | null {\n return useContext(GristHandshakeManagerContext)\n}\n\n/**\n * Returns the active handshake manager for this React tree.\n *\n * Inside `<GristWidgetProvider>` this is the provider's manager. Otherwise a\n * single ref-counted embedded manager is shared across all standalone hook\n * instances on the page.\n */\nexport function useGristManager(options?: BuildManagerOptionsInput): ManagerControls {\n const fromProvider = useContext(GristHandshakeManagerContext)\n const optionsRef = useRef(options)\n optionsRef.current = options\n\n const embeddedRef = useRef<ManagerControls | null>(null)\n const ownsEmbeddedRef = useRef(false)\n\n if (fromProvider) {\n return fromProvider\n }\n\n if (embeddedRef.current === null) {\n embeddedRef.current = acquireEmbeddedHandshakeManager(\n buildManagerOptionsFromGristOptions(optionsRef.current)\n )\n ownsEmbeddedRef.current = true\n }\n\n useEffect(() => {\n if (!ownsEmbeddedRef.current) return\n return () => {\n releaseEmbeddedHandshakeManager()\n embeddedRef.current = null\n ownsEmbeddedRef.current = false\n }\n }, [])\n\n return embeddedRef.current\n}\n","/**\n * Single-call wrapper around `grist.ready()`.\n *\n * Grist's contract is \"call ready once\". Multiple `useGristReady` calls (or\n * `useGristWidget` from sibling components) would otherwise race. We:\n *\n * - Coalesce concurrent calls into one in-flight promise.\n * - Re-call `grist.ready` only when `requiredAccess` widens (so an action-capable\n * hook can upgrade a previously read-only handshake).\n */\n\nimport type { GristReadyOptions, GristRequiredAccess } from \"../types\"\n\nconst ACCESS_RANK: Record<GristRequiredAccess, number> = {\n none: 0,\n \"read table\": 1,\n full: 2,\n}\n\nlet inFlight: Promise<void> | null = null\nlet lastAccess: GristRequiredAccess | null = null\n\nfunction shouldReinit(next: GristRequiredAccess): boolean {\n if (!lastAccess) return true\n return ACCESS_RANK[next] > ACCESS_RANK[lastAccess]\n}\n\n/**\n * Calls `grist.ready` at most once per page (re-called only if access widens).\n * Returns the same promise for all concurrent callers.\n */\nexport function ensureGristReady(payload: GristReadyOptions): Promise<void> {\n const access = payload.requiredAccess ?? \"read table\"\n if (inFlight && !shouldReinit(access)) return inFlight\n\n lastAccess = access\n const next = Promise.resolve().then(() => {\n const result = grist.ready(payload)\n return result instanceof Promise ? result : Promise.resolve()\n })\n\n inFlight = next.catch((err) => {\n inFlight = null\n lastAccess = null\n throw err\n })\n return inFlight\n}\n\n/**\n * Forget the cached ready state. Subsequent calls to {@link ensureGristReady}\n * will re-issue `grist.ready` even at the same access level.\n *\n * Production uses: the handshake manager's `reload()` clears the singleton so\n * a soft retry triggers a fresh `grist.ready` (otherwise the cached promise\n * would short-circuit and the user-visible reload would be a no-op).\n *\n * Tests use this between test cases when swapping `window.grist`.\n */\nexport function resetGristReadySingleton(): void {\n inFlight = null\n lastAccess = null\n}\n\n/** @deprecated Use {@link resetGristReadySingleton}. Kept for test back-compat. */\nexport const resetGristReadyCacheForTests = resetGristReadySingleton\n","import type { ManagerOptions } from \"./handshake/manager\"\nimport { ensureGristReady, resetGristReadySingleton } from \"./ready-singleton\"\nimport type { GristReadyOptions } from \"../types\"\n\nexport type BuildManagerOptionsInput = GristReadyOptions & {\n availabilityMaxAttempts?: number\n availabilityIntervalMs?: number\n logWhenNotEmbedded?: boolean\n}\n\n/**\n * Map public widget options to {@link ManagerOptions} for\n * {@link createGristHandshakeManager}.\n */\nexport function buildManagerOptionsFromGristOptions(\n options?: BuildManagerOptionsInput\n): ManagerOptions {\n const opts: ManagerOptions = {\n requiredAccess: options?.requiredAccess ?? \"read table\",\n columns: options?.columns ?? [],\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n negotiate: {\n readyImpl: (payload) => ensureGristReady(payload),\n },\n onBeforeReload: () => resetGristReadySingleton(),\n }\n if (\n options?.availabilityMaxAttempts !== undefined ||\n options?.availabilityIntervalMs !== undefined\n ) {\n const interval = options?.availabilityIntervalMs ?? 1000\n const max = options?.availabilityMaxAttempts ?? 10\n opts.detect = {\n pollSequenceMs: Array.from({ length: max }, () => interval),\n budgetMs: interval * max,\n }\n }\n return opts\n}\n","/**\n * Detect effect — poll for `window.grist` with exponential backoff inside a\n * finite, optionally adaptive budget.\n *\n * Outputs (dispatched to the manager):\n * - DETECT_START (once, at start)\n * - DETECT_TICK (each unsuccessful poll, with attempts count)\n * - DETECT_AVAILABLE (grist global present AND embedded)\n * - NOT_EMBEDDED (grist global present but `self === top`)\n * - DETECT_TIMEOUT (budget consumed without detection)\n *\n * The effect aborts on `signal.aborted` and clears its pending timer.\n *\n * Pure of `window`: relies on the {@link GristEnvironment} adapter so the\n * effect is fully unit-testable with a virtual clock.\n */\n\nimport type { GristEnvironment, TimerHandle } from \"../environment\"\nimport type { GristAction } from \"../actions\"\n\n/**\n * Default poll sequence (ms). Caps at 5 000 ms — most production widgets see\n * `grist-plugin-api.js` load in < 1 s, but slow 3G can stretch to 20–30 s.\n */\nexport const DEFAULT_POLL_SEQUENCE_MS: readonly number[] = [\n 200, 200, 400, 400, 800, 1600, 3200, 5000, 5000, 5000, 5000, 5000, 5000, 5000,\n] as const\n\n/** Effective budget, adaptive on `navigator.connection.effectiveType`. */\nexport function defaultBudgetMs(hint: ReturnType<GristEnvironment[\"effectiveTypeHint\"]>): number {\n if (hint === \"slow-2g\" || hint === \"2g\") return 120_000\n if (hint === \"3g\") return 60_000\n return 30_000\n}\n\nexport type DetectOptions = {\n /** Custom poll sequence in ms. Index past end uses last value. */\n pollSequenceMs?: readonly number[]\n /** Total wall-clock budget across all polls. */\n budgetMs?: number\n}\n\nexport type DetectHandle = {\n cancel(): void\n}\n\n/**\n * Start the detect effect. The manager owns the cancellation handle and is\n * responsible for calling `cancel()` on `restart()` / `stop()`.\n */\nexport function startDetectEffect(\n env: GristEnvironment,\n dispatch: (action: GristAction) => void,\n signal: AbortSignal,\n options: DetectOptions = {}\n): DetectHandle {\n const sequence = options.pollSequenceMs ?? DEFAULT_POLL_SEQUENCE_MS\n const budgetMs = options.budgetMs ?? defaultBudgetMs(env.effectiveTypeHint())\n\n const startedAt = env.now()\n let attempts = 0\n let timer: TimerHandle | null = null\n let cancelled = false\n\n signal.addEventListener(\"abort\", () => {\n cancelled = true\n if (timer !== null) env.clearTimeout(timer)\n })\n\n dispatch({ type: \"DETECT_START\", atMs: startedAt })\n\n const tick = () => {\n if (cancelled) return\n timer = null\n\n const probe = env.probeGrist()\n if (probe !== null) {\n if (probe.embedded) {\n dispatch({ type: \"DETECT_AVAILABLE\", atMs: env.now() })\n } else {\n dispatch({ type: \"NOT_EMBEDDED\", atMs: env.now() })\n }\n return\n }\n\n attempts += 1\n dispatch({ type: \"DETECT_TICK\", attempts, atMs: env.now() })\n\n const elapsed = env.now() - startedAt\n if (elapsed >= budgetMs) {\n dispatch({ type: \"DETECT_TIMEOUT\", attempts, atMs: env.now() })\n return\n }\n\n const next = sequence[Math.min(attempts - 1, sequence.length - 1)] ?? 1000\n const remaining = budgetMs - elapsed\n timer = env.setTimeout(tick, Math.max(0, Math.min(next, remaining)))\n }\n\n // Probe immediately at t=0 so widgets where grist is already global don't\n // wait for the first interval.\n tick()\n\n return {\n cancel() {\n cancelled = true\n if (timer !== null) {\n env.clearTimeout(timer)\n timer = null\n }\n },\n }\n}\n","/**\n * Negotiate effect — issue `grist.ready(payload)` race-d against a timeout\n * and an external `AbortSignal`.\n *\n * Dispatches:\n * - NEGOTIATE_START\n * - NEGOTIATE_SUCCESS (grist.ready resolved AND `docApi` is present)\n * - NEGOTIATE_FAIL (reason: ready_failed | ready_timeout | manual_terminate)\n *\n * The effect never throws to the caller. Aborts before resolution silently\n * stop dispatching.\n */\n\nimport type { GristEnvironment, GristLike } from \"../environment\"\nimport type { GristAction } from \"../actions\"\nimport type { GristReadyOptions, GristRequiredAccess } from \"../../../types\"\nimport type { GristTerminationReason } from \"../types\"\n\nexport type NegotiateOptions = {\n /** Max ms to wait for `grist.ready` to resolve. */\n timeoutMs?: number\n /**\n * Custom implementation of `grist.ready(payload)`. Defaults to calling\n * `grist.ready` directly via the env. Production callers (the provider)\n * inject a wrapper that routes through `ensureGristReady()` so the\n * page-level singleton dedupes concurrent ready calls.\n */\n readyImpl?: (payload: GristReadyOptions) => Promise<void> | void\n}\n\nexport const DEFAULT_NEGOTIATE_TIMEOUT_MS = 30_000\n\nexport type NegotiateHandle = {\n cancel(): void\n}\n\nexport function startNegotiateEffect(\n env: GristEnvironment,\n dispatch: (action: GristAction) => void,\n signal: AbortSignal,\n payload: GristReadyOptions,\n options: NegotiateOptions = {}\n): NegotiateHandle {\n const timeoutMs = options.timeoutMs ?? DEFAULT_NEGOTIATE_TIMEOUT_MS\n const access: GristRequiredAccess = payload.requiredAccess ?? \"read table\"\n let settled = false\n let timer: ReturnType<GristEnvironment[\"setTimeout\"]> | null = null\n\n const settle = (next: () => void) => {\n if (settled) return\n settled = true\n if (timer !== null) env.clearTimeout(timer)\n if (signal.aborted) return\n next()\n }\n\n dispatch({ type: \"NEGOTIATE_START\", access, atMs: env.now() })\n\n const grist = env.getGrist()\n if (!grist) {\n settle(() =>\n dispatch({\n type: \"NEGOTIATE_FAIL\",\n reason: { kind: \"ready_failed\", message: \"grist global missing at negotiate\" },\n atMs: env.now(),\n })\n )\n return { cancel: () => settle(() => {}) }\n }\n\n // Schedule timeout first; ready() may resolve synchronously and trigger\n // `settle` before this fires.\n timer = env.setTimeout(() => {\n if (settled) return\n settle(() =>\n dispatch({\n type: \"NEGOTIATE_FAIL\",\n reason: { kind: \"ready_timeout\", afterMs: timeoutMs },\n atMs: env.now(),\n })\n )\n }, timeoutMs)\n\n const handleSuccess = (g: GristLike) => {\n if (!g.docApi) {\n dispatch({\n type: \"NEGOTIATE_FAIL\",\n reason: { kind: \"ready_failed\", message: \"grist.docApi missing after ready\" },\n atMs: env.now(),\n })\n return\n }\n dispatch({\n type: \"NEGOTIATE_SUCCESS\",\n access,\n grantedLevel: access, // host doesn't report effective level; widen later via authz events\n atMs: env.now(),\n })\n }\n\n const handleFailure = (err: unknown) => {\n const message = err instanceof Error ? err.message : String(err)\n const reason: GristTerminationReason = { kind: \"ready_failed\", message }\n dispatch({ type: \"NEGOTIATE_FAIL\", reason, atMs: env.now() })\n }\n\n const readyImpl = options.readyImpl ?? ((p) => grist.ready(p))\n try {\n const result = readyImpl(payload)\n if (result && typeof (result as Promise<void>).then === \"function\") {\n ;(result as Promise<void>).then(\n () => settle(() => handleSuccess(env.getGrist() ?? grist)),\n (err) => settle(() => handleFailure(err))\n )\n } else {\n settle(() => handleSuccess(env.getGrist() ?? grist))\n }\n } catch (err) {\n settle(() => handleFailure(err))\n }\n\n signal.addEventListener(\"abort\", () => settle(() => {}))\n\n return { cancel: () => settle(() => {}) }\n}\n","import type { GristWidgetInteraction, GristWidgetLinkingInfo } from \"../types\"\n\n/**\n * Parse `InteractionOptions.linking` from a Grist `onOptions` settings payload.\n */\nexport function parseGristWidgetLinkingInfo(raw: unknown): GristWidgetLinkingInfo | undefined {\n if (!raw || typeof raw !== \"object\" || !(\"asTarget\" in raw)) return undefined\n const linking = raw as { asTarget: unknown; asSource?: unknown }\n return {\n asTarget: linking.asTarget == null ? null : String(linking.asTarget),\n asSource: Boolean(linking.asSource),\n }\n}\n\n/**\n * Normalize the second argument to `grist.onOptions` (Grist `InteractionOptions`).\n * Hosts use `accessLevel` (camelCase); the emulator uses `access_level`.\n */\nexport function normalizeWidgetInteraction(\n interaction: GristWidgetInteraction | null\n): GristWidgetInteraction | null {\n if (!interaction || typeof interaction !== \"object\") return interaction\n const raw = interaction as Record<string, unknown>\n const access_level =\n typeof raw.access_level === \"string\"\n ? raw.access_level\n : typeof raw.accessLevel === \"string\"\n ? raw.accessLevel\n : undefined\n const linking = parseGristWidgetLinkingInfo(raw.linking)\n return {\n ...interaction,\n ...(access_level ? { access_level } : {}),\n ...(linking ? { linking } : {}),\n }\n}\n","/**\n * Module-level subscription manager for Grist's `on*` events.\n *\n * Why this exists: `grist.onRecord`, `onRecords`, `onOptions`, and `onNewRecord`\n * are add-only — there is no `off`. Subscribing in every hook instance leaks\n * handlers for the page lifetime. This module subscribes to each Grist event\n * **at most once** and fans out to React listeners with proper teardown.\n *\n * Last-known values are cached so late subscribers immediately receive the\n * current state (analogous to a BehaviorSubject).\n *\n * Fixed-defaults contract\n * -----------------------\n * The underlying `grist.onRecord` / `onRecords` calls accept a\n * `FetchSelectedOptions` parameter (e.g. `keepEncoded`, `format`), but\n * because we bind **once** at the first subscribe, those options would be\n * \"first-hook-wins\" across the page — a subtle footgun for agents.\n *\n * This module therefore binds with Grist's **public defaults** and does not\n * accept per-subscriber options:\n * - `onRecord` → decoded rows (`keepEncoded: false`)\n * - `onRecords` → decoded rows (`format: \"rows\"`, `keepEncoded: false`)\n *\n * If you need encoded values, use the one-shot readers\n * (`useGrist().fetchSelectedTable({ keepEncoded: true })`) instead of\n * a subscription — that call-site controls its own options.\n */\n\nimport { normalizeWidgetInteraction } from \"../helpers/grist-widget-interaction\"\nimport type { GristWidgetColumnMap, GristWidgetInteraction } from \"../types\"\n\ntype Listener<T> = (value: T) => void\n\nclass GristEvent<T> {\n private listeners = new Set<Listener<T>>()\n private hasValue = false\n private last: T | undefined\n private bind: (() => void) | null = null\n\n constructor(private readonly bindOnce: (emit: (value: T) => void) => void) {}\n\n subscribe(listener: Listener<T>): () => void {\n this.listeners.add(listener)\n if (!this.bind) {\n this.bind = () => {\n // Mark as bound regardless of throw, so we don't retry every render.\n this.bind = () => {}\n try {\n this.bindOnce((value) => {\n this.hasValue = true\n this.last = value\n for (const l of this.listeners) l(value)\n })\n } catch (err) {\n if (typeof console !== \"undefined\") {\n console.error(\"[lib/grist] failed to subscribe to Grist event:\", err)\n }\n }\n }\n this.bind()\n }\n if (this.hasValue && this.last !== undefined) {\n listener(this.last)\n }\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n /** Last cached value (or `undefined` if no event has fired yet). */\n peek(): T | undefined {\n return this.last\n }\n\n /** True once at least one event has fired. */\n get ready(): boolean {\n return this.hasValue\n }\n\n /**\n * Test helper: fan out a payload without calling `grist.on*`.\n * Used to simulate hosts that reuse one record object and mutate it in place.\n */\n deliverForTests(value: T): void {\n this.hasValue = true\n this.last = value\n for (const l of this.listeners) l(value)\n }\n\n /**\n * Test helper: forget the bound state and cached value. Lets a test swap\n * `window.grist` (e.g. via the emulator's `dispose()` + `createGristEmulator()`)\n * and re-bind cleanly on the next subscribe.\n */\n _resetForTests(): void {\n this.listeners.clear()\n this.hasValue = false\n this.last = undefined\n this.bind = null\n }\n}\n\nexport type GristRecordPayload = {\n record: Record<string, unknown> | null\n mappings: GristWidgetColumnMap | null\n}\n\nexport type GristRecordsPayload = {\n records: Record<string, unknown>[]\n mappings: GristWidgetColumnMap | null\n}\n\nexport type GristOptionsPayload = {\n options: Record<string, unknown> | null\n interaction: GristWidgetInteraction | null\n}\n\nexport type GristNewRecordPayload = {\n mappings: GristWidgetColumnMap | null\n}\n\n/**\n * The singletons. These are lazy: nothing is registered with `grist` until\n * the first `subscribe()` call.\n */\ntype GristHostMessage = {\n tableId?: string\n rowId?: number | \"new\"\n dataChange?: boolean\n mappingsChange?: boolean\n}\n\nexport const recordEvent = new GristEvent<GristRecordPayload>((emit) => {\n const deliver = (record: Record<string, unknown> | null, mappings: GristWidgetColumnMap | null) => {\n emit({ record, mappings })\n }\n\n // Pin `keepEncoded: false` so mapped records contain JS-native values for\n // widget code instead of raw cell tuples.\n grist.onRecord(\n (record, mappings) => {\n deliver(record, (mappings as GristWidgetColumnMap | null) ?? null)\n },\n { keepEncoded: false }\n )\n\n // Mirror grist-plugin-api's `onRecord` message handler: Grist's RecordNotifier\n // posts `{ rowId, dataChange: false }` on cursor moves. `onRecord` should\n // handle that, but refetch here when `rowId` changes so widgets never stick on\n // the first row if the callback path is skipped or reordered.\n const fetchSelectedRecord = grist.fetchSelectedRecord ?? grist.docApi?.fetchSelectedRecord\n if (typeof grist.on !== \"function\" || typeof fetchSelectedRecord !== \"function\") {\n return\n }\n let lastRowId: number | undefined\n grist.on(\"message\", async (msg: unknown) => {\n if (!msg || typeof msg !== \"object\") return\n const host = msg as GristHostMessage\n if (!host.tableId || typeof host.rowId !== \"number\") return\n if (host.rowId === lastRowId) return\n lastRowId = host.rowId\n try {\n const record = await fetchSelectedRecord(host.rowId, { keepEncoded: false })\n deliver(record, recordEvent.peek()?.mappings ?? null)\n } catch (err) {\n if (typeof console !== \"undefined\") {\n console.error(\"[grist-widget-sdk] fetchSelectedRecord on cursor message failed:\", err)\n }\n }\n })\n})\n\nexport const recordsEvent = new GristEvent<GristRecordsPayload>((emit) => {\n // Pin `format: \"rows\"` + `keepEncoded: false` so callers always receive\n // row-shaped decoded objects. (GristView would otherwise default to\n // `format: \"columns\"` + `keepEncoded: true`.)\n grist.onRecords(\n (records, mappings) => {\n emit({ records, mappings: (mappings as GristWidgetColumnMap | null) ?? null })\n },\n { keepEncoded: false, format: \"rows\" }\n )\n})\n\nexport const optionsEvent = new GristEvent<GristOptionsPayload>((emit) => {\n grist.onOptions((options, interaction) => {\n const normalized = normalizeWidgetInteraction(\n (interaction as GristWidgetInteraction | null) ?? null\n )\n emit({\n options: options ? { ...options } : null,\n interaction: normalized,\n })\n })\n})\n\nexport const newRecordEvent = new GristEvent<GristNewRecordPayload>((emit) => {\n if (typeof grist.onNewRecord !== \"function\") return\n grist.onNewRecord((mappings) => {\n emit({ mappings: (mappings as GristWidgetColumnMap | null) ?? null })\n })\n})\n\n/**\n * Test-only: reset every event singleton. Call between tests when swapping\n * `window.grist`. Production code never imports this.\n */\nexport function resetGristEventsForTests(): void {\n recordEvent._resetForTests()\n recordsEvent._resetForTests()\n optionsEvent._resetForTests()\n newRecordEvent._resetForTests()\n}\n\n/** Vitest-only: push a record payload through {@link recordEvent} subscribers. */\nexport function deliverRecordForTests(payload: GristRecordPayload): void {\n recordEvent.deliverForTests(payload)\n}\n","/**\n * Subscriptions effect — bind Grist event streams once and feed the handshake\n * machine with generation-stamped, hash-deduplicated dispatches.\n *\n * Why a dedicated effect\n * ----------------------\n * The SDK already has a {@link \"../../subscriptions\"} module that owns the\n * one-time binding to `grist.on*` and the BehaviorSubject-style cache. We do\n * NOT re-bind here — instead, we install a single listener per stream that\n * forwards into the handshake reducer.\n *\n * Generation discipline: every dispatched action carries the CURRENT manager\n * generation read at the moment the event arrives. The reducer enforces\n * `action.generation >= snapshot.generation`, so events arriving after a\n * `reload()` / `restart()` that bumped generation are dropped automatically.\n *\n * Hash: payloads are reduced to a compact, deterministic string so duplicates\n * (e.g. emulator triple-replay of initial state) become no-ops at the reducer.\n */\n\nimport type { GristAction, GristSyncStream } from \"../actions\"\nimport { parseGristRequiredAccess } from \"../access\"\nimport type { GristGeneration } from \"../types\"\nimport {\n newRecordEvent,\n optionsEvent,\n recordEvent,\n recordsEvent,\n} from \"../../subscriptions\"\n\nexport type SubscriptionsHandle = {\n cancel(): void\n}\n\n/**\n * Pluggable binder type. The default {@link defaultSubscriptionsBinder}\n * subscribes to the SDK's singleton {@link GristEvent} instances. Tests can\n * supply a stub that never touches the real `grist` global.\n */\nexport type SubscriptionsBinder = (ctx: SubscriptionsContext) => SubscriptionsHandle\n\nexport type SubscriptionsContext = {\n dispatch: (action: GristAction) => void\n /**\n * Snapshot generation provider. Must be read lazily (inside the callback)\n * because `generation` is monotone but mutates over the lifetime of the\n * subscription.\n */\n getGeneration: () => GristGeneration\n /** Hook for the mapping effect: receive payloads in arrival order. */\n onRecordPayload?: (mappings: Record<string, unknown> | null | undefined) => void\n onRecordsPayload?: (mappings: Record<string, unknown> | null | undefined) => void\n onOptionsPayload?: (options: Record<string, unknown> | null) => void\n onNewRecordPayload?: (mappings: Record<string, unknown> | null | undefined) => void\n}\n\nexport const bindSubscriptions: SubscriptionsBinder = defaultSubscriptionsBinder\n\nexport function defaultSubscriptionsBinder(ctx: SubscriptionsContext): SubscriptionsHandle {\n const offs: Array<() => void> = []\n\n offs.push(\n recordEvent.subscribe((payload) => {\n const hash = quickHash(\"record\", payload)\n dispatchStream(ctx, \"record\", hash)\n ctx.onRecordPayload?.(payload.mappings ?? null)\n })\n )\n\n offs.push(\n recordsEvent.subscribe((payload) => {\n const hash = quickHash(\"records\", payload)\n dispatchStream(ctx, \"records\", hash)\n ctx.onRecordsPayload?.(payload.mappings ?? null)\n })\n )\n\n offs.push(\n optionsEvent.subscribe((payload) => {\n const hash = quickHash(\"options\", payload)\n dispatchStream(ctx, \"options\", hash)\n const level = parseGristRequiredAccess(payload.interaction?.access_level)\n if (level !== null) {\n ctx.dispatch({\n type: \"AUTHZ_REPORT\",\n level,\n atMs: Date.now(),\n generation: ctx.getGeneration(),\n })\n }\n ctx.onOptionsPayload?.(payload.options ?? null)\n })\n )\n\n offs.push(\n newRecordEvent.subscribe((payload) => {\n const hash = quickHash(\"newRecord\", payload)\n dispatchStream(ctx, \"newRecord\", hash)\n ctx.onNewRecordPayload?.(payload.mappings ?? null)\n })\n )\n\n return {\n cancel() {\n for (const off of offs.splice(0)) off()\n },\n }\n}\n\n/**\n * No-op binder for tests that don't care about event streams. Returned handle's\n * `cancel()` is a no-op. Use {@link defaultSubscriptionsBinder} in production.\n */\nexport const noopSubscriptionsBinder: SubscriptionsBinder = () => ({\n cancel: () => {},\n})\n\nfunction dispatchStream(\n ctx: SubscriptionsContext,\n stream: GristSyncStream,\n payloadHash: string\n): void {\n ctx.dispatch({\n type: \"STREAM_RECEIVED\",\n stream,\n payloadHash,\n atMs: Date.now(),\n generation: ctx.getGeneration(),\n })\n}\n\n/**\n * Deterministic, low-cost hash of an event payload.\n *\n * We do NOT need cryptographic strength — only equality on identical inputs\n * for idempotence. A short, stable serialization is enough.\n */\nexport function quickHash(kind: string, payload: unknown): string {\n return `${kind}::${stableSerialize(payload)}`\n}\n\nfunction stableSerialize(value: unknown): string {\n if (value === null) return \"null\"\n if (value === undefined) return \"undefined\"\n const t = typeof value\n if (t === \"number\" || t === \"string\" || t === \"boolean\") return JSON.stringify(value)\n if (Array.isArray(value)) {\n return `[${value.map(stableSerialize).join(\",\")}]`\n }\n if (t === \"object\") {\n const obj = value as Record<string, unknown>\n const keys = Object.keys(obj).sort()\n const body = keys.map((k) => `${JSON.stringify(k)}:${stableSerialize(obj[k])}`).join(\",\")\n return `{${body}}`\n }\n return String(value)\n}\n","/**\n * Mappings effect — feed column-mapping payloads into the reducer from the\n * three Grist sources (`sectionApi.mappings()`, `onRecord`, `onRecords`,\n * `onNewRecord`) and trigger the mapping timeout if nothing arrives in time.\n *\n * Inputs:\n * - `declared` — `columns` from the provider options.\n * - `getGeneration()` — current snapshot generation (for stale-drop).\n * - `getGrist()` — environment accessor to `grist.sectionApi.mappings()`.\n *\n * Outputs:\n * - `MAPPING_DECLARE` (once at start when `declared.length > 0`)\n * - `MAPPING_REPORT` (one per source payload received)\n * - `MAPPING_TIMEOUT` (if no payload after `timeoutMs`)\n *\n * The effect does NOT subscribe to grist events directly — it consumes them\n * via the {@link SubscriptionsContext} payload callbacks. This keeps the\n * single-binding contract of `subscriptions.ts` intact (one `grist.onRecord`\n * binding per page).\n */\n\nimport type { GristEnvironment } from \"../environment\"\nimport type { GristAction } from \"../actions\"\nimport type { GristColumnsToMap, GristWidgetColumnMap } from \"../../../types\"\nimport type { GristGeneration } from \"../types\"\n\nexport type MappingsOptions = {\n /**\n * Time after `MAPPING_DECLARE` before falling back to `unreported` state.\n * Default 5 000 ms.\n */\n timeoutMs?: number\n /**\n * If true, dispatch `MAPPING_INVALIDATE` whenever a payload differs from\n * the last applied mappings. Default false — graceful invalidation requires\n * a `mappingsChange: true` envelope flag the SDK doesn't currently surface,\n * so we leave it opt-in for tests.\n */\n invalidateOnDiff?: boolean\n}\n\nexport const DEFAULT_MAPPING_TIMEOUT_MS = 5_000\n\nexport type MappingsHandle = {\n /** Ingest a payload from `onRecord` / `onRecords` / `onNewRecord`. */\n ingestStreamPayload(\n source: \"stream_record\" | \"stream_records\" | \"stream_new_record\",\n payload: unknown\n ): void\n /** Force a re-fetch of `sectionApi.mappings()` (e.g. on link recovery). */\n refetchSectionApi(): Promise<void>\n /** Cancel all pending timers. */\n cancel(): void\n}\n\n/**\n * Start the mapping effect. Returns a handle the manager wires into the\n * subscriptions effect's `on*Payload` callbacks.\n *\n * The effect is a no-op when `declared.length === 0` (the reducer's\n * `MAPPING_DECLARE` action turns it into `not_declared`).\n */\nexport function startMappingsEffect(\n env: GristEnvironment,\n dispatch: (action: GristAction) => void,\n signal: AbortSignal,\n declared: GristColumnsToMap,\n getGeneration: () => GristGeneration,\n options: MappingsOptions = {}\n): MappingsHandle {\n let timeoutHandle: ReturnType<GristEnvironment[\"setTimeout\"]> | null = null\n let cancelled = false\n\n const startMs = env.now()\n dispatch({ type: \"MAPPING_DECLARE\", declared, atMs: startMs, generation: getGeneration() })\n\n // No declaration → no timeout, no section_api fetch, no further work.\n if (declared.length === 0) {\n return {\n ingestStreamPayload: () => {},\n refetchSectionApi: async () => {},\n cancel: () => {},\n }\n }\n\n // Arm the unreported timeout.\n const timeoutMs = options.timeoutMs ?? DEFAULT_MAPPING_TIMEOUT_MS\n timeoutHandle = env.setTimeout(() => {\n if (cancelled || signal.aborted) return\n dispatch({ type: \"MAPPING_TIMEOUT\", atMs: env.now(), generation: getGeneration() })\n }, timeoutMs)\n\n signal.addEventListener(\"abort\", () => {\n cancelled = true\n if (timeoutHandle !== null) env.clearTimeout(timeoutHandle)\n })\n\n const ingestPayload = (\n source: \"section_api\" | \"stream_record\" | \"stream_records\" | \"stream_new_record\",\n raw: unknown\n ) => {\n if (cancelled || signal.aborted) return\n const payload = coerceMappingsPayload(raw)\n dispatch({\n type: \"MAPPING_REPORT\",\n source,\n payload,\n atMs: env.now(),\n generation: getGeneration(),\n })\n }\n\n // Kick off `sectionApi.mappings()` once.\n const refetchSectionApi = async () => {\n if (cancelled || signal.aborted) return\n const grist = env.getGrist()\n const api = grist?.sectionApi\n if (!api || typeof api.mappings !== \"function\") return\n try {\n const payload = await api.mappings()\n ingestPayload(\"section_api\", payload)\n } catch {\n ingestPayload(\"section_api\", null)\n }\n }\n void refetchSectionApi()\n\n return {\n ingestStreamPayload: (source, payload) => ingestPayload(source, payload),\n refetchSectionApi,\n cancel() {\n cancelled = true\n if (timeoutHandle !== null) env.clearTimeout(timeoutHandle)\n },\n }\n}\n\nfunction coerceMappingsPayload(raw: unknown): GristWidgetColumnMap | null {\n if (raw === null || raw === undefined) return null\n if (typeof raw !== \"object\") return null\n return raw as GristWidgetColumnMap\n}\n","/**\n * Heartbeat effect — periodically verify the RPC channel is alive.\n *\n * Strategy\n * --------\n * 1. Schedule a ping every `intervalMs` (default 30 s).\n * 2. The ping calls a cheap RPC (default `grist.docApi.getDocName()`).\n * 3. Success → `HEARTBEAT_OK`. Failure / timeout → `HEARTBEAT_FAIL`.\n * 4. Any externally-observed RPC success (the caller invokes\n * {@link HeartbeatHandle.recordRpcSuccess}) coalesces with the heartbeat\n * and reschedules the next ping — we don't burn a request when natural\n * traffic already proves the link.\n * 5. Tab hidden? Pause via `env.onVisibilityChange`. Resume on `visible`.\n *\n * The effect is **purely an observer**: it never mutates application state\n * directly — it only dispatches actions the reducer consumes.\n */\n\nimport type { GristEnvironment, TimerHandle } from \"../environment\"\nimport type { GristAction } from \"../actions\"\n\nexport type HeartbeatOptions = {\n /** How often to issue a probe when no natural RPC traffic occurred. */\n intervalMs?: number\n /** Max time to wait for the probe before counting it as a failure. */\n pingTimeoutMs?: number\n /** If true, pause the heartbeat when the document becomes hidden. */\n pauseWhenHidden?: boolean\n /**\n * Probe used when the interval elapses. Defaults to\n * `() => env.getGrist().docApi.getDocName()`. Tests pass a controlled stub.\n */\n probe?: () => Promise<unknown>\n}\n\nexport const DEFAULT_HEARTBEAT_INTERVAL_MS = 30_000\nexport const DEFAULT_HEARTBEAT_PING_TIMEOUT_MS = 10_000\n\nexport type HeartbeatHandle = {\n /** Stop scheduling and cancel any pending probe. Idempotent. */\n cancel(): void\n /**\n * Coalesce: tell the heartbeat that an unrelated RPC just succeeded. The\n * heartbeat treats it as a free `HEARTBEAT_OK` and pushes the next probe\n * out by a full interval.\n */\n recordRpcSuccess(): void\n /**\n * Coalesce: an unrelated RPC failed. The heartbeat dispatches\n * `HEARTBEAT_FAIL` without firing its own probe, so the link signal\n * degrades immediately rather than waiting for the interval.\n */\n recordRpcFailure(): void\n /** Force a probe now (e.g. on `online` event). */\n pingNow(): void\n}\n\nexport function startHeartbeatEffect(\n env: GristEnvironment,\n dispatch: (action: GristAction) => void,\n signal: AbortSignal,\n options: HeartbeatOptions = {}\n): HeartbeatHandle {\n const intervalMs = options.intervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS\n const pingTimeoutMs = options.pingTimeoutMs ?? DEFAULT_HEARTBEAT_PING_TIMEOUT_MS\n const pauseWhenHidden = options.pauseWhenHidden ?? true\n const probe = options.probe ?? defaultProbe(env)\n\n let cancelled = false\n let paused = false\n let intervalTimer: TimerHandle | null = null\n let inFlight = false\n let inFlightTimeoutTimer: TimerHandle | null = null\n\n const clearInterval = () => {\n if (intervalTimer !== null) {\n env.clearTimeout(intervalTimer)\n intervalTimer = null\n }\n }\n const clearInFlightTimeout = () => {\n if (inFlightTimeoutTimer !== null) {\n env.clearTimeout(inFlightTimeoutTimer)\n inFlightTimeoutTimer = null\n }\n }\n\n const scheduleNext = (delay = intervalMs) => {\n clearInterval()\n if (cancelled || paused) return\n intervalTimer = env.setTimeout(ping, delay)\n }\n\n const ping = () => {\n if (cancelled || paused) return\n intervalTimer = null\n if (inFlight) {\n // A previous probe still in flight; let it resolve before scheduling.\n return\n }\n inFlight = true\n\n // Timeout guard.\n inFlightTimeoutTimer = env.setTimeout(() => {\n if (!inFlight) return\n inFlight = false\n clearInFlightTimeout()\n if (!cancelled) {\n dispatch({ type: \"HEARTBEAT_FAIL\", atMs: env.now() })\n scheduleNext()\n }\n }, pingTimeoutMs)\n\n const handleOk = () => {\n if (!inFlight) return\n inFlight = false\n clearInFlightTimeout()\n if (cancelled) return\n dispatch({ type: \"HEARTBEAT_OK\", atMs: env.now() })\n scheduleNext()\n }\n const handleFail = () => {\n if (!inFlight) return\n inFlight = false\n clearInFlightTimeout()\n if (cancelled) return\n dispatch({ type: \"HEARTBEAT_FAIL\", atMs: env.now() })\n scheduleNext()\n }\n\n // Call probe synchronously so we collapse one microtask round. If it\n // throws synchronously or returns a non-thenable, settle immediately.\n let result: unknown\n try {\n result = probe()\n } catch {\n handleFail()\n return\n }\n if (\n result &&\n typeof (result as { then?: unknown }).then === \"function\"\n ) {\n ;(result as Promise<unknown>).then(handleOk, handleFail)\n } else {\n handleOk()\n }\n }\n\n // Visibility wiring.\n const offVisibility =\n pauseWhenHidden && env.onVisibilityChange\n ? env.onVisibilityChange((visible) => {\n if (cancelled) return\n if (visible) {\n if (paused) {\n paused = false\n // Refresh immediately on resume — a hidden tab might have\n // missed a long stale window.\n ping()\n }\n } else {\n paused = true\n clearInterval()\n // Note: an in-flight probe is allowed to settle naturally; we\n // just don't schedule another one.\n }\n })\n : null\n\n // Online recovery — when the network reports it's back, ping immediately.\n const offOnline =\n env.onOnline?.(() => {\n if (cancelled || paused) return\n ping()\n }) ?? null\n\n signal.addEventListener(\"abort\", () => {\n cancelled = true\n clearInterval()\n clearInFlightTimeout()\n inFlight = false\n offVisibility?.()\n offOnline?.()\n })\n\n // Boot: first probe after `intervalMs`. We don't ping at t=0 because the\n // very transition that started the heartbeat (e.g. NEGOTIATE_SUCCESS) just\n // proved the link is alive.\n scheduleNext()\n\n return {\n cancel() {\n cancelled = true\n clearInterval()\n clearInFlightTimeout()\n inFlight = false\n offVisibility?.()\n offOnline?.()\n },\n recordRpcSuccess() {\n if (cancelled || paused) return\n dispatch({ type: \"RPC_SUCCESS\", atMs: env.now() })\n scheduleNext()\n },\n recordRpcFailure() {\n if (cancelled || paused) return\n dispatch({ type: \"RPC_FAILURE\", atMs: env.now(), kind: \"other\" })\n // Don't reschedule — a real failure should keep the interval close so\n // we re-probe quickly to confirm.\n scheduleNext(Math.min(intervalMs, 1_000))\n },\n pingNow() {\n if (cancelled) return\n ping()\n },\n }\n}\n\nfunction defaultProbe(env: GristEnvironment): () => Promise<unknown> {\n return async () => {\n const g = env.getGrist()\n if (!g) throw new Error(\"grist global missing\")\n const docApi = g.docApi\n if (!docApi || typeof docApi.getDocName !== \"function\") {\n throw new Error(\"grist.docApi.getDocName unavailable\")\n }\n return docApi.getDocName()\n }\n}\n","/**\n * Re-apply the cached `grist.onOptions` interaction after `reload()`.\n *\n * `reload()` re-runs `grist.ready` (optimistic grant) but Grist often does not\n * send a fresh `onOptions`. The subscription singleton still holds the last\n * `access_level`, which must be checked once the handshake is `online` again.\n *\n * Only runs after `reload()` — not on the initial connect — so a stale\n * pre-`ready` cache cannot deny a first successful handshake.\n */\n\nimport { isAccessSufficient, parseGristRequiredAccess } from \"./access\"\nimport type { GristAction } from \"./actions\"\nimport type { GristEnvironment } from \"./environment\"\nimport type { GristWidgetSnapshot } from \"./types\"\nimport { optionsEvent } from \"../subscriptions\"\n\nexport function denyAuthzFromCachedOptionsIfInsufficient(\n snapshot: GristWidgetSnapshot,\n dispatch: (action: GristAction) => void,\n env: GristEnvironment\n): void {\n if (snapshot.lifecycle.phase !== \"online\") return\n if (snapshot.authz.state === \"denied\") return\n\n const payload = optionsEvent.peek()\n const level = parseGristRequiredAccess(payload?.interaction?.access_level)\n if (level === null) return\n\n const declared = snapshot.authz.declared\n if (isAccessSufficient(level, declared)) return\n\n dispatch({\n type: \"AUTHZ_REPORT\",\n level,\n atMs: env.now(),\n generation: snapshot.generation,\n })\n}\n","/**\n * Environment abstraction for the handshake effects layer.\n *\n * Why an abstraction\n * ------------------\n * The detect / negotiate / heartbeat effects need access to:\n * - the `grist` global (or absence thereof),\n * - `window.self !== window.top` to detect iframe embedding,\n * - timers (`setTimeout`, `clearTimeout`),\n * - the wall clock (`performance.now()` or `Date.now()`),\n * - `navigator.connection?.effectiveType` for adaptive policy.\n *\n * Putting these behind a small interface lets us test the manager and effects\n * **without touching `window`**, using deterministic fake timers and stubbed\n * detectors / negotiators.\n *\n * Production code calls {@link createBrowserEnvironment()} which returns an\n * adapter against `globalThis`. Tests pass {@link createTestEnvironment} or a\n * hand-rolled stub.\n */\n\nimport type { GristDocApi, GristReadyOptions, GristWidgetColumnMap } from \"../../types\"\n\n// ---------------------------------------------------------------------------\n// Public env interface\n// ---------------------------------------------------------------------------\n\nexport type GristEnvironment = {\n /** Wall-clock ms (monotonic preferred but not required). */\n now(): number\n /** Schedule a callback. */\n setTimeout(cb: () => void, ms: number): TimerHandle\n /** Cancel a timer. */\n clearTimeout(handle: TimerHandle): void\n\n /**\n * Probe whether the `grist` global is present in this realm.\n * Returns `{ embedded }` when the global exists; `null` otherwise.\n */\n probeGrist(): { embedded: boolean } | null\n\n /**\n * Snapshot of the `grist` global. Effects use this once detection has\n * succeeded; callers must NOT cache the result across `restart()`.\n */\n getGrist(): GristLike | null\n\n /** Adaptive detection budget hint (ms). `null` if unknown. */\n effectiveTypeHint(): EffectiveTypeHint | null\n\n /** Add a listener for `visibilitychange` / `online` / `focus`. Returns cleanup. */\n onVisibilityChange?(listener: (visible: boolean) => void): () => void\n onOnline?(listener: () => void): () => void\n}\n\nexport type TimerHandle = ReturnType<typeof setTimeout> | number | { __opaque: true }\n\nexport type EffectiveTypeHint = \"slow-2g\" | \"2g\" | \"3g\" | \"4g\" | \"5g\"\n\n/**\n * Subset of `window.grist` actually used by handshake effects. Mirrors the\n * declared shape in `grist-global.d.ts` but only the pieces we need.\n */\nexport type GristLike = {\n ready: (options?: GristReadyOptions) => Promise<void> | void\n docApi: GristDocApi | null | undefined\n onRecord?: (\n cb: (\n record: Record<string, unknown> | null,\n mappings?: GristWidgetColumnMap | null\n ) => void,\n options?: { keepEncoded?: boolean }\n ) => void\n onRecords?: (\n cb: (\n records: Record<string, unknown>[],\n mappings?: GristWidgetColumnMap | null\n ) => void,\n options?: { keepEncoded?: boolean; format?: string }\n ) => void\n onOptions?: (\n cb: (options: Record<string, unknown> | null, interaction: unknown) => void\n ) => void\n onNewRecord?: (cb: (mappings: GristWidgetColumnMap | null) => void) => void\n sectionApi?: {\n mappings: () => Promise<GristWidgetColumnMap | null>\n }\n}\n\n// ---------------------------------------------------------------------------\n// Browser environment factory\n// ---------------------------------------------------------------------------\n\nexport function createBrowserEnvironment(): GristEnvironment {\n return {\n now: () => (typeof performance !== \"undefined\" ? performance.now() : Date.now()),\n setTimeout: (cb, ms) => globalThis.setTimeout(cb, ms),\n clearTimeout: (h) => globalThis.clearTimeout(h as ReturnType<typeof setTimeout>),\n\n probeGrist() {\n if (typeof globalThis === \"undefined\") return null\n const g = (globalThis as { grist?: unknown }).grist\n if (typeof g === \"undefined\") return null\n const embedded =\n typeof window === \"undefined\" ? false : window.self !== window.top\n return { embedded }\n },\n\n getGrist() {\n const g = (globalThis as { grist?: GristLike }).grist\n return g ?? null\n },\n\n effectiveTypeHint() {\n if (typeof navigator === \"undefined\") return null\n const conn = (navigator as { connection?: { effectiveType?: string } }).connection\n const t = conn?.effectiveType\n if (t === \"slow-2g\" || t === \"2g\" || t === \"3g\" || t === \"4g\" || t === \"5g\") return t\n return null\n },\n\n onVisibilityChange(listener) {\n if (typeof document === \"undefined\") return () => {}\n const handler = () => listener(document.visibilityState === \"visible\")\n document.addEventListener(\"visibilitychange\", handler)\n return () => document.removeEventListener(\"visibilitychange\", handler)\n },\n\n onOnline(listener) {\n if (typeof window === \"undefined\") return () => {}\n const handler = () => listener()\n window.addEventListener(\"online\", handler)\n return () => window.removeEventListener(\"online\", handler)\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Test environment factory — fake timers, stubbed grist\n// ---------------------------------------------------------------------------\n\nexport type TestEnvironmentOptions = {\n /**\n * Provider for the `grist` stub. Called every `probeGrist()` / `getGrist()`.\n * Returning `null` simulates \"script not loaded yet\".\n */\n grist?: () => GristLike | null\n embedded?: () => boolean\n effectiveType?: EffectiveTypeHint\n}\n\nexport type TestEnvironment = GristEnvironment & {\n /** Manually advance virtual time. Fires due timers in order. */\n advance(ms: number): void\n /** Currently scheduled timers. */\n pendingTimers(): readonly { dueAt: number; cb: () => void }[]\n /** Trigger visibilitychange listeners. */\n emitVisibility(visible: boolean): void\n /** Trigger online listeners. */\n emitOnline(): void\n}\n\nexport function createTestEnvironment(opts: TestEnvironmentOptions = {}): TestEnvironment {\n let clock = 0\n const timers: Array<{ id: number; dueAt: number; cb: () => void }> = []\n let nextId = 1\n const visListeners = new Set<(v: boolean) => void>()\n const onlineListeners = new Set<() => void>()\n\n function advance(ms: number) {\n const target = clock + ms\n while (true) {\n timers.sort((a, b) => a.dueAt - b.dueAt)\n const due = timers.find((t) => t.dueAt <= target)\n if (!due) break\n timers.splice(timers.indexOf(due), 1)\n clock = due.dueAt\n due.cb()\n }\n clock = target\n }\n\n return {\n now: () => clock,\n setTimeout(cb, ms) {\n const id = nextId++\n timers.push({ id, dueAt: clock + ms, cb })\n return id as unknown as TimerHandle\n },\n clearTimeout(handle) {\n const id = handle as unknown as number\n const idx = timers.findIndex((t) => t.id === id)\n if (idx >= 0) timers.splice(idx, 1)\n },\n\n probeGrist() {\n const g = opts.grist?.() ?? null\n if (!g) return null\n const embedded = opts.embedded?.() ?? true\n return { embedded }\n },\n getGrist: () => opts.grist?.() ?? null,\n effectiveTypeHint: () => opts.effectiveType ?? null,\n\n onVisibilityChange(listener) {\n visListeners.add(listener)\n return () => visListeners.delete(listener)\n },\n onOnline(listener) {\n onlineListeners.add(listener)\n return () => onlineListeners.delete(listener)\n },\n\n advance,\n pendingTimers: () => timers.map((t) => ({ dueAt: t.dueAt, cb: t.cb })),\n emitVisibility(visible) {\n for (const l of visListeners) l(visible)\n },\n emitOnline() {\n for (const l of onlineListeners) l()\n },\n }\n}\n","/**\n * GristHandshakeManager — single owner of the handshake snapshot.\n *\n * Responsibilities\n * ----------------\n * 1. Hold the snapshot and the {@link GristReducerContext}.\n * 2. Run effects (detect → negotiate → subscriptions + mappings) in order.\n * 3. Bump generation on `reload()` / `restart()` and propagate cancellation\n * through a per-generation {@link AbortController}.\n * 4. Notify subscribers on each snapshot change (interface compatible with\n * React's `useSyncExternalStore`).\n *\n * The manager is a **class**, not a hook. React hooks subscribe to it via the\n * standard `subscribe` / `getSnapshot` pair so unit tests can drive the\n * manager directly with a virtual clock — no React needed.\n */\n\nimport {\n DEFAULT_REDUCER_CONFIG,\n createReducerContext,\n gristReducer,\n initialGristSnapshot,\n nextGeneration,\n type GristAction,\n type GristReducerContext,\n type GristWidgetSnapshot,\n type ReducerConfig,\n} from \"./index\"\nimport { startDetectEffect, type DetectHandle, type DetectOptions } from \"./effects/detect\"\nimport {\n startNegotiateEffect,\n type NegotiateHandle,\n type NegotiateOptions,\n} from \"./effects/negotiate\"\nimport {\n defaultSubscriptionsBinder,\n type SubscriptionsBinder,\n type SubscriptionsHandle,\n} from \"./effects/subscriptions\"\nimport {\n startMappingsEffect,\n type MappingsHandle,\n type MappingsOptions,\n} from \"./effects/mappings\"\nimport {\n startHeartbeatEffect,\n type HeartbeatHandle,\n type HeartbeatOptions,\n} from \"./effects/heartbeat\"\nimport { denyAuthzFromCachedOptionsIfInsufficient } from \"./authz-from-options-cache\"\nimport {\n createBrowserEnvironment,\n type GristEnvironment,\n} from \"./environment\"\nimport type { GristColumnsToMap, GristReadyOptions } from \"../../types\"\n\nexport type ManagerOptions = GristReadyOptions & {\n detect?: DetectOptions\n negotiate?: NegotiateOptions\n mappings?: MappingsOptions\n heartbeat?: HeartbeatOptions | false\n reducerConfig?: Partial<ReducerConfig>\n /**\n * Where to read `grist`, timers, and platform hints from. Defaults to a\n * browser adapter. Tests pass {@link createTestEnvironment}.\n */\n env?: GristEnvironment\n /**\n * Binder for grist event streams. Defaults to\n * {@link defaultSubscriptionsBinder} which wires the SDK's singleton\n * {@link \"../../subscriptions\"} module. Tests can pass `noopSubscriptionsBinder`\n * to avoid touching the real `grist` global.\n */\n subscriptionsBinder?: SubscriptionsBinder\n /**\n * Called when the manager catches an unexpected internal error.\n * Telemetry hook — does not affect reducer state.\n */\n onError?: (err: unknown, where: string) => void\n /**\n * Called from inside {@link ManagerControls.reload} BEFORE generation bump.\n * Production callers route `resetGristReadySingleton()` through here so\n * the page-level ready singleton is cleared and the next negotiate effect\n * issues a fresh `grist.ready` instead of re-using the cached promise.\n */\n onBeforeReload?: () => void\n}\n\nexport type ManagerControls = {\n /** Start the effects pipeline. Idempotent after `start()`. */\n start(): void\n /** Cancel all running effects and reset to `idle`. */\n stop(): void\n /**\n * Soft retry: bumps generation, cancels current effects, re-runs detect/\n * negotiate. Subscriptions are NOT re-bound — the underlying singletons\n * survive (and re-deliver cached values).\n */\n reload(): void\n /**\n * Hard reload of the host iframe. Browser-only — in the test env this is a\n * no-op. Production code may also call `window.location.reload()` directly.\n */\n restart(): void\n subscribe(listener: () => void): () => void\n getSnapshot(): GristWidgetSnapshot\n /**\n * Test/internal: inject an action directly. Used by integration tests that\n * want to simulate authz events, link failures, etc.\n */\n dispatch(action: GristAction): void\n /**\n * Coalesce: an external RPC succeeded — tell the heartbeat to skip the\n * next scheduled probe and reset the missed-pings counter. Returns\n * silently when the heartbeat is disabled.\n */\n recordRpcSuccess(): void\n /**\n * Coalesce: an external RPC failed — degrade the link signal immediately.\n * Returns silently when the heartbeat is disabled.\n */\n recordRpcFailure(): void\n}\n\nexport function createGristHandshakeManager(options: ManagerOptions = {}): ManagerControls {\n const env = options.env ?? createBrowserEnvironment()\n const reducerConfig: ReducerConfig = {\n ...DEFAULT_REDUCER_CONFIG,\n ...(options.reducerConfig ?? {}),\n }\n\n let snapshot = initialGristSnapshot()\n const ctx: GristReducerContext = createReducerContext()\n const listeners = new Set<() => void>()\n\n let started = false\n let abort: AbortController | null = null\n let detectHandle: DetectHandle | null = null\n let negotiateHandle: NegotiateHandle | null = null\n let subscriptionsHandle: SubscriptionsHandle | null = null\n let mappingsHandle: MappingsHandle | null = null\n let heartbeatHandle: HeartbeatHandle | null = null\n /** Set by `reload()`; cleared after one post-online cache check. */\n let recheckAuthzFromCacheAfterOnline = false\n\n const declared: GristColumnsToMap = options.columns ?? []\n const subscriptionsBinder = options.subscriptionsBinder ?? defaultSubscriptionsBinder\n\n const notify = () => {\n for (const l of listeners) {\n try {\n l()\n } catch (err) {\n options.onError?.(err, \"listener\")\n }\n }\n }\n\n const dispatch = (action: GristAction) => {\n const prev = snapshot\n const next = gristReducer(prev, action, ctx, reducerConfig)\n if (next !== prev) {\n snapshot = next\n // Run side-effects that depend on phase transitions BEFORE notifying.\n onTransition(prev, next)\n notify()\n }\n }\n\n /**\n * Effect-runner glue: react to lifecycle transitions inside the manager\n * (without polluting the pure reducer).\n *\n * Rationale: the reducer cannot start a `grist.ready()` because that's\n * impure. The reducer instead emits the transition (DETECT_AVAILABLE →\n * `lifecycle.phase === \"negotiating\"`) and the manager observes the\n * transition here to launch the next effect.\n */\n const onTransition = (prev: GristWidgetSnapshot, next: GristWidgetSnapshot) => {\n if (prev.lifecycle.phase !== \"negotiating\" && next.lifecycle.phase === \"negotiating\") {\n launchNegotiate()\n // Bind `grist.onRecord` before `grist.ready` resolves so the host's\n // post-ready cursor/data push is not dropped (iframe transport).\n launchSubscriptions()\n }\n if (prev.lifecycle.phase !== \"online\" && next.lifecycle.phase === \"online\") {\n launchMappings()\n launchHeartbeat()\n if (recheckAuthzFromCacheAfterOnline) {\n recheckAuthzFromCacheAfterOnline = false\n denyAuthzFromCachedOptionsIfInsufficient(next, dispatch, env)\n }\n }\n if (prev.lifecycle.phase === \"online\" && next.lifecycle.phase !== \"online\") {\n // Leaving the online phase — stop probing immediately.\n heartbeatHandle?.cancel()\n heartbeatHandle = null\n }\n }\n\n const launchDetect = () => {\n if (!abort) return\n detectHandle?.cancel()\n detectHandle = startDetectEffect(env, dispatch, abort.signal, options.detect)\n }\n\n const launchNegotiate = () => {\n if (!abort) return\n negotiateHandle?.cancel()\n negotiateHandle = startNegotiateEffect(\n env,\n dispatch,\n abort.signal,\n buildReadyPayload(options),\n options.negotiate\n )\n }\n\n const launchSubscriptions = () => {\n if (!abort) return\n if (subscriptionsHandle) return\n subscriptionsHandle = subscriptionsBinder({\n dispatch,\n getGeneration: () => snapshot.generation,\n onRecordPayload: (m) =>\n mappingsHandle?.ingestStreamPayload(\"stream_record\", m),\n onRecordsPayload: (m) =>\n mappingsHandle?.ingestStreamPayload(\"stream_records\", m),\n onNewRecordPayload: (m) =>\n mappingsHandle?.ingestStreamPayload(\"stream_new_record\", m),\n })\n }\n\n const launchMappings = () => {\n if (!abort) return\n launchSubscriptions()\n if (mappingsHandle) return\n mappingsHandle = startMappingsEffect(\n env,\n dispatch,\n abort.signal,\n declared,\n () => snapshot.generation,\n options.mappings\n )\n }\n\n const launchHeartbeat = () => {\n if (!abort) return\n if (options.heartbeat === false) return\n if (heartbeatHandle) return\n heartbeatHandle = startHeartbeatEffect(\n env,\n dispatch,\n abort.signal,\n options.heartbeat ?? {}\n )\n }\n\n const cancelAllEffects = () => {\n detectHandle?.cancel()\n detectHandle = null\n negotiateHandle?.cancel()\n negotiateHandle = null\n mappingsHandle?.cancel()\n mappingsHandle = null\n heartbeatHandle?.cancel()\n heartbeatHandle = null\n if (abort && !abort.signal.aborted) abort.abort()\n }\n\n const controls: ManagerControls = {\n start() {\n if (started) return\n started = true\n abort = new AbortController()\n launchDetect()\n },\n stop() {\n if (!started) return\n started = false\n cancelAllEffects()\n subscriptionsHandle?.cancel()\n subscriptionsHandle = null\n // Reset snapshot to `idle` so a subsequent `start()` is clean.\n snapshot = initialGristSnapshot()\n notify()\n },\n reload() {\n if (!started) {\n controls.start()\n return\n }\n recheckAuthzFromCacheAfterOnline = true\n try {\n options.onBeforeReload?.()\n } catch (err) {\n options.onError?.(err, \"onBeforeReload\")\n }\n cancelAllEffects()\n // Bump generation so any lingering microtasks / RPC resolutions get dropped.\n dispatch({ type: \"BUMP_GENERATION\", atMs: env.now(), reason: \"reload\" })\n // Reset snapshot to `idle` for the lifecycle restart.\n snapshot = {\n ...initialGristSnapshot(),\n generation: nextGeneration(snapshot.generation),\n }\n ctx.resolver = null\n abort = new AbortController()\n launchDetect()\n notify()\n },\n restart() {\n controls.reload()\n // Hard reload only makes sense in a real browser realm.\n if (typeof window !== \"undefined\" && typeof window.location?.reload === \"function\") {\n try {\n window.location.reload()\n } catch (err) {\n options.onError?.(err, \"restart\")\n }\n }\n },\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n getSnapshot: () => snapshot,\n dispatch,\n recordRpcSuccess() {\n heartbeatHandle?.recordRpcSuccess()\n },\n recordRpcFailure() {\n heartbeatHandle?.recordRpcFailure()\n },\n }\n\n return controls\n}\n\nfunction buildReadyPayload(options: ManagerOptions): GristReadyOptions {\n const payload: GristReadyOptions = {\n requiredAccess: options.requiredAccess ?? \"read table\",\n columns: options.columns ?? [],\n }\n if (options.allowSelectBy !== undefined) payload.allowSelectBy = options.allowSelectBy\n if (options.hasCustomOptions !== undefined) payload.hasCustomOptions = options.hasCustomOptions\n if (options.onEditOptions) payload.onEditOptions = options.onEditOptions\n return payload\n}\n","/**\n * Page-level embedded handshake manager for widgets that call `useGrist()` /\n * `useGristCore()` without mounting `<GristWidgetProvider>`.\n *\n * A single manager is shared across all hook instances on the page (ref-counted)\n * so mid-level hooks (`useGristActions`, `useGristRowsFromTable`, …) do not\n * spawn competing `grist.ready` handshakes.\n */\n\nimport {\n createGristHandshakeManager,\n type ManagerControls,\n type ManagerOptions,\n} from \"./handshake/manager\"\n\ntype EmbeddedSlot = {\n manager: ManagerControls\n refCount: number\n}\n\nlet embedded: EmbeddedSlot | null = null\n\nexport function acquireEmbeddedHandshakeManager(options: ManagerOptions): ManagerControls {\n if (!embedded) {\n embedded = {\n manager: createGristHandshakeManager(options),\n refCount: 0,\n }\n embedded.manager.start()\n }\n embedded.refCount += 1\n return embedded.manager\n}\n\nexport function releaseEmbeddedHandshakeManager(): void {\n if (!embedded) return\n embedded.refCount -= 1\n if (embedded.refCount <= 0) {\n embedded.manager.stop()\n embedded = null\n }\n}\n\n/** Test isolation — tear down the page-level embedded manager between cases. */\nexport function resetEmbeddedHandshakeManagerForTests(): void {\n if (embedded) {\n embedded.manager.stop()\n embedded = null\n }\n}\n","import { useGristCore, type UseGristCoreOptions } from \"../internal/hooks/use-grist-core\"\n\nexport type UseGristAvailabilityOptions = Pick<\n UseGristCoreOptions,\n \"availabilityMaxAttempts\" | \"availabilityIntervalMs\" | \"logWhenNotEmbedded\"\n>\n\n/**\n * Handshake-backed availability — projects `isAvailable` from the FSM.\n */\nexport function useGristAvailability(options?: UseGristAvailabilityOptions): boolean | null {\n const { isAvailable } = useGristCore(options)\n return isAvailable\n}\n","import { useGristCore, type UseGristCoreOptions } from \"../internal/hooks/use-grist-core\"\nimport type { GristDocApi, GristReadyOptions } from \"../types\"\n\nexport type UseGristReadyOptions = GristReadyOptions & {\n availabilityMaxAttempts?: number\n availabilityIntervalMs?: number\n logWhenNotEmbedded?: boolean\n}\n\nexport type UseGristReadyResult = {\n isAvailable: boolean | null\n docApi: GristDocApi | null\n error: string | null\n reload: () => Promise<void>\n}\n\n/**\n * Handshake-backed `grist.ready` — delegates to {@link useGristCore} (FSM).\n *\n * Prefer `<GristWidgetProvider>` + `useGrist()` for full widget state; use this\n * hook when you only need `docApi` and connection status.\n */\nexport function useGristReady(options?: UseGristReadyOptions): UseGristReadyResult {\n const { isAvailable, docApi, error, reload } = useGristCore(options)\n return { isAvailable, docApi, error, reload }\n}\n","/**\n * `useGristHandshake()` — React adapter over {@link GristHandshakeManager}.\n *\n * Exposes the full {@link GristWidgetSnapshot} plus derived\n * {@link GristCapabilities}, plus `reload()` / `restart()` controls.\n *\n * Identity stability\n * ------------------\n * The snapshot reference changes whenever the reducer produces a new\n * snapshot. Capabilities are memoized by snapshot identity, so consumers\n * branching on a single capability (e.g. `canWriteRecords`) avoid extra\n * re-renders unless that boolean actually flips.\n *\n * SSR-safe: `getServerSnapshot` returns the initial snapshot deterministically.\n */\n\nimport { useEffect, useMemo, useRef, useSyncExternalStore } from \"react\"\n\nimport {\n deriveCapabilities,\n deriveErrorMessage,\n deriveStatus,\n initialGristSnapshot,\n type GristCapabilities,\n type GristWidgetSnapshot,\n type GristWidgetStatus,\n} from \"../internal/handshake\"\nimport {\n createGristHandshakeManager,\n type ManagerControls,\n type ManagerOptions,\n} from \"../internal/handshake/manager\"\n\nexport type UseGristHandshakeOptions = ManagerOptions\n\nexport type UseGristHandshakeResult = {\n snapshot: GristWidgetSnapshot\n status: GristWidgetStatus\n error: string | null\n capabilities: GristCapabilities\n reload: () => void\n restart: () => void\n}\n\n/**\n * Mount a handshake manager local to the component subtree. Multiple\n * widgets in the same page should NOT call this in parallel — instead,\n * mount a single `<GristWidgetProvider>` at the app root.\n *\n * In tests, pass `options.env` (see `createTestEnvironment`) to drive the\n * machine with a virtual clock.\n */\nexport function useGristHandshake(options: UseGristHandshakeOptions = {}): UseGristHandshakeResult {\n // Memoize the manager so it survives re-renders. Caller is responsible for\n // passing stable options; in practice this is set once at the provider.\n const optionsRef = useRef(options)\n optionsRef.current = options\n const managerRef = useRef<ManagerControls | null>(null)\n if (managerRef.current === null) {\n managerRef.current = createGristHandshakeManager(options)\n }\n const manager = managerRef.current\n\n useEffect(() => {\n manager.start()\n return () => {\n manager.stop()\n }\n }, [manager])\n\n const snapshot = useSyncExternalStore(\n manager.subscribe,\n manager.getSnapshot,\n initialGristSnapshot\n )\n\n const projected = useMemo(\n () => ({\n status: deriveStatus(snapshot),\n error: deriveErrorMessage(snapshot),\n capabilities: deriveCapabilities(snapshot),\n }),\n [snapshot]\n )\n\n return useMemo(\n () => ({\n snapshot,\n status: projected.status,\n error: projected.error,\n capabilities: projected.capabilities,\n reload: manager.reload,\n restart: manager.restart,\n }),\n [snapshot, projected, manager]\n )\n}\n\n/**\n * Convenience hook — equivalent to `useGristHandshake().capabilities` but\n * skips returning the full snapshot. Intended for leaf components that only\n * need to gate UI on `canRender` / `canWriteRecords` / etc.\n */\nexport function useGristCapabilities(\n options: UseGristHandshakeOptions = {}\n): GristCapabilities {\n return useGristHandshake(options).capabilities\n}\n","/**\n * `<GristHandshakeProvider>` — opt-in React provider that mounts a single\n * {@link GristHandshakeManager} at the app root and broadcasts its snapshot\n * to all descendants.\n *\n * Use this provider when you want the full SOTA handshake model (snapshot +\n * capabilities + heartbeat + reload/restart) without the slice composition\n * of `<GristWidgetProvider>`. The two providers are independent:\n *\n * - `<GristWidgetProvider>` (legacy) — full SDK surface, hooks slices.\n * - `<GristHandshakeProvider>` (new) — handshake state machine only.\n *\n * Both can coexist if you need both surfaces; `grist.ready` is deduped by\n * the SDK's ready singleton so concurrent calls are safe.\n */\n\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n type ReactNode,\n} from \"react\"\n\nimport {\n createGristHandshakeManager,\n type ManagerControls,\n type ManagerOptions,\n} from \"../internal/handshake/manager\"\nimport {\n deriveCapabilities,\n deriveErrorMessage,\n deriveStatus,\n initialGristSnapshot,\n type GristCapabilities,\n type GristWidgetSnapshot,\n type GristWidgetStatus,\n} from \"../internal/handshake\"\n\nexport type GristHandshakeContextValue = {\n snapshot: GristWidgetSnapshot\n status: GristWidgetStatus\n error: string | null\n capabilities: GristCapabilities\n reload: () => void\n restart: () => void\n recordRpcSuccess: () => void\n recordRpcFailure: () => void\n}\n\nconst GristHandshakeReactContext = createContext<GristHandshakeContextValue | null>(null)\n\nexport type GristHandshakeProviderProps = {\n options?: ManagerOptions\n children: ReactNode\n}\n\n/**\n * Mount a single {@link GristHandshakeManager} for the React subtree and\n * expose its snapshot via context.\n *\n * The manager is created **once** on first mount; the `options` prop is\n * captured at that time. To re-create the manager with new options, unmount\n * and re-mount the provider (e.g. via a `key`).\n */\nexport function GristHandshakeProvider({ options, children }: GristHandshakeProviderProps) {\n // Manager is allocated once. Subsequent option changes are intentionally\n // ignored — handshake configuration should be stable for the lifetime of\n // the widget.\n const managerRef = useRef<ManagerControls | null>(null)\n if (managerRef.current === null) {\n managerRef.current = createGristHandshakeManager(options ?? {})\n }\n const manager = managerRef.current\n\n useEffect(() => {\n manager.start()\n return () => {\n manager.stop()\n }\n }, [manager])\n\n const snapshot = useSyncExternalStore(\n manager.subscribe,\n manager.getSnapshot,\n initialGristSnapshot\n )\n\n const value = useMemo<GristHandshakeContextValue>(\n () => ({\n snapshot,\n status: deriveStatus(snapshot),\n error: deriveErrorMessage(snapshot),\n capabilities: deriveCapabilities(snapshot),\n reload: manager.reload,\n restart: manager.restart,\n recordRpcSuccess: manager.recordRpcSuccess,\n recordRpcFailure: manager.recordRpcFailure,\n }),\n [snapshot, manager]\n )\n\n return (\n <GristHandshakeReactContext.Provider value={value}>\n {children}\n </GristHandshakeReactContext.Provider>\n )\n}\n\n/**\n * Read the handshake context. Throws if no `<GristHandshakeProvider>` is\n * mounted in the tree.\n *\n * Use {@link useGristHandshakeContextOptional} when the provider is optional\n * (e.g. when the same component is sometimes used standalone).\n */\nexport function useGristHandshakeContext(): GristHandshakeContextValue {\n const ctx = useContext(GristHandshakeReactContext)\n if (!ctx) {\n throw new Error(\n \"useGristHandshakeContext() must be used inside <GristHandshakeProvider>\"\n )\n }\n return ctx\n}\n\n/** Non-throwing variant. */\nexport function useGristHandshakeContextOptional(): GristHandshakeContextValue | null {\n return useContext(GristHandshakeReactContext)\n}\n","import { useEffect, useRef, useState } from \"react\"\n\nimport { recordEvent } from \"../internal/subscriptions\"\nimport type { GristReadyOptions, GristWidgetColumnMap } from \"../types\"\nimport { useGristReady } from \"./use-grist-ready\"\n\nexport type UseGristRecordSubscriptionResult = {\n loading: boolean\n error: string | null\n /** Latest mappings from `grist.onRecord` (logical name → real column id, or `string[]` for `allowMultiple`). */\n mappings: GristWidgetColumnMap | null\n}\n\n// Subscription errors are logged inside the singleton; nothing surfaces here.\n\n/**\n * Subscribes to the currently-selected row in the widget's section.\n *\n * The Grist event is shared across all hook instances (no leak).\n */\nexport function useGristRecord(\n cb: (record: Record<string, unknown> | null, mappings: GristWidgetColumnMap | null) => void,\n options?: GristReadyOptions\n): UseGristRecordSubscriptionResult {\n const cbRef = useRef(cb)\n useEffect(() => {\n cbRef.current = cb\n }, [cb])\n\n const [mappings, setMappings] = useState<GristWidgetColumnMap | null>(null)\n const { docApi, error: readyError } = useGristReady({\n requiredAccess: options?.requiredAccess ?? \"read table\",\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n\n useEffect(() => {\n if (!docApi) return\n return recordEvent.subscribe(({ record, mappings: nextMappings }) => {\n setMappings(nextMappings)\n cbRef.current(record, nextMappings)\n })\n }, [docApi])\n\n return {\n loading: docApi === null && !readyError,\n error: readyError,\n mappings,\n }\n}\n","import { useEffect, useRef, useState } from \"react\"\n\nimport { recordsEvent } from \"../internal/subscriptions\"\nimport type { GristReadyOptions, GristWidgetColumnMap } from \"../types\"\nimport { useGristReady } from \"./use-grist-ready\"\nimport type { UseGristRecordSubscriptionResult } from \"./use-grist-record\"\n\n/**\n * Subscribes to the full row set visible in the widget's section\n * (filters, sorts, and linked rows applied).\n */\nexport function useGristRecords(\n cb: (records: Record<string, unknown>[], mappings: GristWidgetColumnMap | null) => void,\n options?: GristReadyOptions\n): UseGristRecordSubscriptionResult {\n const cbRef = useRef(cb)\n useEffect(() => {\n cbRef.current = cb\n }, [cb])\n\n const [mappings, setMappings] = useState<GristWidgetColumnMap | null>(null)\n const { docApi, error: readyError } = useGristReady({\n requiredAccess: options?.requiredAccess ?? \"read table\",\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n\n useEffect(() => {\n if (!docApi) return\n return recordsEvent.subscribe(({ records, mappings: nextMappings }) => {\n setMappings(nextMappings)\n cbRef.current(records, nextMappings)\n })\n }, [docApi])\n\n return {\n loading: docApi === null && !readyError,\n error: readyError,\n mappings,\n }\n}\n","import { useEffect, useRef, useState } from \"react\"\n\nimport { newRecordEvent } from \"../internal/subscriptions\"\nimport type { GristReadyOptions, GristWidgetColumnMap } from \"../types\"\nimport { useGristReady } from \"./use-grist-ready\"\n\nexport type UseGristNewRecordResult = {\n /** True when the user has selected the blank \"new row\" placeholder. */\n isNewRecord: boolean\n /** Mappings reported by Grist alongside the new-record event. */\n mappings: GristWidgetColumnMap | null\n loading: boolean\n error: string | null\n}\n\n/**\n * Subscribes to `grist.onNewRecord` — fires when the user selects the blank\n * row in the linked section. Useful for form-style widgets that need to\n * switch into \"create\" mode.\n *\n * `isNewRecord` is reset to `false` whenever a real `onRecord` payload arrives,\n * so callers don't need to manage that themselves.\n */\nexport function useGristNewRecord(options?: GristReadyOptions): UseGristNewRecordResult {\n const { docApi, error: readyError } = useGristReady({\n requiredAccess: options?.requiredAccess ?? \"read table\",\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n\n const [isNewRecord, setIsNewRecord] = useState(false)\n const [mappings, setMappings] = useState<GristWidgetColumnMap | null>(null)\n const subscribedRef = useRef(false)\n\n useEffect(() => {\n if (!docApi || subscribedRef.current) return\n if (typeof grist.onNewRecord !== \"function\") return\n subscribedRef.current = true\n return newRecordEvent.subscribe(({ mappings: nextMappings }) => {\n setIsNewRecord(true)\n setMappings(nextMappings)\n })\n }, [docApi])\n\n return {\n isNewRecord,\n mappings,\n loading: docApi === null && !readyError,\n error: readyError,\n }\n}\n","import { useCallback, useState } from \"react\"\n\nimport {\n gristAddRecordAction,\n gristBulkAddRecordAction,\n gristBulkRemoveRecordAction,\n gristBulkUpdateRecordAction,\n gristRemoveRecordAction,\n gristUpdateRecordAction,\n type GristUserActionTuple,\n} from \"../helpers/grist-user-actions\"\nimport { useRpcHeartbeatCoalesce } from \"../internal/hooks/use-rpc-heartbeat-coalesce\"\nimport { useGristReady } from \"../low-level-hooks/use-grist-ready\"\nimport type { GristApplyUserActionsOptions, GristReadyOptions } from \"../types\"\n\nexport type GristActionsStatus = \"idle\" | \"running\" | \"error\"\n\nexport type UseGristActionsOptions = Pick<\n GristReadyOptions,\n \"requiredAccess\" | \"columns\" | \"allowSelectBy\" | \"hasCustomOptions\" | \"onEditOptions\"\n>\n\nexport type UseGristActionsResult = {\n status: GristActionsStatus\n error: string | null\n /** Apply raw user actions. `applyOptions.desc` is shown in the document undo stack. */\n apply: (actions: GristUserActionTuple[], applyOptions?: GristApplyUserActionsOptions) => Promise<unknown>\n updateRecord: (\n tableId: string,\n recordId: number,\n patch: Record<string, unknown>,\n applyOptions?: GristApplyUserActionsOptions\n ) => Promise<unknown>\n addRecord: (\n tableId: string,\n values: Record<string, unknown>,\n applyOptions?: GristApplyUserActionsOptions\n ) => Promise<unknown>\n removeRecord: (\n tableId: string,\n recordId: number,\n applyOptions?: GristApplyUserActionsOptions\n ) => Promise<unknown>\n bulkAddRecord: (\n tableId: string,\n values: Record<string, unknown[]>,\n applyOptions?: GristApplyUserActionsOptions\n ) => Promise<unknown>\n bulkUpdateRecord: (\n tableId: string,\n rowIds: number[],\n columnarPatch: Record<string, unknown[]>,\n applyOptions?: GristApplyUserActionsOptions\n ) => Promise<unknown>\n bulkRemoveRecord: (\n tableId: string,\n rowIds: number[],\n applyOptions?: GristApplyUserActionsOptions\n ) => Promise<unknown>\n}\n\nexport function useGristActions(options?: UseGristActionsOptions): UseGristActionsResult {\n const requiredAccess = options?.requiredAccess ?? \"full\"\n const { docApi, error: readyError } = useGristReady({\n requiredAccess,\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n const [status, setStatus] = useState<GristActionsStatus>(\"idle\")\n const [actionError, setActionError] = useState<string | null>(null)\n const coalesceRpc = useRpcHeartbeatCoalesce()\n\n const apply = useCallback(\n async (actions: GristUserActionTuple[], applyOptions?: GristApplyUserActionsOptions): Promise<unknown> => {\n if (!docApi) throw new Error(\"Grist docApi is not ready\")\n setStatus(\"running\")\n setActionError(null)\n try {\n const out = await coalesceRpc(() => docApi.applyUserActions(actions, applyOptions))\n setStatus(\"idle\")\n return out\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n setActionError(message)\n setStatus(\"error\")\n throw err\n }\n },\n [docApi, coalesceRpc]\n )\n\n const updateRecord = useCallback(\n (tableId: string, recordId: number, patch: Record<string, unknown>, applyOptions?: GristApplyUserActionsOptions) =>\n apply([gristUpdateRecordAction(tableId, recordId, patch)], applyOptions),\n [apply]\n )\n\n const addRecord = useCallback(\n (tableId: string, values: Record<string, unknown>, applyOptions?: GristApplyUserActionsOptions) =>\n apply([gristAddRecordAction(tableId, values)], applyOptions),\n [apply]\n )\n\n const removeRecord = useCallback(\n (tableId: string, recordId: number, applyOptions?: GristApplyUserActionsOptions) =>\n apply([gristRemoveRecordAction(tableId, recordId)], applyOptions),\n [apply]\n )\n\n const bulkAddRecord = useCallback(\n (tableId: string, values: Record<string, unknown[]>, applyOptions?: GristApplyUserActionsOptions) =>\n apply([gristBulkAddRecordAction(tableId, values)], applyOptions),\n [apply]\n )\n\n const bulkUpdateRecord = useCallback(\n (tableId: string, rowIds: number[], columnarPatch: Record<string, unknown[]>, applyOptions?: GristApplyUserActionsOptions) =>\n apply([gristBulkUpdateRecordAction(tableId, rowIds, columnarPatch)], applyOptions),\n [apply]\n )\n\n const bulkRemoveRecord = useCallback(\n (tableId: string, rowIds: number[], applyOptions?: GristApplyUserActionsOptions) =>\n apply([gristBulkRemoveRecordAction(tableId, rowIds)], applyOptions),\n [apply]\n )\n\n return {\n status: readyError ? \"error\" : status,\n error: readyError ?? actionError,\n apply,\n updateRecord,\n addRecord,\n removeRecord,\n bulkAddRecord,\n bulkUpdateRecord,\n bulkRemoveRecord,\n }\n}\n","/**\n * Grist `docApi.applyUserActions` payloads are arrays of user-action tuples.\n * @see https://support.getgrist.com/code/interfaces/grist_plugin_api.GristDocAPI/#applyuseractions\n */\n\nexport type GristUserActionTuple = unknown[]\n\n/** `[\"UpdateRecord\", tableId, rowId, { col: value, ... }]` */\nexport function gristUpdateRecordAction(\n tableId: string,\n rowId: number,\n patch: Record<string, unknown>\n): GristUserActionTuple {\n return [\"UpdateRecord\", tableId, rowId, patch]\n}\n\n/** `[\"AddRecord\", tableId, null, { col: value, ... }]` */\nexport function gristAddRecordAction(tableId: string, values: Record<string, unknown>): GristUserActionTuple {\n return [\"AddRecord\", tableId, null, values]\n}\n\n/** `[\"RemoveRecord\", tableId, rowId]` */\nexport function gristRemoveRecordAction(tableId: string, rowId: number): GristUserActionTuple {\n return [\"RemoveRecord\", tableId, rowId]\n}\n\n/** `[\"BulkUpdateRecord\", tableId, rowIds, { col: value[], ... }]` — column arrays must align with `rowIds`. */\nexport function gristBulkUpdateRecordAction(\n tableId: string,\n rowIds: number[],\n columnarPatch: Record<string, unknown[]>\n): GristUserActionTuple {\n return [\"BulkUpdateRecord\", tableId, rowIds, columnarPatch]\n}\n\n/**\n * `[\"BulkAddRecord\", tableId, rowIds | nullArray, { col: value[], ... }]`.\n * Pass `null` (or omit) for `rowIds` to let Grist allocate ids; in user-action\n * format Grist expects an array of nulls of the same length as the value arrays.\n */\nexport function gristBulkAddRecordAction(\n tableId: string,\n values: Record<string, unknown[]>,\n rowIds?: Array<number | null>\n): GristUserActionTuple {\n const length = inferBulkLength(values)\n const ids = rowIds ?? new Array<number | null>(length).fill(null)\n return [\"BulkAddRecord\", tableId, ids, values]\n}\n\n/** `[\"BulkRemoveRecord\", tableId, rowIds]` */\nexport function gristBulkRemoveRecordAction(tableId: string, rowIds: number[]): GristUserActionTuple {\n return [\"BulkRemoveRecord\", tableId, rowIds]\n}\n\n/** `[\"AddColumn\", tableId, colId, colInfo]` — `colInfo` shape depends on Grist version / column type. */\nexport function gristAddColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple {\n return [\"AddColumn\", tableId, colId, colInfo]\n}\n\n/** `[\"AddVisibleColumn\", tableId, colId, colInfo]` — like `AddColumn` but also adds the column to the current view section. */\nexport function gristAddVisibleColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple {\n return [\"AddVisibleColumn\", tableId, colId, colInfo]\n}\n\n/** `[\"RenameColumn\", tableId, colId, newColId]` */\nexport function gristRenameColumnAction(tableId: string, colId: string, newColId: string): GristUserActionTuple {\n return [\"RenameColumn\", tableId, colId, newColId]\n}\n\n/** `[\"ModifyColumn\", tableId, colId, colInfo]` */\nexport function gristModifyColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple {\n return [\"ModifyColumn\", tableId, colId, colInfo]\n}\n\n/** `[\"RemoveColumn\", tableId, colId]` */\nexport function gristRemoveColumnAction(tableId: string, colId: string): GristUserActionTuple {\n return [\"RemoveColumn\", tableId, colId]\n}\n\n/** `[\"AddTable\", tableId, tableInfo]` */\nexport function gristAddTableAction(tableId: string, tableInfo: Record<string, unknown>): GristUserActionTuple {\n return [\"AddTable\", tableId, tableInfo]\n}\n\n/** `[\"RenameTable\", tableId, newTableId]` */\nexport function gristRenameTableAction(tableId: string, newTableId: string): GristUserActionTuple {\n return [\"RenameTable\", tableId, newTableId]\n}\n\n/** `[\"RemoveTable\", tableId]` */\nexport function gristRemoveTableAction(tableId: string): GristUserActionTuple {\n return [\"RemoveTable\", tableId]\n}\n\n/** `[\"DuplicateTable\", tableId, newTableId, options?]` */\nexport function gristDuplicateTableAction(\n tableId: string,\n newTableId: string,\n options?: Record<string, unknown>\n): GristUserActionTuple {\n return options ? [\"DuplicateTable\", tableId, newTableId, options] : [\"DuplicateTable\", tableId, newTableId]\n}\n\n/** `[\"ReplaceTableData\", tableId, rowIds, columnarData]` */\nexport function gristReplaceTableDataAction(\n tableId: string,\n rowIds: Array<number | null>,\n columnarData: Record<string, unknown[]>\n): GristUserActionTuple {\n return [\"ReplaceTableData\", tableId, rowIds, columnarData]\n}\n\n/** `[\"SetDocumentInfo\", info]` */\nexport function gristSetDocumentInfoAction(info: Record<string, unknown>): GristUserActionTuple {\n return [\"SetDocumentInfo\", info]\n}\n\nfunction inferBulkLength(values: Record<string, unknown[]>): number {\n for (const arr of Object.values(values)) if (Array.isArray(arr)) return arr.length\n return 0\n}\n","/**\n * `useRpcHeartbeatCoalesce()` — coalesce real RPC traffic with the handshake\n * heartbeat.\n *\n * When a {@link GristHandshakeManager} is mounted (via `<GristWidgetProvider>`\n * or `<GristHandshakeProvider>`), every successful Grist RPC call is treated\n * as a natural heartbeat: the manager's `recordRpcSuccess()` resets the\n * missed-ping counter and pushes the next probe out by a full interval, so\n * a chatty widget never burns dedicated heartbeat probes.\n *\n * Failures are NOT coalesced: semantic errors (validation, \"row not found\",\n * \"no such column\") would briefly flash a connection-degraded indicator even\n * though the channel is alive. Instead we let the regular heartbeat probe\n * detect real transport failures on its own schedule (~30 s). The user\n * already sees the action error immediately, so the heartbeat indicator\n * doesn't need to race.\n *\n * Outside a provider (no manager in context), the wrapper is a zero-cost\n * passthrough — every slice hook keeps working exactly like the pre-FSM\n * release.\n */\n\nimport { useCallback } from \"react\"\n\nimport { useGristManagerOptional } from \"../handshake-manager-context\"\n\nexport type RpcHeartbeatCoalesce = <T>(fn: () => Promise<T>) => Promise<T>\n\nexport function useRpcHeartbeatCoalesce(): RpcHeartbeatCoalesce {\n const manager = useGristManagerOptional()\n return useCallback<RpcHeartbeatCoalesce>(\n async (fn) => {\n try {\n const result = await fn()\n manager?.recordRpcSuccess()\n return result\n } catch (err) {\n throw err\n }\n },\n [manager]\n )\n}\n","import { useCallback, useEffect, useState } from \"react\"\n\nimport { recordEvent } from \"../internal/subscriptions\"\nimport { useGristReady } from \"../low-level-hooks/use-grist-ready\"\nimport type { GristReadyOptions, GristWidgetColumnMap } from \"../types\"\n\nexport type UseGristMappedRecordResult<T> = {\n rawRecord: Record<string, unknown> | null\n mappedRecord: T | null\n /** Current column mappings reported by Grist. */\n mappings: GristWidgetColumnMap | null\n loading: boolean\n error: string | null\n /**\n * Reverse-map a logical patch (`{ logicalName: value }`) into a real-column\n * patch (`{ realColId: value }`) ready for `applyUserActions` /\n * `useGristTableOps.update`.\n * Falls back to `grist.mapColumnNamesBack` when present, otherwise resolves\n * names manually from `mappings` (single-mapped columns only).\n */\n mapBack: (patch: Partial<T>) => Record<string, unknown>\n /** Resolve a single logical name to its current real column id. */\n resolveColumnId: (logicalName: keyof T & string) => string | undefined\n}\n\nfunction manualMapBack<T>(patch: Partial<T>, mappings: GristWidgetColumnMap | null): Record<string, unknown> {\n if (!mappings) return { ...(patch as Record<string, unknown>) }\n const out: Record<string, unknown> = {}\n for (const [name, value] of Object.entries(patch as Record<string, unknown>)) {\n const target = mappings[name]\n if (Array.isArray(target)) {\n // allowMultiple: cannot decide which underlying column to write — skip and warn.\n if (typeof console !== \"undefined\") {\n console.warn(`[lib/grist] mapBack: cannot reverse-map allowMultiple field \"${name}\" without grist.mapColumnNamesBack`)\n }\n continue\n }\n out[target ?? name] = value\n }\n return out\n}\n\n/**\n * Subscribes to the selected row, applies `grist.mapColumnNames` so widget code\n * sees logical column names, and exposes `mapBack` for safe writes.\n */\nexport function useGristMappedRecord<T = Record<string, unknown>>(\n options?: GristReadyOptions\n): UseGristMappedRecordResult<T> {\n const requiredAccess = options?.requiredAccess ?? \"read table\"\n const { docApi, error: readyError } = useGristReady({\n requiredAccess,\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n const [rawRecord, setRawRecord] = useState<Record<string, unknown> | null>(null)\n const [mappedRecord, setMappedRecord] = useState<T | null>(null)\n const [mappings, setMappings] = useState<GristWidgetColumnMap | null>(null)\n\n useEffect(() => {\n if (!docApi) return\n return recordEvent.subscribe(({ record, mappings: nextMappings }) => {\n setRawRecord(record)\n setMappings(nextMappings)\n if (!record) {\n setMappedRecord(null)\n return\n }\n if (typeof grist.mapColumnNames === \"function\") {\n const mapped = grist.mapColumnNames(record)\n setMappedRecord(\n mapped !== null && mapped !== undefined ? (mapped as T) : null\n )\n return\n }\n setMappedRecord(record as T)\n })\n }, [docApi])\n\n const mapBack = useCallback(\n (patch: Partial<T>): Record<string, unknown> => {\n if (typeof grist.mapColumnNamesBack === \"function\") {\n return grist.mapColumnNamesBack(patch as Record<string, unknown>)\n }\n return manualMapBack<T>(patch, mappings)\n },\n [mappings]\n )\n\n const resolveColumnId = useCallback(\n (logicalName: keyof T & string): string | undefined => {\n const value = mappings?.[logicalName]\n if (Array.isArray(value)) return undefined\n return value ?? undefined\n },\n [mappings]\n )\n\n return {\n rawRecord,\n mappedRecord,\n mappings,\n loading: docApi === null && !readyError,\n error: readyError,\n mapBack,\n resolveColumnId,\n }\n}\n","import { useCallback, useState } from \"react\"\n\nimport { useRpcHeartbeatCoalesce } from \"../internal/hooks/use-rpc-heartbeat-coalesce\"\nimport { useGristReady } from \"../low-level-hooks/use-grist-ready\"\nimport type {\n GristMinimalRecord,\n GristReadyOptions,\n GristTableNewRecord,\n GristTableOpOptions,\n GristTableOperations,\n GristTableUpdateRecord,\n GristTableUpsertOptions,\n GristTableUpsertRecord,\n} from \"../types\"\n\nexport type GristTableOpsStatus = \"idle\" | \"running\" | \"error\"\n\nexport type UseGristTableOpsOptions = {\n /**\n * Table to operate on:\n * - `\"selected\"` (default): use `grist.selectedTable` (the widget's current table).\n * - any other string: use `grist.getTable(tableId)`.\n */\n tableId?: \"selected\" | string\n /** Defaults to `\"full\"` since these operations write to the document. */\n requiredAccess?: GristReadyOptions[\"requiredAccess\"]\n} & Pick<GristReadyOptions, \"columns\" | \"allowSelectBy\" | \"hasCustomOptions\" | \"onEditOptions\">\n\nexport type UseGristTableOpsResult = {\n status: GristTableOpsStatus\n error: string | null\n /** Resolves the runtime tableId (only meaningful when `tableId` is `\"selected\"`). */\n getTableId: () => Promise<string>\n create: (\n records: GristTableNewRecord | GristTableNewRecord[],\n opOptions?: GristTableOpOptions\n ) => Promise<GristMinimalRecord | GristMinimalRecord[]>\n update: (\n records: GristTableUpdateRecord | GristTableUpdateRecord[],\n opOptions?: GristTableOpOptions\n ) => Promise<void>\n upsert: (\n records: GristTableUpsertRecord | GristTableUpsertRecord[],\n opOptions?: GristTableUpsertOptions\n ) => Promise<void>\n destroy: (recordIds: number | number[]) => Promise<void>\n}\n\nfunction resolveOps(tableId: \"selected\" | string): GristTableOperations {\n if (tableId === \"selected\") {\n if (!grist.selectedTable) throw new Error(\"grist.selectedTable is not available\")\n return grist.selectedTable\n }\n if (typeof grist.getTable !== \"function\") {\n throw new Error(\"grist.getTable is not available in this Grist version\")\n }\n return grist.getTable(tableId)\n}\n\n/**\n * Strongly-typed CRUD wrapper around Grist's `TableOperations` (`grist.selectedTable`\n * or `grist.getTable(tableId)`). Prefer this over hand-rolled `applyUserActions`.\n *\n * @see https://support.getgrist.com/code/interfaces/TableOperations.TableOperations/\n */\nexport function useGristTableOps(options?: UseGristTableOpsOptions): UseGristTableOpsResult {\n const requiredAccess = options?.requiredAccess ?? \"full\"\n const tableIdOption: \"selected\" | string = options?.tableId ?? \"selected\"\n const { docApi, error: readyError } = useGristReady({\n requiredAccess,\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n\n const [status, setStatus] = useState<GristTableOpsStatus>(\"idle\")\n const [actionError, setActionError] = useState<string | null>(null)\n const coalesceRpc = useRpcHeartbeatCoalesce()\n\n // `resolveOps` is synchronous and idempotent (it only reads `grist.selectedTable`\n // / `grist.getTable`), so derive `ops` during render instead of via effect.\n let ops: GristTableOperations | null = null\n let resolveError: string | null = null\n if (docApi) {\n try {\n ops = resolveOps(tableIdOption)\n } catch (err) {\n resolveError = err instanceof Error ? err.message : String(err)\n }\n }\n\n const wrap = useCallback(\n async <T>(run: (ops: GristTableOperations) => Promise<T>): Promise<T> => {\n if (!ops) throw new Error(\"Grist table operations are not ready\")\n setStatus(\"running\")\n setActionError(null)\n try {\n const out = await coalesceRpc(() => run(ops))\n setStatus(\"idle\")\n return out\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n setActionError(message)\n setStatus(\"error\")\n throw err\n }\n },\n [ops, coalesceRpc]\n )\n\n const getTableId = useCallback(async () => {\n if (!ops) throw new Error(\"Grist table operations are not ready\")\n return ops.getTableId()\n }, [ops])\n\n const create = useCallback(\n (records: GristTableNewRecord | GristTableNewRecord[], opOptions?: GristTableOpOptions) =>\n wrap((o) => o.create(records, opOptions)),\n [wrap]\n )\n\n const update = useCallback(\n (records: GristTableUpdateRecord | GristTableUpdateRecord[], opOptions?: GristTableOpOptions) =>\n wrap((o) => o.update(records, opOptions)),\n [wrap]\n )\n\n const upsert = useCallback(\n (records: GristTableUpsertRecord | GristTableUpsertRecord[], opOptions?: GristTableUpsertOptions) =>\n wrap((o) => o.upsert(records, opOptions)),\n [wrap]\n )\n\n const destroy = useCallback(\n (recordIds: number | number[]) => wrap((o) => o.destroy(recordIds)),\n [wrap]\n )\n\n return {\n status: readyError || resolveError ? \"error\" : status,\n error: readyError ?? resolveError ?? actionError,\n getTableId,\n create,\n update,\n upsert,\n destroy,\n }\n}\n","import { useCallback, useContext, useEffect, useRef, useState } from \"react\"\n\nimport { tableDataToRows, type GristColumnarTable } from \"../helpers/grist-table-data\"\nimport { GristReadsContext } from \"../internal/contexts\"\nimport { useRpcHeartbeatCoalesce } from \"../internal/hooks/use-rpc-heartbeat-coalesce\"\nimport { useGristReady } from \"../low-level-hooks/use-grist-ready\"\nimport type { GristReadyOptions, GristRowRecord } from \"../types\"\n\nexport type UseGristRowsFromTableOptions<T> = {\n tableId: string\n /** Re-fetch on a fixed cadence (ms). Default: disabled. */\n pollIntervalMs?: number\n /** Map raw rows to a domain shape (e.g. `decodeGristValue` your columns). */\n transform?: (row: GristRowRecord) => GristRowRecord<T>\n requiredAccess?: GristReadyOptions[\"requiredAccess\"]\n} & Pick<GristReadyOptions, \"columns\" | \"allowSelectBy\" | \"hasCustomOptions\" | \"onEditOptions\">\n\nexport type UseGristRowsFromTableResult<T> = {\n rows: GristRowRecord<T>[] | null\n loading: boolean\n error: string | null\n /** Force a re-fetch (call after writes that should be reflected immediately). */\n refresh: () => Promise<void>\n}\n\n/**\n * Subscribes to a full table's rows via `docApi.fetchTable`.\n *\n * Unlike `useGristRecords` (which is bound to the widget's selected section),\n * this hook works against any table id and **doesn't auto-update on Grist\n * writes from elsewhere** — call `refresh()` after your own writes, or set\n * `pollIntervalMs` for polling.\n */\nexport function useGristRowsFromTable<T = Record<string, unknown>>(\n options: UseGristRowsFromTableOptions<T>\n): UseGristRowsFromTableResult<T> {\n const requiredAccess = options.requiredAccess ?? \"read table\"\n const { docApi, error: readyError } = useGristReady({\n requiredAccess,\n columns: options.columns,\n allowSelectBy: options.allowSelectBy,\n hasCustomOptions: options.hasCustomOptions,\n onEditOptions: options.onEditOptions,\n })\n\n // When inside a GristWidgetProvider, delegate to the provider's guarded\n // fetchTable so access-insufficient errors flow through readError → alerts.\n const providerFetchTable = useContext(GristReadsContext)?.fetchTable\n\n const [rows, setRows] = useState<GristRowRecord<T>[] | null>(null)\n const [loading, setLoading] = useState(false)\n const [fetchError, setFetchError] = useState<string | null>(null)\n const transformRef = useRef(options.transform)\n transformRef.current = options.transform\n const coalesceRpc = useRpcHeartbeatCoalesce()\n\n const refresh = useCallback(async () => {\n if (!docApi) return\n setLoading(true)\n setFetchError(null)\n try {\n const data = providerFetchTable\n ? await providerFetchTable(options.tableId)\n : ((await coalesceRpc(\n () => docApi.fetchTable(options.tableId) as Promise<GristColumnarTable>\n )) as GristColumnarTable)\n const raw = tableDataToRows(data)\n const t = transformRef.current\n setRows(t ? raw.map((r) => t(r)) : (raw as GristRowRecord<T>[]))\n } catch (err) {\n setFetchError(err instanceof Error ? err.message : String(err))\n setRows(null)\n } finally {\n setLoading(false)\n }\n }, [docApi, providerFetchTable, options.tableId, coalesceRpc])\n\n useEffect(() => {\n if (docApi) void refresh()\n }, [docApi, refresh])\n\n useEffect(() => {\n if (!docApi || !options.pollIntervalMs || options.pollIntervalMs <= 0) return\n const id = window.setInterval(() => void refresh(), options.pollIntervalMs)\n return () => window.clearInterval(id)\n }, [docApi, options.pollIntervalMs, refresh])\n\n return {\n rows,\n loading,\n error: readyError ?? fetchError,\n refresh,\n }\n}\n","import type { GristDocApi, GristReplicaColumn, GristRowRecord } from \"../types\"\nimport { decodeGristValue } from \"./grist-value-codec\"\nimport {\n type GristSafeParseCellResult,\n type GristSafeTableCellMeta,\n safeParseGristTableData,\n type GristSafeTableCellIssue,\n type SafeParseGristTableDataOptions,\n} from \"./grist-safe-parse\"\n\n/**\n * Column-oriented table payload from `grist.docApi.fetchTable`.\n */\nexport type GristColumnarTable = Record<string, unknown[]>\nexport type GristTableRowsOrder = \"none\" | \"id\" | \"grist\"\nexport type FetchGristTableRowsOptions = {\n /** Optional schema metadata map to decode cells (except `id`) with `decodeGristValue`. */\n columns?: Record<string, GristReplicaColumn>\n /**\n * Row ordering strategy.\n * - `grist` (default): `manualSort` ascending when present, fallback `id`.\n * - `id`: numeric `id` ascending.\n * - `none`: preserve raw `fetchTable` row order.\n */\n order?: GristTableRowsOrder\n /**\n * Enable safe parsing with issue reporting.\n * - `true`: use safe parsing with defaults.\n * - object: pass safe-parse options (`displayHelpersByRef`, `refMode`, ...).\n */\n safeParse?: boolean | Omit<SafeParseGristTableDataOptions, \"columns\" | \"order\">\n /** Optional callback fired with per-cell parse issues when `safeParse` is enabled. */\n onSafeParseIssues?: (issues: GristSafeTableCellIssue[]) => void\n /** Optional callback fired with enriched per-cell safe-parse metadata. */\n onSafeParseCellMeta?: (meta: GristSafeTableCellMeta[]) => void\n /** Optional callback fired with keyed per-cell metadata (`${rowId}:${colId}`). */\n onSafeParseCellMetaByKey?: (metaByKey: Record<string, GristSafeTableCellMeta>) => void\n}\n\nfunction inferDisplayHelpersByNameFromData(data: GristColumnarTable): Record<string, string> {\n const out: Record<string, string> = {}\n for (const colId of Object.keys(data)) {\n if (!colId.startsWith(\"gristHelper_\")) continue\n const baseColId = colId.slice(\"gristHelper_\".length)\n if (!baseColId) continue\n if (!Array.isArray(data[baseColId])) continue\n out[baseColId] = colId\n }\n return out\n}\n\nfunction enrichSafeParseColumnsFromData(\n data: GristColumnarTable,\n columns?: Record<string, GristReplicaColumn>\n): Record<string, GristReplicaColumn> {\n const out: Record<string, GristReplicaColumn> = { ...(columns ?? {}) }\n const keys = Object.keys(data)\n const positionByColId = new Map<string, number>(keys.map((colId, index) => [colId, index + 1]))\n\n // Ensure helper columns exist in safe-parse metadata even when caller schema\n // excluded them (common for UI schemas). Parent positions are synthesized from\n // fetchTable key order so helper/ref fallback matching still works.\n for (const colId of keys) {\n if (!colId.startsWith(\"gristHelper_\")) continue\n if (out[colId]) continue\n out[colId] = {\n type: \"Any\",\n label: colId,\n parentPos: positionByColId.get(colId),\n }\n }\n return out\n}\n\nfunction rowsFromSafeCellResults(\n parsedByColumn: Record<string, GristSafeParseCellResult[]>\n): GristRowRecord<Record<string, unknown>>[] {\n const colIds = Object.keys(parsedByColumn)\n const rowCount = colIds.reduce((max, colId) => {\n const len = parsedByColumn[colId]?.length ?? 0\n return Math.max(max, len)\n }, 0)\n const rows: GristRowRecord<Record<string, unknown>>[] = []\n for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {\n const row: Record<string, unknown> = {}\n for (const colId of colIds) {\n row[colId] = parsedByColumn[colId]?.[rowIndex] ?? null\n }\n rows.push(row as GristRowRecord<Record<string, unknown>>)\n }\n return rows\n}\n\nfunction safeCellRowId(row: Record<string, unknown>): number | null {\n const idCell = row.id\n if (!idCell || typeof idCell !== \"object\" || !(\"typedValue\" in idCell)) return null\n const typed = (idCell as { typedValue?: unknown }).typedValue\n return typeof typed === \"number\" && Number.isFinite(typed) ? typed : null\n}\n\nfunction orderSafeCellRowsByParsedRows(\n rows: GristRowRecord<Record<string, unknown>>[],\n orderedParsedRows: ReadonlyArray<Record<string, unknown>>\n): GristRowRecord<Record<string, unknown>>[] {\n const byId = new Map<number, GristRowRecord<Record<string, unknown>>[]>()\n for (const row of rows) {\n const id = safeCellRowId(row)\n if (id === null) continue\n const bucket = byId.get(id)\n if (bucket) bucket.push(row)\n else byId.set(id, [row])\n }\n const out: GristRowRecord<Record<string, unknown>>[] = []\n for (const parsedRow of orderedParsedRows) {\n const id = typeof parsedRow.id === \"number\" ? parsedRow.id : null\n if (id === null) continue\n const bucket = byId.get(id)\n if (!bucket || bucket.length === 0) continue\n const next = bucket.shift()\n if (next) out.push(next)\n }\n // Preserve any unexpected leftovers (defensive).\n for (const bucket of byId.values()) {\n out.push(...bucket)\n }\n return out\n}\n\n/**\n * Project safe-parse rows to UI display rows by taking each cell's `displayValue`.\n * Useful for grids that render human-friendly values while keeping safe-cell\n * objects available for logic and metadata.\n */\nexport function safeParseRowsToDisplayRows(\n rows: ReadonlyArray<Record<string, unknown>>\n): GristRowRecord<Record<string, unknown>>[] {\n return rows.map((row) => {\n const out: Record<string, unknown> = {}\n for (const [colId, cell] of Object.entries(row)) {\n if (cell && typeof cell === \"object\" && \"displayValue\" in cell) {\n out[colId] = (cell as { displayValue: unknown }).displayValue\n } else {\n out[colId] = cell\n }\n }\n return out as GristRowRecord<Record<string, unknown>>\n })\n}\n\n/** Row count from columnar `fetchTable` data (prefers `id` column length). */\nexport function computeRowCount(data: Record<string, unknown[]>): number {\n const idCol = data.id\n if (Array.isArray(idCol)) return idCol.length\n for (const key of Object.keys(data)) {\n const col = data[key]\n if (Array.isArray(col)) return col.length\n }\n return 0\n}\n\n/**\n * Decode columnar `fetchTable` into replica row objects (same convention as the playground grid).\n */\nexport function decodeColumnarToReplicaRows(\n data: GristColumnarTable,\n columnIds: string[],\n columns: Record<string, GristReplicaColumn>,\n maxRows?: number\n): GristRowRecord[] {\n const nFull = computeRowCount(data)\n const n = maxRows !== undefined ? Math.min(nFull, maxRows) : nFull\n const out: GristRowRecord[] = []\n for (let i = 0; i < n; i++) {\n const row: Record<string, unknown> = {}\n for (const k of columnIds) {\n const arr = data[k]\n const raw = Array.isArray(arr) ? arr[i] : undefined\n if (k === \"id\") {\n row[k] = raw\n } else {\n row[k] = decodeGristValue(raw, columns[k]) as unknown\n }\n }\n out.push(row as GristRowRecord)\n }\n return out\n}\n\n/** Turn `fetchTable` output into row objects (order follows the `id` column). */\nexport function tableDataToRows<T = Record<string, unknown>>(\n data: GristColumnarTable\n): GristRowRecord<T>[] {\n const idCol = data.id\n if (!Array.isArray(idCol) || idCol.length === 0) return []\n const keys = Object.keys(data)\n const n = idCol.length\n const rows: GristRowRecord<T>[] = []\n for (let i = 0; i < n; i++) {\n const row: Record<string, unknown> = {}\n for (const k of keys) {\n const col = data[k]\n row[k] = Array.isArray(col) ? col[i] : undefined\n }\n rows.push(row as GristRowRecord<T>)\n }\n return rows\n}\n\nfunction manualSortKey(row: Record<string, unknown>): number | null {\n const v = row.manualSort\n if (typeof v === \"number\" && !Number.isNaN(v)) return v\n if (typeof v === \"string\" && v.trim() !== \"\") {\n const n = Number(v)\n if (!Number.isNaN(n)) return n\n }\n return null\n}\n\n/**\n * Sort rows by Grist's display order: `manualSort` ascending when present on\n * any row, otherwise numeric `id` ascending.\n */\nexport function sortRowsByGristOrder<T = Record<string, unknown>>(\n rows: readonly GristRowRecord<T>[]\n): GristRowRecord<T>[] {\n const hasManual = rows.some((r) => manualSortKey(r as Record<string, unknown>) !== null)\n if (!hasManual) {\n return [...rows].sort((a, b) => {\n const ida = typeof a.id === \"number\" ? a.id : 0\n const idb = typeof b.id === \"number\" ? b.id : 0\n return ida - idb\n })\n }\n return [...rows].sort((a, b) => {\n const ma = manualSortKey(a as Record<string, unknown>)\n const mb = manualSortKey(b as Record<string, unknown>)\n if (ma !== null && mb !== null && ma !== mb) return ma - mb\n if (ma !== null && mb === null) return -1\n if (ma === null && mb !== null) return 1\n const ida = typeof a.id === \"number\" ? a.id : 0\n const idb = typeof b.id === \"number\" ? b.id : 0\n return ida - idb\n })\n}\n\n/** Row index where `id` matches `rowId`, or `null`. */\nexport function findRowIndexById(data: GristColumnarTable, rowId: number | string): number | null {\n const idCol = data.id\n if (!Array.isArray(idCol)) return null\n const idx = idCol.findIndex((v) => v === rowId || (typeof v === \"number\" && v === Number(rowId)))\n return idx === -1 ? null : idx\n}\n\n/** Single row at `index`, or `null` if out of range. */\nexport function rowAtIndex<T = Record<string, unknown>>(\n data: GristColumnarTable,\n index: number\n): GristRowRecord<T> | null {\n const idCol = data.id\n if (!Array.isArray(idCol) || index < 0 || index >= idCol.length) return null\n const keys = Object.keys(data)\n const row: Record<string, unknown> = {}\n for (const k of keys) {\n const col = data[k]\n row[k] = Array.isArray(col) ? col[index] : undefined\n }\n return row as GristRowRecord<T>\n}\n\n/**\n * Fetch a single row from any table by id. Convenience around\n * `docApi.fetchTable` + {@link findRowIndexById} + {@link rowAtIndex}.\n */\nexport async function fetchGristRow<T = Record<string, unknown>>(\n docApi: GristDocApi,\n tableId: string,\n rowId: number\n): Promise<GristRowRecord<T> | null> {\n const data = (await docApi.fetchTable(tableId)) as GristColumnarTable\n const index = findRowIndexById(data, rowId)\n if (index === null) return null\n return rowAtIndex<T>(data, index)\n}\n\n/**\n * Same row materialization as {@link fetchGristTableRows} after `fetchTable`\n * returns columnar data. Hosts that already have columnar data (e.g. the\n * emulator building from in-memory rows) can use this and skip `docApi.fetchTable`.\n */\nexport function materializeGristRowsFromColumnar<T extends Record<string, unknown> = Record<string, unknown>>(\n data: GristColumnarTable,\n options?: FetchGristTableRowsOptions\n): GristRowRecord<T>[] {\n const order = options?.order ?? \"grist\"\n if (options?.safeParse) {\n const safeOptions = options.safeParse === true ? {} : options.safeParse\n const columnsForSafeParse = enrichSafeParseColumnsFromData(data, options?.columns)\n const displayHelpersByRef = {\n ...inferDisplayHelpersByNameFromData(data),\n ...(safeOptions.displayHelpersByRef ?? {}),\n }\n const parsed = safeParseGristTableData<T>(data, {\n ...safeOptions,\n columns: columnsForSafeParse,\n displayHelpersByRef,\n order,\n })\n options.onSafeParseIssues?.(parsed.cellIssues)\n options.onSafeParseCellMeta?.(parsed.cellMeta)\n options.onSafeParseCellMetaByKey?.(parsed.cellMetaByKey)\n const rawSafeRows = rowsFromSafeCellResults(parsed.cellResultsByColumn)\n const orderedSafeRows =\n order === \"none\"\n ? rawSafeRows\n : orderSafeCellRowsByParsedRows(rawSafeRows, parsed.rows as ReadonlyArray<Record<string, unknown>>)\n return orderedSafeRows as GristRowRecord<T>[]\n }\n\n let rows: GristRowRecord<T>[]\n if (options?.columns) {\n const columnIds = Object.keys(data)\n rows = decodeColumnarToReplicaRows(data, columnIds, options.columns) as GristRowRecord<T>[]\n } else {\n rows = tableDataToRows<T>(data)\n }\n\n if (order === \"none\") return rows\n if (order === \"id\") {\n return [...rows].sort((a, b) => {\n const ida = typeof a.id === \"number\" ? a.id : 0\n const idb = typeof b.id === \"number\" ? b.id : 0\n return ida - idb\n })\n }\n return sortRowsByGristOrder(rows)\n}\n\n/**\n * Fetch a table and return rows as objects, optionally decoded from schema and sorted.\n */\nexport async function fetchGristTableRows<T extends Record<string, unknown> = Record<string, unknown>>(\n docApi: GristDocApi,\n tableId: string,\n options: FetchGristTableRowsOptions & { safeParse: true | Omit<SafeParseGristTableDataOptions, \"columns\" | \"order\"> }\n): Promise<GristRowRecord<{ [K in keyof T]: GristSafeParseCellResult<T[K]> | null }>[]>\nexport async function fetchGristTableRows<T extends Record<string, unknown> = Record<string, unknown>>(\n docApi: GristDocApi,\n tableId: string,\n options?: FetchGristTableRowsOptions\n): Promise<GristRowRecord<T>[]>\nexport async function fetchGristTableRows<T extends Record<string, unknown> = Record<string, unknown>>(\n docApi: GristDocApi,\n tableId: string,\n options?: FetchGristTableRowsOptions\n): Promise<GristRowRecord<T>[]> {\n const data = (await docApi.fetchTable(tableId)) as GristColumnarTable\n return materializeGristRowsFromColumnar<T>(data, options)\n}\n","import { createContext, useContext, useMemo, type Context } from \"react\"\n\nimport type { ComposedGristWidgetSlices } from \"./compose-grist-widget\"\nimport { mergeGristResultFromSlices } from \"./merge-grist-result\"\nimport type { UseGristResult } from \"../use-grist-types\"\nimport type { UseGristAttachmentsRestResult } from \"./hooks/use-grist-attachments-rest\"\nimport type { UseGristCoreResult } from \"./hooks/use-grist-core\"\nimport type { UseGristCurrentTableResult } from \"./hooks/use-grist-current-table\"\nimport type { UseGristLinkingResult } from \"./hooks/use-grist-linking\"\nimport type { UseGristReadsResult } from \"./hooks/use-grist-reads\"\nimport type { UseGristSectionApiResult } from \"./hooks/use-grist-section-api\"\nimport type { UseGristSelectionResult } from \"./hooks/use-grist-selection\"\nimport type { UseGristTableOpsBoundResult } from \"./hooks/use-grist-table-ops-bound\"\nimport type { UseGristThemeResult } from \"./hooks/use-grist-theme\"\nimport type { UseGristWidgetOptionsApiResult } from \"./hooks/use-grist-widget-options-api\"\n\nexport type GristCoreContextValue = UseGristCoreResult & {\n tableError: string | null\n}\n\nexport type GristStatusSliceValue = GristCoreContextValue & UseGristCurrentTableResult\n\nexport const GristCoreContext = createContext<GristCoreContextValue | null>(null)\nexport const GristSelectionContext = createContext<UseGristSelectionResult | null>(null)\nexport const GristCurrentTableContext = createContext<UseGristCurrentTableResult | null>(null)\nexport const GristWritesContext = createContext<UseGristTableOpsBoundResult | null>(null)\nexport const GristReadsContext = createContext<UseGristReadsResult | null>(null)\nexport const GristWidgetOptionsApiContext = createContext<UseGristWidgetOptionsApiResult | null>(null)\nexport const GristLinkingContext = createContext<UseGristLinkingResult | null>(null)\nexport const GristAttachmentsRestContext = createContext<UseGristAttachmentsRestResult | null>(null)\nexport const GristThemeContext = createContext<UseGristThemeResult | null>(null)\nexport const GristSectionApiContext = createContext<UseGristSectionApiResult | null>(null)\n\nfunction useRequiredContext<T>(ctx: Context<T | null>, name: string): T {\n const value = useContext(ctx)\n if (!value) {\n throw new Error(`${name} must be used inside <GristWidgetProvider>`)\n }\n return value\n}\n\nexport function useGristCoreContext(): GristCoreContextValue {\n return useRequiredContext(GristCoreContext, \"useGristCoreContext()\")\n}\n\nexport function useGristSelectionContext<\n TRow = Record<string, unknown>,\n TMapped = Record<string, unknown>,\n>(): UseGristSelectionResult<TRow, TMapped> {\n return useRequiredContext(GristSelectionContext, \"useGristSelectionContext()\") as UseGristSelectionResult<\n TRow,\n TMapped\n >\n}\n\nexport function useGristCurrentTableContext(): UseGristCurrentTableResult {\n return useRequiredContext(GristCurrentTableContext, \"useGristCurrentTableContext()\")\n}\n\nexport function useGristWritesContext(): UseGristTableOpsBoundResult {\n return useRequiredContext(GristWritesContext, \"useGristWritesContext()\")\n}\n\nexport function useGristReadsContext(): UseGristReadsResult {\n return useRequiredContext(GristReadsContext, \"useGristReadsContext()\")\n}\n\nexport function useGristWidgetOptionsApiContext(): UseGristWidgetOptionsApiResult {\n return useRequiredContext(GristWidgetOptionsApiContext, \"useGristWidgetOptionsApiContext()\")\n}\n\nexport function useGristLinkingContext(): UseGristLinkingResult {\n return useRequiredContext(GristLinkingContext, \"useGristLinkingContext()\")\n}\n\nexport function useGristAttachmentsRestContext(): UseGristAttachmentsRestResult {\n return useRequiredContext(GristAttachmentsRestContext, \"useGristAttachmentsRestContext()\")\n}\n\nexport function useGristThemeContext(): UseGristThemeResult {\n return useRequiredContext(GristThemeContext, \"useGristThemeContext()\")\n}\n\nexport function useGristSectionApiContext(): UseGristSectionApiResult {\n return useRequiredContext(GristSectionApiContext, \"useGristSectionApiContext()\")\n}\n\n/**\n * Live merge of all provider slices — used by `useGrist()` / `useGristContext()` so\n * cursor-driven `onRecord` updates are not lost behind a stale memoized snapshot.\n */\nexport function useGristFromProvider<\n TRow = Record<string, unknown>,\n TMapped = Record<string, unknown>,\n>(): UseGristResult<TRow, TMapped> {\n const core = useGristCoreContext()\n const selection = useGristSelectionContext<TRow, TMapped>()\n const currentTable = useGristCurrentTableContext()\n const writes = useGristWritesContext()\n const reads = useGristReadsContext()\n const widgetOptionsApi = useGristWidgetOptionsApiContext()\n const linking = useGristLinkingContext()\n const attachmentsRest = useGristAttachmentsRestContext()\n const theme = useGristThemeContext()\n const sectionApi = useGristSectionApiContext()\n\n return useMemo(\n () =>\n mergeGristResultFromSlices<TRow, TMapped>({\n core,\n selection,\n currentTable,\n writes,\n reads,\n widgetOptionsApi,\n linking,\n attachmentsRest,\n theme,\n sectionApi,\n } as ComposedGristWidgetSlices),\n [\n core,\n selection,\n currentTable,\n writes,\n reads,\n widgetOptionsApi,\n linking,\n attachmentsRest,\n theme,\n sectionApi,\n ]\n )\n}\n","import { useCallback, useEffect, useState } from \"react\"\n\nimport {\n fetchWithGristAuth as fetchWithGristAuthCore,\n getGristAccessToken,\n type GristAccessToken,\n} from \"../helpers/grist-fetch-with-auth\"\nimport { useGristReady } from \"../low-level-hooks/use-grist-ready\"\nimport type { GristReadyOptions } from \"../types\"\n\nexport type UseGristAccessTokenOptions = {\n /** Default for all token requests issued by this hook. */\n readOnly?: boolean\n /** Pre-fetch a token on mount so `token` is non-null when widgets render. */\n prefetch?: boolean\n /** Defaults to `\"full\"` since tokens are used for REST calls. */\n requiredAccess?: GristReadyOptions[\"requiredAccess\"]\n} & Pick<GristReadyOptions, \"columns\" | \"allowSelectBy\" | \"hasCustomOptions\" | \"onEditOptions\">\n\nexport type UseGristAccessTokenResult = {\n token: GristAccessToken | null\n loading: boolean\n error: string | null\n /** Request a fresh token (Grist tokens are short-lived; do not cache aggressively). */\n refresh: (overrides?: { readOnly?: boolean }) => Promise<GristAccessToken>\n /** `fetch()` against Grist's REST API with auth attached. */\n fetch: (path: string, init?: RequestInit & { readOnly?: boolean }) => Promise<Response>\n}\n\n/**\n * Exposes `docApi.getAccessToken` plus a ready-to-use `fetch` for calling\n * Grist's REST API (e.g. attachments, records, sql) from a widget.\n */\nexport function useGristAccessToken(options?: UseGristAccessTokenOptions): UseGristAccessTokenResult {\n const requiredAccess = options?.requiredAccess ?? \"full\"\n const readOnlyDefault = options?.readOnly ?? false\n const prefetch = options?.prefetch ?? false\n const { docApi, error: readyError } = useGristReady({\n requiredAccess,\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n\n const [token, setToken] = useState<GristAccessToken | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const refresh = useCallback(\n async (overrides?: { readOnly?: boolean }): Promise<GristAccessToken> => {\n if (!docApi) throw new Error(\"Grist is not ready\")\n setLoading(true)\n setError(null)\n try {\n const next = await getGristAccessToken(docApi, { readOnly: overrides?.readOnly ?? readOnlyDefault })\n setToken(next)\n return next\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n setError(message)\n throw err\n } finally {\n setLoading(false)\n }\n },\n [docApi, readOnlyDefault]\n )\n\n const fetchFn = useCallback(\n (path: string, init?: RequestInit & { readOnly?: boolean }) => {\n if (!docApi) throw new Error(\"Grist is not ready\")\n return fetchWithGristAuthCore(docApi, path, { readOnly: readOnlyDefault, ...init })\n },\n [docApi, readOnlyDefault]\n )\n\n useEffect(() => {\n if (docApi && prefetch) void refresh()\n }, [docApi, prefetch, refresh])\n\n return {\n token,\n loading,\n error: readyError ?? error,\n refresh,\n fetch: fetchFn,\n }\n}\n","import type { GristDocApi } from \"../types\"\n\nexport type GristAccessToken = {\n token: string\n baseUrl: string\n}\n\ntype TokenCacheEntry = {\n token: GristAccessToken\n fetchedAt: number\n}\n\nconst TOKEN_TTL_MS = 45_000\nconst cache = new Map<string, TokenCacheEntry>()\nconst inFlight = new Map<string, Promise<GristAccessToken>>()\n\nfunction cacheKey(readOnly: boolean): string {\n return readOnly ? \"ro\" : \"rw\"\n}\n\n/**\n * Returns an access token suitable for calling Grist's REST API directly.\n * Tokens are short-lived; results are cached briefly and coalesced per readOnly flag.\n */\nexport async function getGristAccessToken(\n docApi: GristDocApi,\n options?: { readOnly?: boolean }\n): Promise<GristAccessToken> {\n if (!docApi.getAccessToken) {\n throw new Error(\"getAccessToken is not available on docApi (need higher access level)\")\n }\n\n const readOnly = options?.readOnly ?? false\n const key = cacheKey(readOnly)\n const now = Date.now()\n const hit = cache.get(key)\n if (hit && now - hit.fetchedAt < TOKEN_TTL_MS) {\n return hit.token\n }\n\n const pending = inFlight.get(key)\n if (pending) return pending\n\n const next = docApi.getAccessToken({ readOnly }).then((token) => {\n cache.set(key, { token, fetchedAt: Date.now() })\n inFlight.delete(key)\n return token\n })\n\n inFlight.set(key, next)\n return next\n}\n\n/** Clear cached tokens (tests or after auth errors). */\nexport function clearGristAccessTokenCache(): void {\n cache.clear()\n inFlight.clear()\n}\n\n/**\n * `fetch()` against Grist's REST API with the doc access token attached.\n */\nexport async function fetchWithGristAuth(\n docApi: GristDocApi,\n path: string,\n init?: RequestInit & { readOnly?: boolean }\n): Promise<Response> {\n const readOnly = init?.readOnly ?? false\n let token: GristAccessToken\n try {\n token = await getGristAccessToken(docApi, { readOnly })\n } catch (err) {\n clearGristAccessTokenCache()\n throw err\n }\n\n const isAbsolute = /^https?:\\/\\//i.test(path)\n const cleanPath = isAbsolute ? path : path.startsWith(\"/\") ? path : `/${path}`\n const method = (init?.method ?? \"GET\").toUpperCase()\n\n const url = isAbsolute\n ? new URL(path)\n : new URL(`${token.baseUrl.replace(/\\/$/, \"\")}${cleanPath}`)\n\n // Grist signs widget REST calls with a short-lived `auth` query parameter.\n // Do not send `Authorization: Bearer` from the browser: many Grist deployments\n // omit it from Access-Control-Allow-Headers, which breaks POST (e.g. attachment upload).\n if (!url.searchParams.has(\"auth\")) url.searchParams.set(\"auth\", token.token)\n\n const headers = new Headers(init?.headers ?? {})\n headers.delete(\"Authorization\")\n\n const response = await fetch(url.toString(), { ...init, headers })\n if (response.status === 401) {\n clearGristAccessTokenCache()\n const retryToken = await getGristAccessToken(docApi, { readOnly })\n url.searchParams.set(\"auth\", retryToken.token)\n return fetch(url.toString(), { ...init, headers })\n }\n return response\n}\n","import { useCallback, useEffect, useState } from \"react\"\n\nimport { useGristReady } from \"../low-level-hooks/use-grist-ready\"\nimport type { GristReadyOptions, GristWidgetColumnMap } from \"../types\"\n\nexport type UseGristSectionResult = {\n /** Current widget ↔ section column mappings, refreshed on demand. */\n mappings: GristWidgetColumnMap | null\n loading: boolean\n error: string | null\n /** Re-read mappings from `sectionApi`. */\n refresh: () => Promise<void>\n /**\n * Update the widget's declared `ColumnsToMap` (and other ready options) after\n * mount. Use this to dynamically request more columns once the user has\n * configured the widget.\n */\n configure: (options: GristReadyOptions) => Promise<void>\n}\n\n/**\n * Wraps Grist's `sectionApi` (`mappings()` + `configure()`) so widgets can\n * inspect/refresh the column mapping and re-declare requirements without\n * touching `grist` directly.\n */\nexport function useGristSection(options?: GristReadyOptions): UseGristSectionResult {\n const { docApi, error: readyError } = useGristReady({\n requiredAccess: options?.requiredAccess ?? \"read table\",\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n\n const [mappings, setMappings] = useState<GristWidgetColumnMap | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const refresh = useCallback(async () => {\n if (!docApi) return\n if (!grist.sectionApi?.mappings) {\n setError(\"grist.sectionApi.mappings is not available in this Grist version\")\n return\n }\n setLoading(true)\n setError(null)\n try {\n const next = await grist.sectionApi.mappings()\n setMappings(next)\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n } finally {\n setLoading(false)\n }\n }, [docApi])\n\n const configure = useCallback(async (next: GristReadyOptions) => {\n if (!grist.sectionApi?.configure) {\n throw new Error(\"grist.sectionApi.configure is not available in this Grist version\")\n }\n await grist.sectionApi.configure(next)\n }, [])\n\n useEffect(() => {\n if (docApi) void refresh()\n }, [docApi, refresh])\n\n return {\n mappings,\n loading,\n error: readyError ?? error,\n refresh,\n configure,\n }\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\n\nimport { optionsEvent } from \"../internal/subscriptions\"\nimport { useGristReady } from \"../low-level-hooks/use-grist-ready\"\nimport type { GristReadyOptions } from \"../types\"\n\nexport type UseGristWidgetOptionsConfig<T> = {\n /** Default values returned before Grist responds and merged with stored options. */\n defaults: T\n /** Debounce window for `setOptions` writes (ms). Default: 250. */\n debounceMs?: number\n /**\n * Optional namespace inside `grist.setOptions`. When set, this hook reads/writes\n * `grist.setOption(namespace, value)` instead of the whole options blob — useful\n * to share the options bag with `hasCustomOptions` / unrelated widget state.\n */\n namespace?: string\n requiredAccess?: GristReadyOptions[\"requiredAccess\"]\n} & Pick<GristReadyOptions, \"columns\" | \"allowSelectBy\" | \"hasCustomOptions\" | \"onEditOptions\">\n\nexport type UseGristWidgetOptionsResult<T> = {\n options: T\n /** True until Grist has responded with the initial `onOptions` payload. */\n loading: boolean\n error: string | null\n /** Replace the options object (debounced write to Grist). */\n setOptions: (next: T) => void\n /** Patch a subset of keys (debounced write to Grist). */\n patchOptions: (patch: Partial<T>) => void\n /** Reset to `defaults` (immediate write to Grist). */\n reset: () => Promise<void>\n}\n\nfunction mergeWithDefaults<T>(defaults: T, stored: unknown, namespace?: string): T {\n if (stored === null || stored === undefined) return { ...defaults }\n const source = (namespace ? (stored as Record<string, unknown>)[namespace] : stored) as Partial<T> | undefined\n if (!source || typeof source !== \"object\") return { ...defaults }\n return { ...defaults, ...source }\n}\n\n/**\n * Persist a typed object of widget settings via `grist.onOptions` / `setOptions`.\n *\n * - Defaults are returned immediately (no flicker before Grist responds).\n * - Writes are debounced and batched.\n * - `hasCustomOptions` defaults to `true` so Grist exposes the gear icon — pass\n * `hasCustomOptions: false` to opt out.\n */\nexport function useGristWidgetOptions<T extends Record<string, unknown>>(\n config: UseGristWidgetOptionsConfig<T>\n): UseGristWidgetOptionsResult<T> {\n const requiredAccess = config.requiredAccess ?? \"read table\"\n const debounceMs = config.debounceMs ?? 250\n const hasCustomOptions = config.hasCustomOptions ?? true\n const { docApi, error: readyError } = useGristReady({\n requiredAccess,\n columns: config.columns,\n allowSelectBy: config.allowSelectBy,\n hasCustomOptions,\n onEditOptions: config.onEditOptions,\n })\n\n const defaultsRef = useRef(config.defaults)\n useEffect(() => {\n defaultsRef.current = config.defaults\n }, [config.defaults])\n\n const [options, setOptionsState] = useState<T>(() => ({ ...config.defaults }))\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n const lastWrittenRef = useRef<T | null>(null)\n const debounceRef = useRef<number | null>(null)\n\n // Subscribe to remote changes (initial + external edits)\n useEffect(() => {\n if (!docApi) return\n return optionsEvent.subscribe(({ options: stored }) => {\n const merged = mergeWithDefaults(defaultsRef.current, stored, config.namespace)\n lastWrittenRef.current = merged\n setOptionsState(merged)\n setLoading(false)\n })\n }, [docApi, config.namespace])\n\n const flushWrite = useCallback(\n async (value: T) => {\n try {\n if (config.namespace) {\n await grist.setOption(config.namespace, value)\n } else {\n await grist.setOptions({ ...value })\n }\n lastWrittenRef.current = value\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n }\n },\n [config.namespace]\n )\n\n const scheduleWrite = useCallback(\n (value: T) => {\n if (debounceRef.current) window.clearTimeout(debounceRef.current)\n debounceRef.current = window.setTimeout(() => {\n debounceRef.current = null\n void flushWrite(value)\n }, debounceMs)\n },\n [debounceMs, flushWrite]\n )\n\n useEffect(() => {\n return () => {\n if (debounceRef.current) window.clearTimeout(debounceRef.current)\n }\n }, [])\n\n const setOptionsApi = useCallback(\n (next: T) => {\n setOptionsState(next)\n scheduleWrite(next)\n },\n [scheduleWrite]\n )\n\n const patchOptionsApi = useCallback(\n (patch: Partial<T>) => {\n setOptionsState((prev) => {\n const next = { ...prev, ...patch }\n scheduleWrite(next)\n return next\n })\n },\n [scheduleWrite]\n )\n\n const reset = useCallback(async () => {\n if (debounceRef.current) {\n window.clearTimeout(debounceRef.current)\n debounceRef.current = null\n }\n const next = { ...defaultsRef.current }\n setOptionsState(next)\n await flushWrite(next)\n }, [flushWrite])\n\n return useMemo(\n () => ({\n options,\n loading,\n error: readyError ?? error,\n setOptions: setOptionsApi,\n patchOptions: patchOptionsApi,\n reset,\n }),\n [options, loading, readyError, error, setOptionsApi, patchOptionsApi, reset]\n )\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\n\nimport {\n useGristSelectionContext,\n useGristWidgetOptionsApiContext,\n} from \"../internal/contexts\"\n\nexport type UseGristWidgetOptionsFromContextConfig<T> = {\n /** Default values returned before Grist responds and merged with stored options. */\n defaults: T\n /** Debounce window for `setOptions` writes (ms). Default: 250. */\n debounceMs?: number\n /**\n * Optional namespace key. When set, this hook reads/writes\n * `options[namespace]` instead of the whole options blob.\n */\n namespace?: string\n}\n\nexport type UseGristWidgetOptionsFromContextResult<T> = {\n options: T\n /** True until Grist has responded with the initial `onOptions` payload. */\n loading: boolean\n /** Replace the options object (debounced write to Grist). */\n setOptions: (next: T) => void\n /** Patch a subset of keys (debounced write to Grist). */\n patchOptions: (patch: Partial<T>) => void\n /** Reset to `defaults` (immediate write to Grist). */\n reset: () => Promise<void>\n}\n\nfunction mergeWithDefaults<T>(defaults: T, stored: unknown, namespace?: string): T {\n if (stored === null || stored === undefined) return { ...defaults }\n const source = (namespace ? (stored as Record<string, unknown>)[namespace] : stored) as Partial<T> | undefined\n if (!source || typeof source !== \"object\") return { ...defaults }\n return { ...defaults, ...source }\n}\n\n/**\n * Typed widget options hook designed for use **inside a `<GristWidgetProvider>`**.\n *\n * Unlike `useGristWidgetOptions()` (which calls `useGristReady()` and conflicts\n * with the provider), this hook reads from the provider's existing context —\n * `widgetOptions` for reactive reads, and `patchWidgetOptions` / `setWidgetOptions`\n * for writes.\n *\n * ```tsx\n * function MyConfigPanel() {\n * const { options, patchOptions } = useGristWidgetOptionsFromContext({\n * defaults: { color: '#FF0000', showLabels: true },\n * })\n * return <input value={options.color} onChange={e => patchOptions({ color: e.target.value })} />\n * }\n * ```\n */\nexport function useGristWidgetOptionsFromContext<T extends Record<string, unknown>>(\n config: UseGristWidgetOptionsFromContextConfig<T>\n): UseGristWidgetOptionsFromContextResult<T> {\n const debounceMs = config.debounceMs ?? 250\n const selection = useGristSelectionContext()\n const optionsApi = useGristWidgetOptionsApiContext()\n\n const defaultsRef = useRef(config.defaults)\n useEffect(() => {\n defaultsRef.current = config.defaults\n }, [config.defaults])\n\n const rawOptions = selection.widgetOptions\n const hasReceived = rawOptions !== null\n\n const merged = useMemo(\n () => mergeWithDefaults(defaultsRef.current, rawOptions, config.namespace),\n [rawOptions, config.namespace]\n )\n\n const [localOverride, setLocalOverride] = useState<T | null>(null)\n const options = localOverride ?? merged\n\n useEffect(() => {\n setLocalOverride(null)\n }, [rawOptions])\n\n const debounceRef = useRef<number | null>(null)\n\n const flushWrite = useCallback(\n async (value: T) => {\n if (config.namespace) {\n await optionsApi.setWidgetOption(config.namespace, value)\n } else {\n await optionsApi.setWidgetOptions({ ...value })\n }\n },\n [config.namespace, optionsApi]\n )\n\n const scheduleWrite = useCallback(\n (value: T) => {\n if (debounceRef.current) window.clearTimeout(debounceRef.current)\n debounceRef.current = window.setTimeout(() => {\n debounceRef.current = null\n void flushWrite(value)\n }, debounceMs)\n },\n [debounceMs, flushWrite]\n )\n\n useEffect(() => {\n return () => {\n if (debounceRef.current) window.clearTimeout(debounceRef.current)\n }\n }, [])\n\n const setOptions = useCallback(\n (next: T) => {\n setLocalOverride(next)\n scheduleWrite(next)\n },\n [scheduleWrite]\n )\n\n const patchOptions = useCallback(\n (patch: Partial<T>) => {\n setLocalOverride((prev) => {\n const base = prev ?? merged\n const next = { ...base, ...patch }\n scheduleWrite(next)\n return next\n })\n },\n [merged, scheduleWrite]\n )\n\n const reset = useCallback(async () => {\n if (debounceRef.current) {\n window.clearTimeout(debounceRef.current)\n debounceRef.current = null\n }\n const next = { ...defaultsRef.current }\n setLocalOverride(null)\n await flushWrite(next)\n }, [flushWrite])\n\n return useMemo(\n () => ({\n options,\n loading: !hasReceived,\n setOptions,\n patchOptions,\n reset,\n }),\n [options, hasReceived, setOptions, patchOptions, reset]\n )\n}\n","import { useCallback, useEffect, useState } from \"react\"\n\nimport type { GristColumnarTable } from \"./helpers/grist-table-data\"\nimport { useGristReady } from \"./low-level-hooks/use-grist-ready\"\nimport type { GristFetchSelectedOptions, GristReadyOptions, GristRequiredAccess } from \"./types\"\n\nexport type UseGristTableOptions = {\n requiredAccess?: GristRequiredAccess\n /**\n * `'current'` (default): follow the widget’s selected table.\n * Any other string: fixed table id (no `grist.selectedTable` polling).\n */\n tableId?: \"current\" | string\n} & Pick<GristReadyOptions, \"columns\" | \"allowSelectBy\" | \"hasCustomOptions\" | \"onEditOptions\">\n\nexport type UseGristTableResult = {\n tableId: string | null\n loading: boolean\n error: string | null\n refresh: () => Promise<void>\n fetchTable: (targetTableId: string) => Promise<GristColumnarTable>\n listTables: () => Promise<string[]>\n getDocName: () => Promise<string>\n fetchSelectedTable: (fetchOptions?: GristFetchSelectedOptions) => Promise<GristColumnarTable>\n fetchSelectedRecord: (rowId: number, fetchOptions?: GristFetchSelectedOptions) => Promise<Record<string, unknown>>\n}\n\nfunction isCurrentMode(option: string | undefined): boolean {\n return option === undefined || option === \"current\"\n}\n\n/**\n * Selected or fixed table id plus document reads — no raw `docApi` on the result.\n */\nexport function useGristTable(options?: UseGristTableOptions): UseGristTableResult {\n const requiredAccess = options?.requiredAccess ?? \"read table\"\n const tableIdOption = options?.tableId ?? \"current\"\n const { docApi, error: readyError } = useGristReady({\n requiredAccess,\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n const [tableId, setTableId] = useState<string | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const fetchTable = useCallback(\n async (targetTableId: string) => {\n if (!docApi) throw new Error(\"Grist is not ready\")\n return docApi.fetchTable(targetTableId) as Promise<GristColumnarTable>\n },\n [docApi]\n )\n\n const listTables = useCallback(async () => {\n if (!docApi) throw new Error(\"Grist is not ready\")\n const out = await Promise.resolve(docApi.listTables())\n return [...out]\n }, [docApi])\n\n const getDocName = useCallback(async () => {\n if (!docApi) throw new Error(\"Grist is not ready\")\n return String(await Promise.resolve(docApi.getDocName()))\n }, [docApi])\n\n const fetchSelectedTable = useCallback(\n async (fetchOptions?: GristFetchSelectedOptions) => {\n if (!docApi) throw new Error(\"Grist is not ready\")\n const impl = docApi.fetchSelectedTable ?? grist.fetchSelectedTable\n if (!impl) throw new Error(\"fetchSelectedTable is not available in this Grist version\")\n const merged: GristFetchSelectedOptions = { keepEncoded: false, format: \"rows\", ...fetchOptions }\n return (await impl(merged)) as GristColumnarTable\n },\n [docApi]\n )\n\n const fetchSelectedRecord = useCallback(\n async (rowId: number, fetchOptions?: GristFetchSelectedOptions) => {\n if (!docApi) throw new Error(\"Grist is not ready\")\n const impl = docApi.fetchSelectedRecord ?? grist.fetchSelectedRecord\n if (!impl) throw new Error(\"fetchSelectedRecord is not available in this Grist version\")\n const merged: GristFetchSelectedOptions = { keepEncoded: false, format: \"rows\", ...fetchOptions }\n return (await impl(rowId, merged)) as Record<string, unknown>\n },\n [docApi]\n )\n\n const refresh = useCallback(async () => {\n if (!docApi) return\n if (!isCurrentMode(tableIdOption)) {\n setTableId(tableIdOption)\n setError(null)\n return\n }\n setLoading(true)\n setError(null)\n try {\n const next = await grist.selectedTable?.getTableId()\n setTableId(next ?? null)\n } catch (err) {\n setTableId(null)\n setError(err instanceof Error ? err.message : String(err))\n } finally {\n setLoading(false)\n }\n }, [docApi, tableIdOption])\n\n useEffect(() => {\n if (docApi) void refresh()\n }, [docApi, refresh])\n\n return {\n tableId,\n loading,\n error: readyError ?? error,\n refresh,\n fetchTable,\n listTables,\n getDocName,\n fetchSelectedTable,\n fetchSelectedRecord,\n }\n}\n"],"mappings":"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","names":["import_react","nextAuthz","ACCESS_RANK","grist","grist","inFlight","embedded","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react","inFlight","import_react","import_react","import_react","mergeWithDefaults","import_react"]}
|
|
1
|
+
{"version":3,"sources":["../src/sdk/advanced/index.ts","../src/sdk/internal/hooks/use-grist-core.ts","../src/sdk/internal/handshake/types.ts","../src/sdk/internal/handshake/actions.ts","../src/sdk/helpers/grist-mappings.ts","../src/sdk/internal/handshake/mapping-resolver.ts","../src/sdk/internal/handshake/access.ts","../src/sdk/internal/handshake/reducer.ts","../src/sdk/internal/handshake/derive.ts","../src/sdk/internal/handshake-manager-context.ts","../src/sdk/internal/ready-singleton.ts","../src/sdk/internal/build-manager-options.ts","../src/sdk/internal/handshake/effects/detect.ts","../src/sdk/internal/handshake/effects/negotiate.ts","../src/sdk/helpers/grist-widget-interaction.ts","../src/sdk/internal/subscriptions.ts","../src/sdk/internal/handshake/effects/subscriptions.ts","../src/sdk/internal/handshake/effects/mappings.ts","../src/sdk/internal/handshake/effects/heartbeat.ts","../src/sdk/internal/handshake/authz-from-options-cache.ts","../src/sdk/internal/handshake/environment.ts","../src/sdk/internal/handshake/manager.ts","../src/sdk/internal/embedded-handshake-manager.ts","../src/sdk/low-level-hooks/use-grist-availability.ts","../src/sdk/low-level-hooks/use-grist-ready.ts","../src/sdk/low-level-hooks/use-grist-handshake.ts","../src/sdk/low-level-hooks/use-ambient-grist-handshake.ts","../src/sdk/components/grist-handshake-provider.tsx","../src/sdk/low-level-hooks/use-grist-record.ts","../src/sdk/low-level-hooks/use-grist-records.ts","../src/sdk/low-level-hooks/use-grist-new-record.ts","../src/sdk/mid-level-hooks/use-grist-actions.ts","../src/sdk/helpers/grist-user-actions.ts","../src/sdk/internal/hooks/use-rpc-heartbeat-coalesce.ts","../src/sdk/mid-level-hooks/use-grist-mapped-record.ts","../src/sdk/mid-level-hooks/use-grist-table-ops.ts","../src/sdk/mid-level-hooks/use-grist-rows-from-table.ts","../src/sdk/helpers/grist-table-data.ts","../src/sdk/internal/contexts.tsx","../src/sdk/mid-level-hooks/use-grist-access-token.ts","../src/sdk/helpers/grist-fetch-with-auth.ts","../src/sdk/mid-level-hooks/use-grist-section.ts","../src/sdk/mid-level-hooks/use-grist-widget-options.ts","../src/sdk/mid-level-hooks/use-grist-widget-options-from-context.ts","../src/sdk/use-grist-table.ts"],"sourcesContent":["/**\n * Advanced hooks for power users / internal tooling.\n *\n * ⚠️ For day-to-day widget code, agents and humans should use the primary\n * surface from `@/lib/grist`:\n *\n * ```tsx\n * import { GristWidgetProvider, GristBoundary, useGrist, useGristSchema } from \"grist-widget-sdk\"\n * ```\n *\n * The hooks re-exported below are lower-level building blocks that overlap\n * with `useGrist()` — mixing them is a source of bugs (stacked `ready` calls,\n * divergent state). Only reach for them when you need a capability that the\n * primary hook does not expose.\n */\n\n// Low-level: availability / ready-handshake / raw subscriptions\nexport { useGristAvailability } from \"../low-level-hooks/use-grist-availability\"\nexport {\n useGristReady,\n type UseGristReadyOptions,\n type UseGristReadyResult,\n} from \"../low-level-hooks/use-grist-ready\"\n\n// Handshake state machine (SOTA) — opt-in foundation for widgets that want\n// fine-grained snapshot + capabilities + reload/restart control.\nexport {\n useGristHandshake,\n useGristCapabilities,\n type UseGristHandshakeOptions,\n type UseGristHandshakeResult,\n} from \"../low-level-hooks/use-grist-handshake\"\nexport {\n useAmbientGristHandshake,\n type UseAmbientGristHandshakeResult,\n} from \"../low-level-hooks/use-ambient-grist-handshake\"\nexport {\n GristHandshakeProvider,\n useGristHandshakeContext,\n useGristHandshakeContextOptional,\n type GristHandshakeContextValue,\n type GristHandshakeProviderProps,\n} from \"../components/grist-handshake-provider\"\nexport type {\n GristAuthz,\n GristCapabilities,\n GristConfig,\n GristCurrentTableState,\n GristGeneration,\n GristLifecycle,\n GristLifecyclePhase,\n GristLink,\n GristLinkState,\n GristMappingComplete,\n GristMappingIncomplete,\n GristMappingSource,\n GristMappingState,\n GristStreamFreshness,\n GristSync,\n GristTerminationReason,\n GristWidgetSnapshot,\n} from \"../internal/handshake\"\nexport {\n useGristRecord,\n type UseGristRecordSubscriptionResult,\n} from \"../low-level-hooks/use-grist-record\"\nexport { useGristRecords } from \"../low-level-hooks/use-grist-records\"\nexport {\n useGristNewRecord,\n type UseGristNewRecordResult,\n} from \"../low-level-hooks/use-grist-new-record\"\n\n// Mid-level: focused single-responsibility hooks\nexport {\n useGristActions,\n type GristActionsStatus,\n type UseGristActionsOptions,\n type UseGristActionsResult,\n} from \"../mid-level-hooks/use-grist-actions\"\nexport {\n useGristMappedRecord,\n type UseGristMappedRecordResult,\n} from \"../mid-level-hooks/use-grist-mapped-record\"\nexport {\n useGristTableOps,\n type GristTableOpsStatus,\n type UseGristTableOpsOptions,\n type UseGristTableOpsResult,\n} from \"../mid-level-hooks/use-grist-table-ops\"\nexport {\n useGristRowsFromTable,\n type UseGristRowsFromTableOptions,\n type UseGristRowsFromTableResult,\n} from \"../mid-level-hooks/use-grist-rows-from-table\"\nexport {\n useGristAccessToken,\n type UseGristAccessTokenOptions,\n type UseGristAccessTokenResult,\n} from \"../mid-level-hooks/use-grist-access-token\"\nexport {\n useGristSection,\n type UseGristSectionResult,\n} from \"../mid-level-hooks/use-grist-section\"\nexport {\n useGristWidgetOptions,\n type UseGristWidgetOptionsConfig,\n type UseGristWidgetOptionsResult,\n} from \"../mid-level-hooks/use-grist-widget-options\"\nexport {\n useGristWidgetOptionsFromContext,\n type UseGristWidgetOptionsFromContextConfig,\n type UseGristWidgetOptionsFromContextResult,\n} from \"../mid-level-hooks/use-grist-widget-options-from-context\"\n\nexport type {\n ListTablesOptions,\n ListColumnsOptions,\n GristColumnInfo,\n} from \"../internal/hooks/use-grist-reads\"\n\n// Focused \"just a table handle\" top-level hook (redundant with useGristWidget\n// for most cases). Prefer `useGrist().currentTableId` + `useGrist().fetchTable()`.\nexport {\n useGristTable,\n type UseGristTableOptions,\n type UseGristTableResult,\n} from \"../use-grist-table\"\n","import { useCallback, useMemo, useSyncExternalStore } from \"react\"\n\nimport type { GristDocApi, GristReadyOptions } from \"../../types\"\nimport {\n deriveCapabilities,\n deriveErrorMessage,\n deriveStatus,\n initialGristSnapshot,\n type GristCapabilities,\n type GristWidgetSnapshot,\n type GristWidgetStatus as HandshakeStatus,\n} from \"../handshake\"\nimport type { ManagerControls } from \"../handshake/manager\"\nimport { useGristManager } from \"../handshake-manager-context\"\n\n/** Canonical 4-state status — projected from the handshake snapshot. */\nexport type GristWidgetStatus = HandshakeStatus\n\nexport type UseGristCoreOptions = GristReadyOptions & {\n availabilityMaxAttempts?: number\n availabilityIntervalMs?: number\n logWhenNotEmbedded?: boolean\n}\n\nexport type UseGristCoreResult = {\n status: GristWidgetStatus\n isAvailable: boolean | null\n isReady: boolean\n error: string | null\n docApi: GristDocApi | null\n /** Derived from the handshake snapshot — same values as `useGristCapabilities()` on `/advanced`. */\n capabilities: GristCapabilities\n reload: () => Promise<void>\n}\n\n/** Project handshake availability for legacy `isAvailable` consumers. */\nexport function computeAvailabilityFromSnapshot(\n snapshot: GristWidgetSnapshot\n): boolean | null {\n const { phase } = snapshot.lifecycle\n if (phase === \"idle\" || phase === \"detecting\") return null\n if (phase === \"terminated\") {\n const reason = snapshot.lifecycle.reason.kind\n if (reason === \"not_embedded\" || reason === \"script_timeout\") return false\n return true\n }\n return true\n}\n\n/**\n * Core connectivity hook — always backed by {@link GristHandshakeManager}.\n */\nexport function useGristCore(options?: UseGristCoreOptions): UseGristCoreResult {\n const manager = useGristManager(options)\n return useGristCoreFromManager(manager)\n}\n\n/**\n * Subscribe to an existing manager and project its snapshot into\n * {@link UseGristCoreResult}.\n */\nexport function useGristCoreFromManager(manager: ManagerControls): UseGristCoreResult {\n const snapshot = useSyncExternalStore(\n manager.subscribe,\n manager.getSnapshot,\n initialGristSnapshot\n )\n const status = deriveStatus(snapshot)\n const error = deriveErrorMessage(snapshot)\n const docApi = useMemo<GristDocApi | null>(() => {\n if (snapshot.lifecycle.phase !== \"online\") return null\n if (typeof grist === \"undefined\") return null\n return grist.docApi ?? null\n }, [snapshot.lifecycle.phase, snapshot.generation])\n const isAvailable = computeAvailabilityFromSnapshot(snapshot)\n const reload = useCallback(async () => {\n manager.reload()\n }, [manager])\n const capabilities = useMemo(() => deriveCapabilities(snapshot), [snapshot])\n return useMemo(\n () => ({\n status,\n isAvailable,\n isReady: status === \"ready\",\n error,\n docApi,\n capabilities,\n reload,\n }),\n [status, isAvailable, error, docApi, capabilities, reload]\n )\n}\n","/**\n * Pure type contracts for the handshake / synchronization state machine.\n *\n * The machine models five orthogonal axes that together describe the widget's\n * relationship with the Grist host:\n *\n * 1. `LIFECYCLE` — protocol handshake (one-shot via `grist.ready`).\n * 2. `LINK` — RPC channel health (continuous, post-online).\n * 3. `AUTHZ` — effective access level granted by the host.\n * 4. `CONFIG` — column mapping contract with the end-user.\n * 5. `SYNC` — freshness of each event stream and ancillary RPCs.\n *\n * The legacy public `status` (\"booting\" / \"ready\" / \"unavailable\" / \"error\")\n * is a **pure projection** of the snapshot, computed in `./derive.ts`.\n *\n * No runtime side effects in this file — types only.\n */\n\nimport type { GristColumnsToMap, GristRequiredAccess, GristWidgetColumnMap } from \"../../types\"\n\n// ---------------------------------------------------------------------------\n// Branded primitives\n// ---------------------------------------------------------------------------\n\n/** Monotone counter bumped on reload / restart / link recovery / access widening. */\nexport type GristGeneration = number & { readonly __brand: \"GristGeneration\" }\n\nexport const ZERO_GENERATION = 0 as GristGeneration\n\nexport function nextGeneration(current: GristGeneration): GristGeneration {\n return (current + 1) as GristGeneration\n}\n\n// ---------------------------------------------------------------------------\n// 1. LIFECYCLE — handshake protocol\n// ---------------------------------------------------------------------------\n\nexport type GristLifecyclePhase =\n | \"idle\"\n | \"detecting\"\n | \"negotiating\"\n | \"online\"\n | \"terminated\"\n\nexport type GristTerminationReason =\n | { kind: \"script_timeout\"; attempts: number }\n | { kind: \"not_embedded\" }\n | { kind: \"ready_failed\"; message: string }\n | { kind: \"ready_timeout\"; afterMs: number }\n | { kind: \"host_rejected\"; message: string }\n | { kind: \"manual_terminate\" }\n\nexport type GristLifecycle =\n | { phase: \"idle\" }\n | { phase: \"detecting\"; attempts: number; startedAtMs: number }\n | { phase: \"negotiating\"; access: GristRequiredAccess; startedAtMs: number }\n | { phase: \"online\"; sinceMs: number; declaredAccess: GristRequiredAccess }\n | { phase: \"terminated\"; reason: GristTerminationReason; atMs: number }\n\n// ---------------------------------------------------------------------------\n// 2. LINK — RPC channel health\n// ---------------------------------------------------------------------------\n\nexport type GristLinkState = \"connected\" | \"stale\" | \"lost\"\n\nexport type GristLink = {\n state: GristLinkState\n /** ms timestamp of last successful RPC (or heartbeat). `null` before any. */\n lastSuccessMs: number | null\n /** Consecutive RPC failures since the last success. */\n missedPings: number\n}\n\n// ---------------------------------------------------------------------------\n// 3. AUTHZ — effective access\n// ---------------------------------------------------------------------------\n\nexport type GristAuthz =\n | { state: \"pending\"; declared: GristRequiredAccess }\n | {\n state: \"granted\"\n /** Effective level granted by the host. May differ from `declared`. */\n level: GristRequiredAccess\n declared: GristRequiredAccess\n /** True if granted level >= a previously granted level for the same widget. */\n widened: boolean\n }\n | { state: \"denied\"; declared: GristRequiredAccess; message?: string }\n\n// ---------------------------------------------------------------------------\n// 4. CONFIG — column-mapping contract\n// ---------------------------------------------------------------------------\n\n/**\n * Source that produced a mapping payload, used for deterministic merge by\n * {@link MappingResolver} (see `./mapping-resolver.ts`).\n */\nexport type GristMappingSource =\n | \"section_api\"\n | \"stream_record\"\n | \"stream_records\"\n | \"stream_new_record\"\n\n/**\n * Static priority order for mapping sources (lower index = higher priority).\n * `section_api` is the host's source-of-truth when available; stream payloads\n * are reactive fallbacks.\n */\nexport const MAPPING_SOURCE_PRIORITY: readonly GristMappingSource[] = [\n \"section_api\",\n \"stream_record\",\n \"stream_records\",\n \"stream_new_record\",\n] as const\n\nexport type GristMappingComplete = {\n state: \"complete\"\n declared: GristColumnsToMap\n mappings: GristWidgetColumnMap\n /** `\"merged\"` is used internally when multiple sources contributed. */\n source: GristMappingSource | \"merged\"\n reportedAtMs: number\n}\n\nexport type GristMappingIncomplete = {\n state: \"incomplete\"\n declared: GristColumnsToMap\n mappings: GristWidgetColumnMap\n missing: readonly string[]\n emptyMultiples: readonly string[]\n source: GristMappingSource | \"merged\"\n reportedAtMs: number\n}\n\nexport type GristMappingState =\n /** Widget did not declare any `columns` in `grist.ready`. */\n | { state: \"not_declared\" }\n /** Columns declared but no host report yet received. */\n | { state: \"pending\"; declared: GristColumnsToMap; declaredAtMs: number }\n | GristMappingIncomplete\n | GristMappingComplete\n /** User just modified the mapping; previous payload preserved to avoid UI flicker. */\n | {\n state: \"invalidating\"\n previous: GristMappingIncomplete | GristMappingComplete\n sinceMs: number\n }\n /** Declared but no host report after a timeout. Soft state — payload may still arrive. */\n | { state: \"unreported\"; declared: GristColumnsToMap; afterMs: number }\n\nexport type GristConfig = {\n mappings: GristMappingState\n}\n\n// ---------------------------------------------------------------------------\n// 5. SYNC — stream freshness\n// ---------------------------------------------------------------------------\n\nexport type GristStreamFreshness =\n | { state: \"cold\" }\n | { state: \"warming\"; sinceMs: number }\n | { state: \"hot\"; lastEventMs: number; generation: GristGeneration; payloadHash: string }\n | {\n state: \"stale\"\n lastEventMs: number\n reason: \"link_lost\" | \"tab_hidden\" | \"host_quiet\"\n }\n\nexport type GristCurrentTableState =\n | { state: \"unknown\" }\n | { state: \"known\"; tableId: string; refreshing: boolean }\n /** Local error (e.g. `selectedTable.getTableId()` rejected) — does NOT propagate to global status. */\n | { state: \"errored\"; message: string; sinceMs: number }\n\nexport type GristSync = {\n record: GristStreamFreshness\n records: GristStreamFreshness\n options: GristStreamFreshness\n newRecord: GristStreamFreshness\n currentTable: GristCurrentTableState\n}\n\n// ---------------------------------------------------------------------------\n// SNAPSHOT (aggregate)\n// ---------------------------------------------------------------------------\n\nexport type GristWidgetSnapshot = {\n generation: GristGeneration\n lifecycle: GristLifecycle\n link: GristLink\n authz: GristAuthz\n config: GristConfig\n sync: GristSync\n}\n\n// ---------------------------------------------------------------------------\n// PROJECTION — legacy 4-state status + capabilities\n// ---------------------------------------------------------------------------\n\n/** Mirrors the long-standing public type, kept for compat. */\nexport type GristWidgetStatus = \"booting\" | \"ready\" | \"unavailable\" | \"error\"\n\n/**\n * Pre-computed booleans that widget code should consume directly instead of\n * re-deriving from `status` + mapping flags + writes flags.\n *\n * Every field is a **pure function of the snapshot** and is referentially\n * stable across renders that don't change the snapshot's relevant slices.\n */\nexport type GristCapabilities = {\n /** `lifecycle.online` + `link != lost` + `authz.state === \"granted\"` with read or higher. */\n canRead: boolean\n /** `canRead` + (`mappings.state` is `complete` or `not_declared`). */\n canRender: boolean\n /** `canRender` + `authz.level === \"full\"`. Required before any write. */\n canWriteRecords: boolean\n /** `canWriteRecords` + `sync.currentTable.state === \"known\"`. */\n canWriteSchema: boolean\n /** `canRead` + `link === \"connected\"`. */\n canFetchTable: boolean\n /** `canRead` + `sync.record.state === \"hot\"` + mappings not invalidating. */\n hasFreshSelection: boolean\n /** `mappings.state === \"complete\" || \"not_declared\"`. */\n hasUsableMappings: boolean\n /** Logical column names still missing from user mapping (incomplete only). */\n missingMappings: readonly string[]\n /** `allowMultiple` columns mapped to `[]` (incomplete only). */\n emptyMultipleMappings: readonly string[]\n}\n\n// ---------------------------------------------------------------------------\n// Initial snapshot factory\n// ---------------------------------------------------------------------------\n\nexport function initialGristSnapshot(): GristWidgetSnapshot {\n return {\n generation: ZERO_GENERATION,\n lifecycle: { phase: \"idle\" },\n link: { state: \"connected\", lastSuccessMs: null, missedPings: 0 },\n authz: { state: \"pending\", declared: \"read table\" },\n config: { mappings: { state: \"not_declared\" } },\n sync: {\n record: { state: \"cold\" },\n records: { state: \"cold\" },\n options: { state: \"cold\" },\n newRecord: { state: \"cold\" },\n currentTable: { state: \"unknown\" },\n },\n }\n}\n","/**\n * Action union for the handshake state machine.\n *\n * Every action that can mutate the snapshot is listed here as a discriminated\n * union — exhaustiveness checks in the reducer rely on it.\n *\n * Actions either:\n * - target a single axis (e.g. `RPC_SUCCESS` → link), OR\n * - target multiple axes via a single transition (e.g. `NEGOTIATE_SUCCESS`\n * advances lifecycle AND authz at once).\n *\n * Generation-carrying actions can be **dropped** by the reducer if their\n * `generation` is older than the snapshot's current generation; this is the\n * primary defence against stale events arriving after a reload/restart.\n */\n\nimport type { GristColumnsToMap, GristRequiredAccess, GristWidgetColumnMap } from \"../../types\"\nimport type {\n GristGeneration,\n GristMappingSource,\n GristTerminationReason,\n} from \"./types\"\n\n// ---------------------------------------------------------------------------\n// Lifecycle actions\n// ---------------------------------------------------------------------------\n\nexport type GristLifecycleAction =\n | { type: \"DETECT_START\"; atMs: number }\n | { type: \"DETECT_TICK\"; attempts: number; atMs: number }\n | { type: \"DETECT_AVAILABLE\"; atMs: number }\n | { type: \"DETECT_TIMEOUT\"; attempts: number; atMs: number }\n | { type: \"NOT_EMBEDDED\"; atMs: number }\n | { type: \"NEGOTIATE_START\"; access: GristRequiredAccess; atMs: number }\n | {\n type: \"NEGOTIATE_SUCCESS\"\n access: GristRequiredAccess\n grantedLevel: GristRequiredAccess\n atMs: number\n }\n | { type: \"NEGOTIATE_FAIL\"; reason: GristTerminationReason; atMs: number }\n | { type: \"TERMINATE\"; reason: GristTerminationReason; atMs: number }\n\n// ---------------------------------------------------------------------------\n// Link actions\n// ---------------------------------------------------------------------------\n\nexport type GristLinkAction =\n | { type: \"RPC_SUCCESS\"; atMs: number }\n | { type: \"RPC_FAILURE\"; atMs: number; kind: \"timeout\" | \"rejected\" | \"other\" }\n | { type: \"HEARTBEAT_OK\"; atMs: number }\n | { type: \"HEARTBEAT_FAIL\"; atMs: number }\n\n// ---------------------------------------------------------------------------\n// Authz actions\n// ---------------------------------------------------------------------------\n\nexport type GristAuthzAction = {\n type: \"AUTHZ_REPORT\"\n /** Effective access level reported by Grist (`onOptions` / `interaction.access_level`). */\n level: GristRequiredAccess\n atMs: number\n generation: GristGeneration\n}\n\n// ---------------------------------------------------------------------------\n// Generation control\n// ---------------------------------------------------------------------------\n\nexport type GristGenerationAction = {\n type: \"BUMP_GENERATION\"\n atMs: number\n reason: \"reload\" | \"restart\" | \"link_recovery\" | \"widening\"\n}\n\n// ---------------------------------------------------------------------------\n// Config / mapping actions\n// ---------------------------------------------------------------------------\n\nexport type GristMappingAction =\n | {\n type: \"MAPPING_DECLARE\"\n declared: GristColumnsToMap\n atMs: number\n generation: GristGeneration\n }\n | {\n type: \"MAPPING_REPORT\"\n source: GristMappingSource\n payload: GristWidgetColumnMap | null\n atMs: number\n generation: GristGeneration\n }\n | { type: \"MAPPING_INVALIDATE\"; atMs: number; generation: GristGeneration }\n | { type: \"MAPPING_INVALIDATION_RESOLVE\"; atMs: number; generation: GristGeneration }\n | { type: \"MAPPING_TIMEOUT\"; atMs: number; generation: GristGeneration }\n\n// ---------------------------------------------------------------------------\n// Sync actions\n// ---------------------------------------------------------------------------\n\nexport type GristSyncStream = \"record\" | \"records\" | \"options\" | \"newRecord\"\n\nexport type GristSyncAction =\n | {\n type: \"STREAM_RECEIVED\"\n stream: GristSyncStream\n payloadHash: string\n atMs: number\n generation: GristGeneration\n }\n | { type: \"STREAM_WARMING\"; stream: GristSyncStream; atMs: number }\n | {\n type: \"STREAM_STALE\"\n stream: GristSyncStream\n atMs: number\n reason: \"link_lost\" | \"tab_hidden\" | \"host_quiet\"\n }\n | { type: \"CURRENT_TABLE_KNOWN\"; tableId: string; atMs: number }\n | { type: \"CURRENT_TABLE_REFRESH_START\"; atMs: number }\n | { type: \"CURRENT_TABLE_ERROR\"; message: string; atMs: number }\n\n// ---------------------------------------------------------------------------\n// Top-level union\n// ---------------------------------------------------------------------------\n\nexport type GristAction =\n | GristLifecycleAction\n | GristLinkAction\n | GristAuthzAction\n | GristGenerationAction\n | GristMappingAction\n | GristSyncAction\n\n/**\n * Type guard: actions that carry a `generation` field and can therefore be\n * dropped by the reducer if `action.generation < snapshot.generation`.\n */\nexport function hasGenerationStamp(\n action: GristAction\n): action is Extract<GristAction, { generation: GristGeneration }> {\n return \"generation\" in action && typeof action.generation === \"number\"\n}\n","import type { GristColumnsToMap, GristColumnToMap, GristWidgetColumnMap } from \"../types\"\n\nexport type ValidateColumnMappingsResult = {\n ok: boolean\n /**\n * `true` while the host has not yet reported column mappings (e.g. between\n * `grist.ready()` resolving and the first `onRecord` / `onRecords` callback,\n * or before `sectionApi.mappings()` returns). Callers should not gate UI or\n * emit mapping alerts while pending.\n */\n pending?: boolean\n /** Logical names of required columns the user has not yet mapped. */\n missing: string[]\n /** Logical names of `allowMultiple` columns mapped to an empty list. */\n emptyMultiples: string[]\n}\n\n/**\n * Merges mapping payloads from `onRecord`, `onRecords`, and `sectionApi.mappings()`.\n * Later sources win; `undefined` sources are skipped; `null` means \"host reported\n * no mappings\" but still counts as a known (empty) payload.\n */\nexport function mergeWidgetColumnMaps(\n ...sources: (GristWidgetColumnMap | null | undefined)[]\n): GristWidgetColumnMap | null | undefined {\n let merged: GristWidgetColumnMap = {}\n let sawDefined = false\n for (const src of sources) {\n if (src === undefined) continue\n sawDefined = true\n if (src === null) continue\n merged = { ...merged, ...src }\n }\n if (!sawDefined) return undefined\n return merged\n}\n\nfunction asColumnSpec(entry: string | GristColumnToMap): GristColumnToMap {\n return typeof entry === \"string\" ? { name: entry } : entry\n}\n\n/**\n * Verifies that the current Grist mappings cover every required column declared\n * in `grist.ready({ columns })`.\n *\n * Use this before reading `mappedRecord` so you can render a \"please configure\n * the widget\" prompt instead of a confusing `null`.\n */\nexport function validateColumnMappings(\n spec: GristColumnsToMap | undefined | null,\n mappings: GristWidgetColumnMap | null | undefined\n): ValidateColumnMappingsResult {\n const missing: string[] = []\n const emptyMultiples: string[] = []\n if (!spec) return { ok: true, pending: false, missing, emptyMultiples }\n\n const m = mappings ?? {}\n for (const raw of spec) {\n const col = asColumnSpec(raw)\n if (col.optional) continue\n const value = m[col.name]\n if (col.allowMultiple) {\n if (value === undefined) {\n missing.push(col.name)\n } else if (!Array.isArray(value) || value.length === 0) {\n emptyMultiples.push(col.name)\n }\n } else {\n if (value === undefined || value === null || value === \"\") missing.push(col.name)\n }\n }\n return {\n ok: missing.length === 0 && emptyMultiples.length === 0,\n pending: false,\n missing,\n emptyMultiples,\n }\n}\n\n/** Status while waiting for the host to report mappings (see `pending`). */\nexport const pendingColumnMappingStatus: ValidateColumnMappingsResult = {\n ok: true,\n pending: true,\n missing: [],\n emptyMultiples: [],\n}\n","/**\n * Deterministic merge of column-mapping payloads across multiple sources.\n *\n * Why this exists\n * ---------------\n * Grist reports column mappings from several places, possibly in different\n * orders depending on network timing and event scheduling:\n *\n * - `grist.sectionApi.mappings()` (one-shot promise),\n * - `grist.onRecord` (stream),\n * - `grist.onRecords` (stream, occasionally diverges from `onRecord`),\n * - `grist.onNewRecord` (stream, only on the \"new\" placeholder row).\n *\n * A \"later wins\" merge would let arrival order dictate the final mappings —\n * unstable and surprising. This resolver applies a fixed priority order\n * (see {@link MAPPING_SOURCE_PRIORITY}) so the resolution is a **pure function\n * of the set of received payloads**, independent of the order in which they\n * arrived.\n *\n * Generation safety\n * -----------------\n * Each payload is ingested with the {@link GristGeneration} that was active\n * when the source produced it. Payloads with `generation` strictly less than\n * the resolver's current generation are silently dropped — this is how the\n * machine recovers from reloads / restarts without ever applying a stale\n * mapping over a fresh one.\n */\n\nimport { validateColumnMappings } from \"../../helpers/grist-mappings\"\nimport type { GristColumnsToMap, GristWidgetColumnMap } from \"../../types\"\nimport {\n MAPPING_SOURCE_PRIORITY,\n type GristGeneration,\n type GristMappingComplete,\n type GristMappingIncomplete,\n type GristMappingSource,\n type GristMappingState,\n} from \"./types\"\n\nexport type MappingPayload = {\n source: GristMappingSource\n /** `null` is a known empty payload (host explicitly reported no mappings). */\n payload: GristWidgetColumnMap | null\n receivedAtMs: number\n generation: GristGeneration\n}\n\nexport type ResolveOutcome =\n | {\n kind: \"no_change\"\n reason: \"stale_generation\" | \"duplicate_payload\"\n }\n | {\n kind: \"resolved\"\n state: GristMappingComplete | GristMappingIncomplete\n }\n | { kind: \"still_pending\" }\n\n/**\n * Stateful resolver. One instance per declaration of `columns` (i.e. per\n * widget lifetime, modulo `reset()` on declare-change or restart).\n *\n * The resolver does NOT own the higher-level {@link GristMappingState} —\n * it returns concrete `complete` / `incomplete` payloads that the reducer\n * folds into the snapshot.\n */\nexport class MappingResolver {\n private declared: GristColumnsToMap\n private currentGeneration: GristGeneration\n /** Latest accepted payload per source. */\n private payloads = new Map<GristMappingSource, MappingPayload>()\n /** Last resolved state, used to detect duplicate ingests. */\n private lastResolvedHash: string | null = null\n\n constructor(declared: GristColumnsToMap, generation: GristGeneration) {\n this.declared = declared\n this.currentGeneration = generation\n }\n\n /**\n * Replace the declared columns (e.g. user passed a new `columns` array to\n * the provider). Clears accumulated payloads — they were validated against\n * the previous declaration.\n */\n redeclare(declared: GristColumnsToMap, atGeneration: GristGeneration): void {\n this.declared = declared\n this.currentGeneration = atGeneration\n this.payloads.clear()\n this.lastResolvedHash = null\n }\n\n /** Bump the active generation. Payloads from older generations are dropped. */\n bumpGeneration(next: GristGeneration): void {\n if (next <= this.currentGeneration) return\n this.currentGeneration = next\n // Drop any cached payloads from older generations.\n for (const [src, p] of this.payloads) {\n if (p.generation < next) this.payloads.delete(src)\n }\n this.lastResolvedHash = null\n }\n\n /** Forget every payload and reset the hash. The declaration is preserved. */\n reset(): void {\n this.payloads.clear()\n this.lastResolvedHash = null\n }\n\n /** Cheap, deterministic snapshot for tests/debug. */\n knownSources(): readonly GristMappingSource[] {\n return [...this.payloads.keys()]\n }\n\n /**\n * Ingest one payload. Returns a {@link ResolveOutcome}:\n * - `still_pending` — no payloads accepted yet.\n * - `no_change/stale_*` — input generation is older than current.\n * - `no_change/duplicate_payload` — input did not move the resolution.\n * - `resolved` — new resolution differs from the previous one.\n *\n * Call {@link current} to read the latest resolution without affecting\n * the duplicate-detection hash.\n */\n ingest(input: MappingPayload): ResolveOutcome {\n if (input.generation < this.currentGeneration) {\n return { kind: \"no_change\", reason: \"stale_generation\" }\n }\n if (input.generation > this.currentGeneration) {\n this.bumpGeneration(input.generation)\n }\n this.payloads.set(input.source, input)\n\n const computed = this.compute()\n if (computed === null) return { kind: \"still_pending\" }\n\n const hash = computeMappingHash(\n computed.mappings,\n computed.state === \"complete\",\n computed.source\n )\n if (hash === this.lastResolvedHash) {\n return { kind: \"no_change\", reason: \"duplicate_payload\" }\n }\n this.lastResolvedHash = hash\n return { kind: \"resolved\", state: computed }\n }\n\n /**\n * Return the resolver's current resolution (pure read). Equivalent to\n * `ingest(...)` without mutating any state — used by tests and by the\n * reducer when re-applying after `MAPPING_INVALIDATION_RESOLVE`.\n */\n current(): ResolveOutcome {\n const computed = this.compute()\n if (computed === null) return { kind: \"still_pending\" }\n return { kind: \"resolved\", state: computed }\n }\n\n /**\n * Compute the current best mapping from accumulated payloads (pure).\n *\n * Priority: higher-priority sources **override** specific keys from\n * lower-priority sources. Keys absent from a higher-priority source fall\n * back to lower-priority sources. This matches the production behaviour\n * where `sectionApi.mappings()` is authoritative for the keys it reports,\n * but stream payloads can fill in keys the section_api hasn't reported yet.\n */\n private compute(): GristMappingComplete | GristMappingIncomplete | null {\n if (this.payloads.size === 0) return null\n\n const merged: GristWidgetColumnMap = {}\n let latestReportedAt = 0\n const contributors: GristMappingSource[] = []\n\n for (const src of [...MAPPING_SOURCE_PRIORITY].reverse()) {\n const p = this.payloads.get(src)\n if (!p) continue\n contributors.push(src)\n if (p.receivedAtMs > latestReportedAt) latestReportedAt = p.receivedAtMs\n if (p.payload === null) continue\n for (const [k, v] of Object.entries(p.payload)) {\n merged[k] = v\n }\n }\n\n if (contributors.length === 0) return null\n\n const validation = validateColumnMappings(this.declared, merged)\n const source: GristMappingSource | \"merged\" =\n contributors.length === 1 ? (contributors[0] as GristMappingSource) : \"merged\"\n\n if (validation.ok) {\n return {\n state: \"complete\",\n declared: this.declared,\n mappings: merged,\n source,\n reportedAtMs: latestReportedAt,\n }\n }\n\n return {\n state: \"incomplete\",\n declared: this.declared,\n mappings: merged,\n missing: validation.missing,\n emptyMultiples: validation.emptyMultiples,\n source,\n reportedAtMs: latestReportedAt,\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Stable hash for a {complete|incomplete} mapping payload. Deterministic\n * across re-resolutions of identical inputs so the resolver can de-duplicate.\n */\nexport function computeMappingHash(\n mappings: GristWidgetColumnMap,\n ok: boolean,\n source: GristMappingSource | \"merged\"\n): string {\n const keys = Object.keys(mappings).sort()\n const body = keys.map((k) => {\n const v = mappings[k]\n if (Array.isArray(v)) return `${k}=[${v.join(\",\")}]`\n return `${k}=${v ?? \"\"}`\n }).join(\"|\")\n return `${ok ? \"OK\" : \"KO\"}::${source}::${body}`\n}\n\n/**\n * Decide whether to treat a newly arrived payload as a graceful invalidation\n * of the previous mapping (user just edited the section's mapping settings).\n *\n * Returns `true` if `previous` was `complete`/`incomplete` and `next` differs\n * by at least one key. Pure function, no side effects.\n */\nexport function shouldInvalidate(\n previous: GristMappingState,\n nextMappings: GristWidgetColumnMap\n): boolean {\n if (previous.state !== \"complete\" && previous.state !== \"incomplete\") return false\n const a = previous.mappings\n const aKeys = Object.keys(a)\n const bKeys = Object.keys(nextMappings)\n if (aKeys.length !== bKeys.length) return true\n for (const k of aKeys) {\n const av = a[k]\n const bv = nextMappings[k]\n if (Array.isArray(av) && Array.isArray(bv)) {\n if (av.length !== bv.length) return true\n for (let i = 0; i < av.length; i++) if (av[i] !== bv[i]) return true\n } else if (av !== bv) {\n return true\n }\n }\n return false\n}\n","/**\n * Access-level helpers used by the handshake state machine.\n *\n * Kept in a tiny dedicated file because the same ranking is needed by the\n * reducer (`widened` flag, future re-negotiation logic) and potentially by\n * effects (decide whether to re-issue `grist.ready`).\n */\n\nimport type { GristRequiredAccess } from \"../../types\"\n\n/** Strictly-ordered ranking. Higher rank = stronger access. */\nexport const ACCESS_RANK: Record<GristRequiredAccess, number> = {\n none: 0,\n \"read table\": 1,\n full: 2,\n}\n\nconst GRIST_ACCESS_LEVELS: readonly GristRequiredAccess[] = [\"none\", \"read table\", \"full\"]\n\n/** Parse `interaction.access_level` / `accessLevel` from Grist `onOptions`. */\nexport function parseGristRequiredAccess(\n raw: string | undefined\n): GristRequiredAccess | null {\n if (!raw) return null\n return (GRIST_ACCESS_LEVELS as readonly string[]).includes(raw)\n ? (raw as GristRequiredAccess)\n : null\n}\n\n/** True when the host-granted level meets what the widget declared in `grist.ready`. */\nexport function isAccessSufficient(\n granted: GristRequiredAccess,\n required: GristRequiredAccess\n): boolean {\n return ACCESS_RANK[granted] >= ACCESS_RANK[required]\n}\n\n/** Creator-panel label for a Grist access level (for user-facing copy). */\nexport function gristAccessLevelLabel(level: GristRequiredAccess): string {\n switch (level) {\n case \"none\":\n return \"No document access\"\n case \"read table\":\n return \"Read selected table\"\n case \"full\":\n return \"Full document access\"\n }\n}\n\nexport function formatInsufficientAccessMessage(\n required: GristRequiredAccess,\n _granted: GristRequiredAccess\n): string {\n const need = gristAccessLevelLabel(required)\n return `Set Access level to at least \"${need}\" in the Custom widget settings.`\n}\n\nexport function isAccessWidening(\n previous: GristRequiredAccess | null,\n next: GristRequiredAccess\n): boolean {\n if (previous === null) return true\n return ACCESS_RANK[next] > ACCESS_RANK[previous]\n}\n","/**\n * Pure reducer for the handshake state machine.\n *\n * Contract\n * --------\n * - **Pure**: `(snapshot, action, ctx) => nextSnapshot`. No I/O, no globals,\n * no Date.now() — callers pass timestamps via `action.atMs` and (optionally)\n * a context object for the {@link MappingResolver}.\n * - **Total**: every (snapshot.lifecycle.phase × action.type) pair returns\n * either an updated snapshot or the identity snapshot.\n * - **Idempotent for replays**: same generation-stamped action applied twice\n * leaves the snapshot unchanged (`payloadHash` short-circuit on streams,\n * resolver hash short-circuit on mappings).\n * - **Generation-safe**: any action carrying a `generation` lower than the\n * current snapshot generation is dropped silently — this is what makes\n * the SDK robust to late events after reload/restart.\n *\n * The reducer never throws. Invariant violations are surfaced via\n * `./invariants.ts` if the caller opts in.\n */\n\nimport {\n hasGenerationStamp,\n type GristAction,\n type GristMappingAction,\n type GristSyncAction,\n type GristSyncStream,\n} from \"./actions\"\nimport { MappingResolver } from \"./mapping-resolver\"\nimport {\n ZERO_GENERATION,\n nextGeneration,\n type GristGeneration,\n type GristLifecycle,\n type GristLink,\n type GristMappingComplete,\n type GristMappingIncomplete,\n type GristMappingState,\n type GristStreamFreshness,\n type GristSync,\n type GristTerminationReason,\n type GristWidgetSnapshot,\n} from \"./types\"\nimport {\n ACCESS_RANK,\n formatInsufficientAccessMessage,\n isAccessSufficient,\n} from \"./access\"\nimport type { GristRequiredAccess } from \"../../types\"\n\n/**\n * Per-machine mutable companion to the snapshot. The reducer reads from it\n * (and lets the resolver mutate it in-place) so the snapshot itself can stay\n * a deeply-frozen, immutable value.\n */\nexport type GristReducerContext = {\n resolver: MappingResolver | null\n}\n\nexport function createReducerContext(): GristReducerContext {\n return { resolver: null }\n}\n\n/**\n * Tuning knobs. Pass `Partial<ReducerConfig>` to override defaults; values\n * are read by the reducer at each step.\n */\nexport type ReducerConfig = {\n /** Stale-pings threshold before link transitions to `stale`. */\n staleAfterMissed: number\n /** Stale-pings threshold before link transitions to `lost`. */\n lostAfterMissed: number\n}\n\nexport const DEFAULT_REDUCER_CONFIG: ReducerConfig = {\n staleAfterMissed: 1,\n lostAfterMissed: 3,\n}\n\n// ---------------------------------------------------------------------------\n// Reducer entry point\n// ---------------------------------------------------------------------------\n\nexport function gristReducer(\n snapshot: GristWidgetSnapshot,\n action: GristAction,\n ctx: GristReducerContext,\n config: ReducerConfig = DEFAULT_REDUCER_CONFIG\n): GristWidgetSnapshot {\n // Generation gate — applied to every action that carries a generation.\n if (hasGenerationStamp(action) && action.generation < snapshot.generation) {\n return snapshot\n }\n\n switch (action.type) {\n // -- Lifecycle --------------------------------------------------------\n case \"DETECT_START\":\n return reduceDetectStart(snapshot, action.atMs)\n case \"DETECT_TICK\":\n return reduceDetectTick(snapshot, action.attempts)\n case \"DETECT_AVAILABLE\":\n return reduceDetectAvailable(snapshot, action.atMs)\n case \"DETECT_TIMEOUT\":\n return reduceTerminate(snapshot, {\n kind: \"script_timeout\",\n attempts: action.attempts,\n }, action.atMs)\n case \"NOT_EMBEDDED\":\n return reduceTerminate(snapshot, { kind: \"not_embedded\" }, action.atMs)\n case \"NEGOTIATE_START\":\n return reduceNegotiateStart(snapshot, action.access, action.atMs)\n case \"NEGOTIATE_SUCCESS\":\n return reduceNegotiateSuccess(snapshot, action.access, action.grantedLevel, action.atMs)\n case \"NEGOTIATE_FAIL\":\n return reduceTerminate(snapshot, action.reason, action.atMs)\n case \"TERMINATE\":\n return reduceTerminate(snapshot, action.reason, action.atMs)\n case \"AUTHZ_REPORT\":\n return reduceAuthzReport(snapshot, action.level, action.atMs)\n\n // -- Link -------------------------------------------------------------\n case \"RPC_SUCCESS\":\n case \"HEARTBEAT_OK\":\n return reduceLinkSuccess(snapshot, action.atMs)\n case \"RPC_FAILURE\":\n case \"HEARTBEAT_FAIL\":\n return reduceLinkFailure(snapshot, action.atMs, config)\n\n // -- Generation -------------------------------------------------------\n case \"BUMP_GENERATION\":\n return reduceBumpGeneration(snapshot, ctx)\n\n // -- Config / mappings -----------------------------------------------\n case \"MAPPING_DECLARE\":\n case \"MAPPING_REPORT\":\n case \"MAPPING_INVALIDATE\":\n case \"MAPPING_INVALIDATION_RESOLVE\":\n case \"MAPPING_TIMEOUT\":\n return reduceMapping(snapshot, action, ctx)\n\n // -- Sync -------------------------------------------------------------\n case \"STREAM_RECEIVED\":\n case \"STREAM_WARMING\":\n case \"STREAM_STALE\":\n return reduceStream(snapshot, action)\n case \"CURRENT_TABLE_KNOWN\":\n return updateSync(snapshot, {\n ...snapshot.sync,\n currentTable: { state: \"known\", tableId: action.tableId, refreshing: false },\n })\n case \"CURRENT_TABLE_REFRESH_START\":\n if (snapshot.sync.currentTable.state !== \"known\") return snapshot\n return updateSync(snapshot, {\n ...snapshot.sync,\n currentTable: { ...snapshot.sync.currentTable, refreshing: true },\n })\n case \"CURRENT_TABLE_ERROR\":\n return updateSync(snapshot, {\n ...snapshot.sync,\n currentTable: { state: \"errored\", message: action.message, sinceMs: action.atMs },\n })\n\n default: {\n // Exhaustiveness check — TypeScript guarantees this is `never`.\n const _exhaustive: never = action\n void _exhaustive\n return snapshot\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Lifecycle transitions\n// ---------------------------------------------------------------------------\n\nfunction reduceDetectStart(snapshot: GristWidgetSnapshot, atMs: number): GristWidgetSnapshot {\n if (snapshot.lifecycle.phase !== \"idle\") return snapshot\n return {\n ...snapshot,\n lifecycle: { phase: \"detecting\", attempts: 0, startedAtMs: atMs },\n }\n}\n\nfunction reduceDetectTick(snapshot: GristWidgetSnapshot, attempts: number): GristWidgetSnapshot {\n if (snapshot.lifecycle.phase !== \"detecting\") return snapshot\n if (attempts === snapshot.lifecycle.attempts) return snapshot\n return {\n ...snapshot,\n lifecycle: { ...snapshot.lifecycle, attempts },\n }\n}\n\nfunction reduceDetectAvailable(\n snapshot: GristWidgetSnapshot,\n atMs: number\n): GristWidgetSnapshot {\n if (snapshot.lifecycle.phase !== \"detecting\" && snapshot.lifecycle.phase !== \"idle\") {\n return snapshot\n }\n return {\n ...snapshot,\n lifecycle: {\n phase: \"negotiating\",\n access: snapshot.authz.declared,\n startedAtMs: atMs,\n },\n }\n}\n\nfunction reduceNegotiateStart(\n snapshot: GristWidgetSnapshot,\n access: GristWidgetSnapshot[\"authz\"][\"declared\"],\n atMs: number\n): GristWidgetSnapshot {\n // Allowed from `detecting` (first negotiate), `negotiating` (re-arm),\n // or `online` (access widening triggers re-negotiation in place).\n if (snapshot.lifecycle.phase === \"terminated\") return snapshot\n return {\n ...snapshot,\n authz: { state: \"pending\", declared: access },\n lifecycle:\n snapshot.lifecycle.phase === \"online\"\n ? snapshot.lifecycle\n : { phase: \"negotiating\", access, startedAtMs: atMs },\n }\n}\n\nfunction authzDeclared(snapshot: GristWidgetSnapshot): GristRequiredAccess {\n return snapshot.authz.declared\n}\n\nfunction reduceAuthzReport(\n snapshot: GristWidgetSnapshot,\n level: GristRequiredAccess,\n _atMs: number\n): GristWidgetSnapshot {\n if (snapshot.lifecycle.phase !== \"online\") return snapshot\n\n const declared = authzDeclared(snapshot)\n\n if (isAccessSufficient(level, declared)) {\n const previousLevel =\n snapshot.authz.state === \"granted\" ? snapshot.authz.level : null\n const widened =\n previousLevel !== null && ACCESS_RANK[level] > ACCESS_RANK[previousLevel]\n const nextAuthz = {\n state: \"granted\" as const,\n level,\n declared,\n widened,\n }\n if (\n snapshot.authz.state === \"granted\" &&\n snapshot.authz.level === nextAuthz.level &&\n snapshot.authz.declared === nextAuthz.declared &&\n snapshot.authz.widened === nextAuthz.widened\n ) {\n return snapshot\n }\n return { ...snapshot, authz: nextAuthz }\n }\n\n const message = formatInsufficientAccessMessage(declared, level)\n const nextAuthz = { state: \"denied\" as const, declared, message }\n if (\n snapshot.authz.state === \"denied\" &&\n snapshot.authz.declared === nextAuthz.declared &&\n snapshot.authz.message === nextAuthz.message\n ) {\n return snapshot\n }\n return { ...snapshot, authz: nextAuthz }\n}\n\nfunction reduceNegotiateSuccess(\n snapshot: GristWidgetSnapshot,\n access: GristWidgetSnapshot[\"authz\"][\"declared\"],\n grantedLevel: GristWidgetSnapshot[\"authz\"][\"declared\"],\n atMs: number\n): GristWidgetSnapshot {\n if (snapshot.lifecycle.phase === \"terminated\") return snapshot\n\n const previousLevel =\n snapshot.authz.state === \"granted\" ? snapshot.authz.level : null\n const widened =\n previousLevel !== null && ACCESS_RANK[grantedLevel] > ACCESS_RANK[previousLevel]\n\n return {\n ...snapshot,\n authz: {\n state: \"granted\",\n level: grantedLevel,\n declared: access,\n widened,\n },\n lifecycle:\n snapshot.lifecycle.phase === \"online\"\n ? snapshot.lifecycle\n : { phase: \"online\", sinceMs: atMs, declaredAccess: access },\n link: { state: \"connected\", lastSuccessMs: atMs, missedPings: 0 },\n }\n}\n\nfunction reduceTerminate(\n snapshot: GristWidgetSnapshot,\n reason: GristTerminationReason,\n atMs: number\n): GristWidgetSnapshot {\n if (snapshot.lifecycle.phase === \"terminated\") return snapshot\n const lifecycle: GristLifecycle = { phase: \"terminated\", reason, atMs }\n return { ...snapshot, lifecycle }\n}\n\n// ---------------------------------------------------------------------------\n// Link transitions\n// ---------------------------------------------------------------------------\n\nfunction reduceLinkSuccess(snapshot: GristWidgetSnapshot, atMs: number): GristWidgetSnapshot {\n if (snapshot.lifecycle.phase !== \"online\") return snapshot\n const link: GristLink = { state: \"connected\", lastSuccessMs: atMs, missedPings: 0 }\n if (\n snapshot.link.state === link.state &&\n snapshot.link.lastSuccessMs === link.lastSuccessMs &&\n snapshot.link.missedPings === link.missedPings\n ) {\n return snapshot\n }\n return { ...snapshot, link }\n}\n\nfunction reduceLinkFailure(\n snapshot: GristWidgetSnapshot,\n _atMs: number,\n config: ReducerConfig\n): GristWidgetSnapshot {\n if (snapshot.lifecycle.phase !== \"online\") return snapshot\n const missed = snapshot.link.missedPings + 1\n let state: GristLink[\"state\"] = snapshot.link.state\n if (missed >= config.lostAfterMissed) state = \"lost\"\n else if (missed >= config.staleAfterMissed) state = \"stale\"\n return {\n ...snapshot,\n link: { state, lastSuccessMs: snapshot.link.lastSuccessMs, missedPings: missed },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Generation\n// ---------------------------------------------------------------------------\n\nfunction reduceBumpGeneration(\n snapshot: GristWidgetSnapshot,\n ctx: GristReducerContext\n): GristWidgetSnapshot {\n const next = nextGeneration(snapshot.generation)\n ctx.resolver?.bumpGeneration(next)\n return {\n ...snapshot,\n generation: next,\n // Marking streams as warming again ensures hooks treat the data as\n // \"in transit\" until a fresh event arrives under the new generation.\n sync: {\n ...snapshot.sync,\n record: degradeForRefresh(snapshot.sync.record, snapshot.generation),\n records: degradeForRefresh(snapshot.sync.records, snapshot.generation),\n options: degradeForRefresh(snapshot.sync.options, snapshot.generation),\n newRecord: degradeForRefresh(snapshot.sync.newRecord, snapshot.generation),\n },\n }\n}\n\nfunction degradeForRefresh(\n freshness: GristStreamFreshness,\n prevGeneration: GristGeneration\n): GristStreamFreshness {\n if (freshness.state === \"hot\" && freshness.generation === prevGeneration) {\n return { state: \"stale\", lastEventMs: freshness.lastEventMs, reason: \"host_quiet\" }\n }\n return freshness\n}\n\n// ---------------------------------------------------------------------------\n// Mappings (config)\n// ---------------------------------------------------------------------------\n\nfunction reduceMapping(\n snapshot: GristWidgetSnapshot,\n action: GristMappingAction,\n ctx: GristReducerContext\n): GristWidgetSnapshot {\n switch (action.type) {\n case \"MAPPING_DECLARE\": {\n if (action.declared.length === 0) {\n // Empty declaration → revert to `not_declared`.\n if (snapshot.config.mappings.state === \"not_declared\") return snapshot\n ctx.resolver = null\n return {\n ...snapshot,\n config: { mappings: { state: \"not_declared\" } },\n }\n }\n if (!ctx.resolver) {\n ctx.resolver = new MappingResolver(action.declared, snapshot.generation)\n } else {\n ctx.resolver.redeclare(action.declared, snapshot.generation)\n }\n return {\n ...snapshot,\n config: {\n mappings: {\n state: \"pending\",\n declared: action.declared,\n declaredAtMs: action.atMs,\n },\n },\n }\n }\n case \"MAPPING_REPORT\": {\n if (!ctx.resolver) return snapshot\n const outcome = ctx.resolver.ingest({\n source: action.source,\n payload: action.payload,\n receivedAtMs: action.atMs,\n generation: action.generation,\n })\n if (outcome.kind !== \"resolved\") return snapshot\n return applyResolved(snapshot, outcome.state)\n }\n case \"MAPPING_INVALIDATE\": {\n const previous = snapshot.config.mappings\n if (previous.state !== \"complete\" && previous.state !== \"incomplete\") {\n return snapshot\n }\n return {\n ...snapshot,\n config: {\n mappings: {\n state: \"invalidating\",\n previous: previous as GristMappingComplete | GristMappingIncomplete,\n sinceMs: action.atMs,\n },\n },\n }\n }\n case \"MAPPING_INVALIDATION_RESOLVE\": {\n if (snapshot.config.mappings.state !== \"invalidating\") return snapshot\n if (!ctx.resolver) {\n // Resolver lost (e.g. declaration cleared) — fall back to `previous`.\n return {\n ...snapshot,\n config: { mappings: snapshot.config.mappings.previous },\n }\n }\n const outcome = ctx.resolver.current()\n if (outcome.kind !== \"resolved\") {\n return {\n ...snapshot,\n config: { mappings: snapshot.config.mappings.previous },\n }\n }\n return applyResolved(snapshot, outcome.state)\n }\n case \"MAPPING_TIMEOUT\": {\n const previous = snapshot.config.mappings\n if (previous.state !== \"pending\") return snapshot\n return {\n ...snapshot,\n config: {\n mappings: {\n state: \"unreported\",\n declared: previous.declared,\n afterMs: action.atMs - previous.declaredAtMs,\n },\n },\n }\n }\n }\n}\n\nfunction applyResolved(\n snapshot: GristWidgetSnapshot,\n resolved: GristMappingComplete | GristMappingIncomplete\n): GristWidgetSnapshot {\n const existing = snapshot.config.mappings\n if (\n (existing.state === \"complete\" || existing.state === \"incomplete\") &&\n sameResolved(existing, resolved)\n ) {\n return snapshot\n }\n return { ...snapshot, config: { mappings: resolved } }\n}\n\nfunction sameResolved(\n a: GristMappingState,\n b: GristMappingComplete | GristMappingIncomplete\n): boolean {\n if (a.state !== b.state) return false\n if (a.state !== \"complete\" && a.state !== \"incomplete\") return false\n if (a.source !== b.source) return false\n const ak = Object.keys(a.mappings)\n const bk = Object.keys(b.mappings)\n if (ak.length !== bk.length) return false\n for (const k of ak) {\n const av = a.mappings[k]\n const bv = b.mappings[k]\n if (Array.isArray(av) && Array.isArray(bv)) {\n if (av.length !== bv.length) return false\n for (let i = 0; i < av.length; i++) if (av[i] !== bv[i]) return false\n } else if (av !== bv) {\n return false\n }\n }\n return true\n}\n\n// ---------------------------------------------------------------------------\n// Streams (sync)\n// ---------------------------------------------------------------------------\n\nfunction reduceStream(snapshot: GristWidgetSnapshot, action: GristSyncAction): GristWidgetSnapshot {\n if (action.type === \"CURRENT_TABLE_KNOWN\" || action.type === \"CURRENT_TABLE_REFRESH_START\" || action.type === \"CURRENT_TABLE_ERROR\") {\n return snapshot\n }\n const stream = action.stream\n const current = snapshot.sync[stream]\n\n if (action.type === \"STREAM_WARMING\") {\n if (current.state === \"warming\" || current.state === \"hot\") return snapshot\n return updateStream(snapshot, stream, { state: \"warming\", sinceMs: action.atMs })\n }\n\n if (action.type === \"STREAM_STALE\") {\n if (current.state !== \"hot\") return snapshot\n return updateStream(snapshot, stream, {\n state: \"stale\",\n lastEventMs: current.lastEventMs,\n reason: action.reason,\n })\n }\n\n // STREAM_RECEIVED — idempotent on (generation, hash).\n if (\n current.state === \"hot\" &&\n current.generation === action.generation &&\n current.payloadHash === action.payloadHash\n ) {\n return snapshot\n }\n return updateStream(snapshot, stream, {\n state: \"hot\",\n lastEventMs: action.atMs,\n generation: action.generation,\n payloadHash: action.payloadHash,\n })\n}\n\nfunction updateStream(\n snapshot: GristWidgetSnapshot,\n stream: GristSyncStream,\n next: GristStreamFreshness\n): GristWidgetSnapshot {\n return {\n ...snapshot,\n sync: { ...snapshot.sync, [stream]: next },\n }\n}\n\nfunction updateSync(snapshot: GristWidgetSnapshot, sync: GristSync): GristWidgetSnapshot {\n return { ...snapshot, sync }\n}\n\n// ---------------------------------------------------------------------------\n// Re-exports (convenience)\n// ---------------------------------------------------------------------------\n\nexport { ZERO_GENERATION, nextGeneration }\n","/**\n * Pure projections from a {@link GristWidgetSnapshot}:\n *\n * - {@link deriveStatus} → legacy 4-state `status` (compat).\n * - {@link deriveCapabilities} → ready-to-bind booleans for widget UI.\n *\n * Both are deterministic and side-effect free. Callers (hooks, Boundary)\n * memoize them by reference equality of `snapshot`.\n */\n\nimport { ACCESS_RANK } from \"./access\"\nimport {\n pendingColumnMappingStatus,\n type ValidateColumnMappingsResult,\n} from \"../../helpers/grist-mappings\"\nimport type { GristColumnsToMap, GristWidgetColumnMap } from \"../../types\"\nimport type {\n GristCapabilities,\n GristMappingState,\n GristWidgetSnapshot,\n GristWidgetStatus,\n} from \"./types\"\n\n// ---------------------------------------------------------------------------\n// Status projection\n// ---------------------------------------------------------------------------\n\nexport function deriveStatus(snapshot: GristWidgetSnapshot): GristWidgetStatus {\n const { lifecycle, authz, link } = snapshot\n\n if (lifecycle.phase === \"idle\" || lifecycle.phase === \"detecting\") return \"booting\"\n\n if (lifecycle.phase === \"negotiating\") return \"booting\"\n\n if (lifecycle.phase === \"terminated\") {\n switch (lifecycle.reason.kind) {\n case \"script_timeout\":\n case \"not_embedded\":\n return \"unavailable\"\n case \"host_rejected\":\n case \"ready_failed\":\n case \"ready_timeout\":\n case \"manual_terminate\":\n return \"error\"\n }\n }\n\n // lifecycle.phase === \"online\"\n if (authz.state === \"denied\") return \"error\"\n if (link.state === \"lost\") return \"error\"\n return \"ready\"\n}\n\n/** Extract a single human-readable error message, if any, from the snapshot. */\nexport function deriveErrorMessage(snapshot: GristWidgetSnapshot): string | null {\n if (snapshot.lifecycle.phase === \"terminated\") {\n switch (snapshot.lifecycle.reason.kind) {\n case \"ready_failed\":\n return snapshot.lifecycle.reason.message\n case \"ready_timeout\":\n return `Grist handshake timed out after ${snapshot.lifecycle.reason.afterMs}ms`\n case \"host_rejected\":\n return snapshot.lifecycle.reason.message\n case \"script_timeout\":\n return `Grist plugin API never loaded (${snapshot.lifecycle.reason.attempts} attempts)`\n case \"not_embedded\":\n return \"This page is not embedded in a Grist iframe\"\n case \"manual_terminate\":\n return null\n }\n }\n if (snapshot.authz.state === \"denied\") {\n return (\n snapshot.authz.message ??\n `Grist denied requested access (${snapshot.authz.declared})`\n )\n }\n if (snapshot.lifecycle.phase === \"online\" && snapshot.link.state === \"lost\") {\n return `Grist link lost (${snapshot.link.missedPings} consecutive failures)`\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// Capabilities derivation\n// ---------------------------------------------------------------------------\n\nexport function deriveCapabilities(snapshot: GristWidgetSnapshot): GristCapabilities {\n const { lifecycle, authz, link, config, sync } = snapshot\n const online = lifecycle.phase === \"online\"\n const granted = authz.state === \"granted\"\n const grantedReadOrMore =\n granted && ACCESS_RANK[authz.level] >= ACCESS_RANK[\"read table\"]\n const grantedFull = granted && authz.level === \"full\"\n const linkUsable = link.state !== \"lost\"\n\n const canRead = online && linkUsable && grantedReadOrMore\n const canFetchTable = canRead && link.state === \"connected\"\n\n const mappingsUsable = hasUsableMappings(config.mappings)\n const missing = mappingsMissing(config.mappings)\n const emptyMultiples = mappingsEmptyMultiples(config.mappings)\n\n const canRender = canRead && mappingsUsable\n const canWriteRecords = canRender && grantedFull\n const canWriteSchema =\n canWriteRecords && sync.currentTable.state === \"known\"\n\n const hasFreshSelection =\n canRead &&\n sync.record.state === \"hot\" &&\n config.mappings.state !== \"invalidating\"\n\n return {\n canRead,\n canRender,\n canWriteRecords,\n canWriteSchema,\n canFetchTable,\n hasFreshSelection,\n hasUsableMappings: mappingsUsable,\n missingMappings: missing,\n emptyMultipleMappings: emptyMultiples,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Mapping helpers\n// ---------------------------------------------------------------------------\n\nfunction hasUsableMappings(state: GristMappingState): boolean {\n if (state.state === \"not_declared\") return true\n if (state.state === \"complete\") return true\n if (state.state === \"invalidating\") {\n return state.previous.state === \"complete\"\n }\n return false\n}\n\nfunction mappingsMissing(state: GristMappingState): readonly string[] {\n if (state.state === \"incomplete\") return state.missing\n if (state.state === \"invalidating\" && state.previous.state === \"incomplete\") {\n return state.previous.missing\n }\n return EMPTY\n}\n\nfunction mappingsEmptyMultiples(state: GristMappingState): readonly string[] {\n if (state.state === \"incomplete\") return state.emptyMultiples\n if (state.state === \"invalidating\" && state.previous.state === \"incomplete\") {\n return state.previous.emptyMultiples\n }\n return EMPTY\n}\n\nconst EMPTY: readonly string[] = Object.freeze([])\n\n// ---------------------------------------------------------------------------\n// Column-mapping projection (public `columnMappingStatus` shape)\n// ---------------------------------------------------------------------------\n\n/** Resolved mappings from the FSM config axis, if any. */\nexport function extractResolvedMappings(\n state: GristMappingState\n): GristWidgetColumnMap | null {\n switch (state.state) {\n case \"complete\":\n case \"incomplete\":\n return state.mappings\n case \"invalidating\":\n if (state.previous.state === \"complete\" || state.previous.state === \"incomplete\") {\n return state.previous.mappings\n }\n return null\n default:\n return null\n }\n}\n\n/**\n * Project {@link GristMappingState} into the legacy\n * {@link ValidateColumnMappingsResult} consumed by `useGrist().columnMappingStatus`.\n */\nexport function deriveColumnMappingStatus(\n mappingState: GristMappingState,\n columns?: GristColumnsToMap\n): ValidateColumnMappingsResult {\n if (!columns?.length) {\n return { ok: true, pending: false, missing: [], emptyMultiples: [] }\n }\n switch (mappingState.state) {\n case \"not_declared\":\n return { ok: true, pending: false, missing: [], emptyMultiples: [] }\n case \"pending\":\n return pendingColumnMappingStatus\n case \"unreported\":\n return pendingColumnMappingStatus\n case \"complete\":\n return { ok: true, pending: false, missing: [], emptyMultiples: [] }\n case \"incomplete\":\n return {\n ok: false,\n pending: false,\n missing: [...mappingState.missing],\n emptyMultiples: [...mappingState.emptyMultiples],\n }\n case \"invalidating\":\n return deriveColumnMappingStatus(mappingState.previous, columns)\n }\n}\n","/**\n * Internal context that hosts the shared {@link GristHandshakeManager} for a\n * widget subtree.\n *\n * - `<GristWidgetProvider>` publishes the manager via\n * {@link GristHandshakeManagerContext}.\n * - Standalone `useGrist()` / mid-level hooks without a provider acquire the\n * page-level embedded manager ({@link acquireEmbeddedHandshakeManager}).\n *\n * All SDK hooks call {@link useGristManager} — there is no pre-FSM fallback.\n */\n\nimport { createContext, useContext, useEffect, useRef } from \"react\"\n\nimport { buildManagerOptionsFromGristOptions, type BuildManagerOptionsInput } from \"./build-manager-options\"\nimport {\n acquireEmbeddedHandshakeManager,\n releaseEmbeddedHandshakeManager,\n} from \"./embedded-handshake-manager\"\nimport type { ManagerControls } from \"./handshake/manager\"\n\nexport const GristHandshakeManagerContext = createContext<ManagerControls | null>(null)\n\n/** Read the manager from context. Returns `null` outside a provider. */\nexport function useGristManagerOptional(): ManagerControls | null {\n return useContext(GristHandshakeManagerContext)\n}\n\n/**\n * Returns the active handshake manager for this React tree.\n *\n * Inside `<GristWidgetProvider>` this is the provider's manager. Otherwise a\n * single ref-counted embedded manager is shared across all standalone hook\n * instances on the page.\n */\nexport function useGristManager(options?: BuildManagerOptionsInput): ManagerControls {\n const fromProvider = useContext(GristHandshakeManagerContext)\n const optionsRef = useRef(options)\n optionsRef.current = options\n\n const embeddedRef = useRef<ManagerControls | null>(null)\n const ownsEmbeddedRef = useRef(false)\n\n if (fromProvider) {\n return fromProvider\n }\n\n if (embeddedRef.current === null) {\n embeddedRef.current = acquireEmbeddedHandshakeManager(\n buildManagerOptionsFromGristOptions(optionsRef.current)\n )\n ownsEmbeddedRef.current = true\n }\n\n useEffect(() => {\n if (!ownsEmbeddedRef.current) return\n return () => {\n releaseEmbeddedHandshakeManager()\n embeddedRef.current = null\n ownsEmbeddedRef.current = false\n }\n }, [])\n\n return embeddedRef.current\n}\n","/**\n * Single-call wrapper around `grist.ready()`.\n *\n * Grist's contract is \"call ready once\". Multiple `useGristReady` calls (or\n * `useGristWidget` from sibling components) would otherwise race. We:\n *\n * - Coalesce concurrent calls into one in-flight promise.\n * - Re-call `grist.ready` only when `requiredAccess` widens (so an action-capable\n * hook can upgrade a previously read-only handshake).\n */\n\nimport type { GristColumnsToMap, GristReadyOptions, GristRequiredAccess } from \"../types\"\n\nconst ACCESS_RANK: Record<GristRequiredAccess, number> = {\n none: 0,\n \"read table\": 1,\n full: 2,\n}\n\nlet inFlight: Promise<void> | null = null\nlet lastAccess: GristRequiredAccess | null = null\nlet lastColumnNames: ReadonlySet<string> | null = null\n\nfunction columnNamesOf(columns: GristColumnsToMap | undefined): Set<string> {\n return new Set((columns ?? []).map((c) => (typeof c === \"string\" ? c : c.name)))\n}\n\n/**\n * Re-init whenever the new payload asks for strictly more than what's\n * already cached: a higher access level, or a column name the cached\n * payload didn't declare. Prevents a bare fallback caller (e.g. a status\n * chip with no declared columns) from permanently sticking a widget's real,\n * richer requirements to the host once it happens to win the initial race.\n */\nfunction shouldReinit(next: GristRequiredAccess, nextColumns: ReadonlySet<string>): boolean {\n if (!lastAccess) return true\n if (ACCESS_RANK[next] > ACCESS_RANK[lastAccess]) return true\n for (const name of nextColumns) {\n if (!lastColumnNames?.has(name)) return true\n }\n return false\n}\n\n/**\n * Calls `grist.ready` at most once per page (re-called only if access widens\n * or previously-undeclared columns are requested).\n * Returns the same promise for all concurrent callers.\n */\nexport function ensureGristReady(payload: GristReadyOptions): Promise<void> {\n const access = payload.requiredAccess ?? \"read table\"\n const columns = columnNamesOf(payload.columns)\n if (inFlight && !shouldReinit(access, columns)) return inFlight\n\n lastAccess = access\n lastColumnNames = columns\n const next = Promise.resolve().then(() => {\n const result = grist.ready(payload)\n return result instanceof Promise ? result : Promise.resolve()\n })\n\n inFlight = next.catch((err) => {\n inFlight = null\n lastAccess = null\n lastColumnNames = null\n throw err\n })\n return inFlight\n}\n\n/**\n * Forget the cached ready state. Subsequent calls to {@link ensureGristReady}\n * will re-issue `grist.ready` even at the same access level.\n *\n * Production uses: the handshake manager's `reload()` clears the singleton so\n * a soft retry triggers a fresh `grist.ready` (otherwise the cached promise\n * would short-circuit and the user-visible reload would be a no-op).\n *\n * Tests use this between test cases when swapping `window.grist`.\n */\nexport function resetGristReadySingleton(): void {\n inFlight = null\n lastAccess = null\n lastColumnNames = null\n}\n\n/** @deprecated Use {@link resetGristReadySingleton}. Kept for test back-compat. */\nexport const resetGristReadyCacheForTests = resetGristReadySingleton\n","import type { ManagerOptions } from \"./handshake/manager\"\nimport { ensureGristReady, resetGristReadySingleton } from \"./ready-singleton\"\nimport type { GristReadyOptions } from \"../types\"\n\nexport type BuildManagerOptionsInput = GristReadyOptions & {\n availabilityMaxAttempts?: number\n availabilityIntervalMs?: number\n logWhenNotEmbedded?: boolean\n}\n\n/**\n * Map public widget options to {@link ManagerOptions} for\n * {@link createGristHandshakeManager}.\n */\nexport function buildManagerOptionsFromGristOptions(\n options?: BuildManagerOptionsInput\n): ManagerOptions {\n const opts: ManagerOptions = {\n requiredAccess: options?.requiredAccess ?? \"read table\",\n columns: options?.columns ?? [],\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n negotiate: {\n readyImpl: (payload) => ensureGristReady(payload),\n },\n onBeforeReload: () => resetGristReadySingleton(),\n }\n if (\n options?.availabilityMaxAttempts !== undefined ||\n options?.availabilityIntervalMs !== undefined\n ) {\n const interval = options?.availabilityIntervalMs ?? 1000\n const max = options?.availabilityMaxAttempts ?? 10\n opts.detect = {\n pollSequenceMs: Array.from({ length: max }, () => interval),\n budgetMs: interval * max,\n }\n }\n return opts\n}\n","/**\n * Detect effect — poll for `window.grist` with exponential backoff inside a\n * finite, optionally adaptive budget.\n *\n * Outputs (dispatched to the manager):\n * - DETECT_START (once, at start)\n * - DETECT_TICK (each unsuccessful poll, with attempts count)\n * - DETECT_AVAILABLE (grist global present AND embedded)\n * - NOT_EMBEDDED (grist global present but `self === top`)\n * - DETECT_TIMEOUT (budget consumed without detection)\n *\n * The effect aborts on `signal.aborted` and clears its pending timer.\n *\n * Pure of `window`: relies on the {@link GristEnvironment} adapter so the\n * effect is fully unit-testable with a virtual clock.\n */\n\nimport type { GristEnvironment, TimerHandle } from \"../environment\"\nimport type { GristAction } from \"../actions\"\n\n/**\n * Default poll sequence (ms). Caps at 5 000 ms — most production widgets see\n * `grist-plugin-api.js` load in < 1 s, but slow 3G can stretch to 20–30 s.\n */\nexport const DEFAULT_POLL_SEQUENCE_MS: readonly number[] = [\n 200, 200, 400, 400, 800, 1600, 3200, 5000, 5000, 5000, 5000, 5000, 5000, 5000,\n] as const\n\n/** Effective budget, adaptive on `navigator.connection.effectiveType`. */\nexport function defaultBudgetMs(hint: ReturnType<GristEnvironment[\"effectiveTypeHint\"]>): number {\n if (hint === \"slow-2g\" || hint === \"2g\") return 120_000\n if (hint === \"3g\") return 60_000\n return 30_000\n}\n\nexport type DetectOptions = {\n /** Custom poll sequence in ms. Index past end uses last value. */\n pollSequenceMs?: readonly number[]\n /** Total wall-clock budget across all polls. */\n budgetMs?: number\n}\n\nexport type DetectHandle = {\n cancel(): void\n}\n\n/**\n * Start the detect effect. The manager owns the cancellation handle and is\n * responsible for calling `cancel()` on `restart()` / `stop()`.\n */\nexport function startDetectEffect(\n env: GristEnvironment,\n dispatch: (action: GristAction) => void,\n signal: AbortSignal,\n options: DetectOptions = {}\n): DetectHandle {\n const sequence = options.pollSequenceMs ?? DEFAULT_POLL_SEQUENCE_MS\n const budgetMs = options.budgetMs ?? defaultBudgetMs(env.effectiveTypeHint())\n\n const startedAt = env.now()\n let attempts = 0\n let timer: TimerHandle | null = null\n let cancelled = false\n\n signal.addEventListener(\"abort\", () => {\n cancelled = true\n if (timer !== null) env.clearTimeout(timer)\n })\n\n dispatch({ type: \"DETECT_START\", atMs: startedAt })\n\n const tick = () => {\n if (cancelled) return\n timer = null\n\n const probe = env.probeGrist()\n if (probe !== null) {\n if (probe.embedded) {\n dispatch({ type: \"DETECT_AVAILABLE\", atMs: env.now() })\n } else {\n dispatch({ type: \"NOT_EMBEDDED\", atMs: env.now() })\n }\n return\n }\n\n attempts += 1\n dispatch({ type: \"DETECT_TICK\", attempts, atMs: env.now() })\n\n const elapsed = env.now() - startedAt\n if (elapsed >= budgetMs) {\n dispatch({ type: \"DETECT_TIMEOUT\", attempts, atMs: env.now() })\n return\n }\n\n const next = sequence[Math.min(attempts - 1, sequence.length - 1)] ?? 1000\n const remaining = budgetMs - elapsed\n timer = env.setTimeout(tick, Math.max(0, Math.min(next, remaining)))\n }\n\n // Probe immediately at t=0 so widgets where grist is already global don't\n // wait for the first interval.\n tick()\n\n return {\n cancel() {\n cancelled = true\n if (timer !== null) {\n env.clearTimeout(timer)\n timer = null\n }\n },\n }\n}\n","/**\n * Negotiate effect — issue `grist.ready(payload)` race-d against a timeout\n * and an external `AbortSignal`.\n *\n * Dispatches:\n * - NEGOTIATE_START\n * - NEGOTIATE_SUCCESS (grist.ready resolved AND `docApi` is present)\n * - NEGOTIATE_FAIL (reason: ready_failed | ready_timeout | manual_terminate)\n *\n * The effect never throws to the caller. Aborts before resolution silently\n * stop dispatching.\n */\n\nimport type { GristEnvironment, GristLike } from \"../environment\"\nimport type { GristAction } from \"../actions\"\nimport type { GristReadyOptions, GristRequiredAccess } from \"../../../types\"\nimport type { GristTerminationReason } from \"../types\"\n\nexport type NegotiateOptions = {\n /** Max ms to wait for `grist.ready` to resolve. */\n timeoutMs?: number\n /**\n * Custom implementation of `grist.ready(payload)`. Defaults to calling\n * `grist.ready` directly via the env. Production callers (the provider)\n * inject a wrapper that routes through `ensureGristReady()` so the\n * page-level singleton dedupes concurrent ready calls.\n */\n readyImpl?: (payload: GristReadyOptions) => Promise<void> | void\n}\n\nexport const DEFAULT_NEGOTIATE_TIMEOUT_MS = 30_000\n\nexport type NegotiateHandle = {\n cancel(): void\n}\n\nexport function startNegotiateEffect(\n env: GristEnvironment,\n dispatch: (action: GristAction) => void,\n signal: AbortSignal,\n payload: GristReadyOptions,\n options: NegotiateOptions = {}\n): NegotiateHandle {\n const timeoutMs = options.timeoutMs ?? DEFAULT_NEGOTIATE_TIMEOUT_MS\n const access: GristRequiredAccess = payload.requiredAccess ?? \"read table\"\n let settled = false\n let timer: ReturnType<GristEnvironment[\"setTimeout\"]> | null = null\n\n const settle = (next: () => void) => {\n if (settled) return\n settled = true\n if (timer !== null) env.clearTimeout(timer)\n if (signal.aborted) return\n next()\n }\n\n dispatch({ type: \"NEGOTIATE_START\", access, atMs: env.now() })\n\n const grist = env.getGrist()\n if (!grist) {\n settle(() =>\n dispatch({\n type: \"NEGOTIATE_FAIL\",\n reason: { kind: \"ready_failed\", message: \"grist global missing at negotiate\" },\n atMs: env.now(),\n })\n )\n return { cancel: () => settle(() => {}) }\n }\n\n // Schedule timeout first; ready() may resolve synchronously and trigger\n // `settle` before this fires.\n timer = env.setTimeout(() => {\n if (settled) return\n settle(() =>\n dispatch({\n type: \"NEGOTIATE_FAIL\",\n reason: { kind: \"ready_timeout\", afterMs: timeoutMs },\n atMs: env.now(),\n })\n )\n }, timeoutMs)\n\n const handleSuccess = (g: GristLike) => {\n if (!g.docApi) {\n dispatch({\n type: \"NEGOTIATE_FAIL\",\n reason: { kind: \"ready_failed\", message: \"grist.docApi missing after ready\" },\n atMs: env.now(),\n })\n return\n }\n dispatch({\n type: \"NEGOTIATE_SUCCESS\",\n access,\n grantedLevel: access, // host doesn't report effective level; widen later via authz events\n atMs: env.now(),\n })\n }\n\n const handleFailure = (err: unknown) => {\n const message = err instanceof Error ? err.message : String(err)\n const reason: GristTerminationReason = { kind: \"ready_failed\", message }\n dispatch({ type: \"NEGOTIATE_FAIL\", reason, atMs: env.now() })\n }\n\n const readyImpl = options.readyImpl ?? ((p) => grist.ready(p))\n try {\n const result = readyImpl(payload)\n if (result && typeof (result as Promise<void>).then === \"function\") {\n ;(result as Promise<void>).then(\n () => settle(() => handleSuccess(env.getGrist() ?? grist)),\n (err) => settle(() => handleFailure(err))\n )\n } else {\n settle(() => handleSuccess(env.getGrist() ?? grist))\n }\n } catch (err) {\n settle(() => handleFailure(err))\n }\n\n signal.addEventListener(\"abort\", () => settle(() => {}))\n\n return { cancel: () => settle(() => {}) }\n}\n","import type { GristWidgetInteraction, GristWidgetLinkingInfo } from \"../types\"\n\n/**\n * Parse `InteractionOptions.linking` from a Grist `onOptions` settings payload.\n */\nexport function parseGristWidgetLinkingInfo(raw: unknown): GristWidgetLinkingInfo | undefined {\n if (!raw || typeof raw !== \"object\" || !(\"asTarget\" in raw)) return undefined\n const linking = raw as { asTarget: unknown; asSource?: unknown }\n return {\n asTarget: linking.asTarget == null ? null : String(linking.asTarget),\n asSource: Boolean(linking.asSource),\n }\n}\n\n/**\n * Normalize the second argument to `grist.onOptions` (Grist `InteractionOptions`).\n * Hosts use `accessLevel` (camelCase); the emulator uses `access_level`.\n */\nexport function normalizeWidgetInteraction(\n interaction: GristWidgetInteraction | null\n): GristWidgetInteraction | null {\n if (!interaction || typeof interaction !== \"object\") return interaction\n const raw = interaction as Record<string, unknown>\n const access_level =\n typeof raw.access_level === \"string\"\n ? raw.access_level\n : typeof raw.accessLevel === \"string\"\n ? raw.accessLevel\n : undefined\n const linking = parseGristWidgetLinkingInfo(raw.linking)\n return {\n ...interaction,\n ...(access_level ? { access_level } : {}),\n ...(linking ? { linking } : {}),\n }\n}\n","/**\n * Module-level subscription manager for Grist's `on*` events.\n *\n * Why this exists: `grist.onRecord`, `onRecords`, `onOptions`, and `onNewRecord`\n * are add-only — there is no `off`. Subscribing in every hook instance leaks\n * handlers for the page lifetime. This module subscribes to each Grist event\n * **at most once** and fans out to React listeners with proper teardown.\n *\n * Last-known values are cached so late subscribers immediately receive the\n * current state (analogous to a BehaviorSubject).\n *\n * Fixed-defaults contract\n * -----------------------\n * The underlying `grist.onRecord` / `onRecords` calls accept a\n * `FetchSelectedOptions` parameter (e.g. `keepEncoded`, `format`), but\n * because we bind **once** at the first subscribe, those options would be\n * \"first-hook-wins\" across the page — a subtle footgun for agents.\n *\n * This module therefore binds with Grist's **public defaults** and does not\n * accept per-subscriber options:\n * - `onRecord` → decoded rows (`keepEncoded: false`)\n * - `onRecords` → decoded rows (`format: \"rows\"`, `keepEncoded: false`)\n *\n * If you need encoded values, use the one-shot readers\n * (`useGrist().fetchSelectedTable({ keepEncoded: true })`) instead of\n * a subscription — that call-site controls its own options.\n */\n\nimport { normalizeWidgetInteraction } from \"../helpers/grist-widget-interaction\"\nimport type { GristWidgetColumnMap, GristWidgetInteraction } from \"../types\"\n\ntype Listener<T> = (value: T) => void\n\nclass GristEvent<T> {\n private listeners = new Set<Listener<T>>()\n private hasValue = false\n private last: T | undefined\n private bind: (() => void) | null = null\n\n constructor(private readonly bindOnce: (emit: (value: T) => void) => void) {}\n\n subscribe(listener: Listener<T>): () => void {\n this.listeners.add(listener)\n if (!this.bind) {\n this.bind = () => {\n // Mark as bound regardless of throw, so we don't retry every render.\n this.bind = () => {}\n try {\n this.bindOnce((value) => {\n this.hasValue = true\n this.last = value\n for (const l of this.listeners) l(value)\n })\n } catch (err) {\n if (typeof console !== \"undefined\") {\n console.error(\"[lib/grist] failed to subscribe to Grist event:\", err)\n }\n }\n }\n this.bind()\n }\n if (this.hasValue && this.last !== undefined) {\n listener(this.last)\n }\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n /** Last cached value (or `undefined` if no event has fired yet). */\n peek(): T | undefined {\n return this.last\n }\n\n /** True once at least one event has fired. */\n get ready(): boolean {\n return this.hasValue\n }\n\n /**\n * Test helper: fan out a payload without calling `grist.on*`.\n * Used to simulate hosts that reuse one record object and mutate it in place.\n */\n deliverForTests(value: T): void {\n this.hasValue = true\n this.last = value\n for (const l of this.listeners) l(value)\n }\n\n /**\n * Test helper: forget the bound state and cached value. Lets a test swap\n * `window.grist` (e.g. via the emulator's `dispose()` + `createGristEmulator()`)\n * and re-bind cleanly on the next subscribe.\n */\n _resetForTests(): void {\n this.listeners.clear()\n this.hasValue = false\n this.last = undefined\n this.bind = null\n }\n}\n\nexport type GristRecordPayload = {\n record: Record<string, unknown> | null\n mappings: GristWidgetColumnMap | null\n}\n\nexport type GristRecordsPayload = {\n records: Record<string, unknown>[]\n mappings: GristWidgetColumnMap | null\n}\n\nexport type GristOptionsPayload = {\n options: Record<string, unknown> | null\n interaction: GristWidgetInteraction | null\n}\n\nexport type GristNewRecordPayload = {\n mappings: GristWidgetColumnMap | null\n}\n\n/**\n * The singletons. These are lazy: nothing is registered with `grist` until\n * the first `subscribe()` call.\n */\ntype GristHostMessage = {\n tableId?: string\n rowId?: number | \"new\"\n dataChange?: boolean\n mappingsChange?: boolean\n}\n\nexport const recordEvent = new GristEvent<GristRecordPayload>((emit) => {\n const deliver = (record: Record<string, unknown> | null, mappings: GristWidgetColumnMap | null) => {\n emit({ record, mappings })\n }\n\n // Pin `keepEncoded: false` so mapped records contain JS-native values for\n // widget code instead of raw cell tuples.\n grist.onRecord(\n (record, mappings) => {\n deliver(record, (mappings as GristWidgetColumnMap | null) ?? null)\n },\n { keepEncoded: false }\n )\n\n // Mirror grist-plugin-api's `onRecord` message handler: Grist's RecordNotifier\n // posts `{ rowId, dataChange: false }` on cursor moves. `onRecord` should\n // handle that, but refetch here when `rowId` changes so widgets never stick on\n // the first row if the callback path is skipped or reordered.\n const fetchSelectedRecord = grist.fetchSelectedRecord ?? grist.docApi?.fetchSelectedRecord\n if (typeof grist.on !== \"function\" || typeof fetchSelectedRecord !== \"function\") {\n return\n }\n let lastRowId: number | undefined\n grist.on(\"message\", async (msg: unknown) => {\n if (!msg || typeof msg !== \"object\") return\n const host = msg as GristHostMessage\n if (!host.tableId || typeof host.rowId !== \"number\") return\n if (host.rowId === lastRowId) return\n lastRowId = host.rowId\n try {\n const record = await fetchSelectedRecord(host.rowId, { keepEncoded: false })\n deliver(record, recordEvent.peek()?.mappings ?? null)\n } catch (err) {\n if (typeof console !== \"undefined\") {\n console.error(\"[grist-widget-sdk] fetchSelectedRecord on cursor message failed:\", err)\n }\n }\n })\n})\n\nexport const recordsEvent = new GristEvent<GristRecordsPayload>((emit) => {\n // Pin `format: \"rows\"` + `keepEncoded: false` so callers always receive\n // row-shaped decoded objects. (GristView would otherwise default to\n // `format: \"columns\"` + `keepEncoded: true`.)\n grist.onRecords(\n (records, mappings) => {\n emit({ records, mappings: (mappings as GristWidgetColumnMap | null) ?? null })\n },\n { keepEncoded: false, format: \"rows\" }\n )\n})\n\nexport const optionsEvent = new GristEvent<GristOptionsPayload>((emit) => {\n grist.onOptions((options, interaction) => {\n const normalized = normalizeWidgetInteraction(\n (interaction as GristWidgetInteraction | null) ?? null\n )\n emit({\n options: options ? { ...options } : null,\n interaction: normalized,\n })\n })\n})\n\nexport const newRecordEvent = new GristEvent<GristNewRecordPayload>((emit) => {\n if (typeof grist.onNewRecord !== \"function\") return\n grist.onNewRecord((mappings) => {\n emit({ mappings: (mappings as GristWidgetColumnMap | null) ?? null })\n })\n})\n\n/**\n * Test-only: reset every event singleton. Call between tests when swapping\n * `window.grist`. Production code never imports this.\n */\nexport function resetGristEventsForTests(): void {\n recordEvent._resetForTests()\n recordsEvent._resetForTests()\n optionsEvent._resetForTests()\n newRecordEvent._resetForTests()\n}\n\n/** Vitest-only: push a record payload through {@link recordEvent} subscribers. */\nexport function deliverRecordForTests(payload: GristRecordPayload): void {\n recordEvent.deliverForTests(payload)\n}\n","/**\n * Subscriptions effect — bind Grist event streams once and feed the handshake\n * machine with generation-stamped, hash-deduplicated dispatches.\n *\n * Why a dedicated effect\n * ----------------------\n * The SDK already has a {@link \"../../subscriptions\"} module that owns the\n * one-time binding to `grist.on*` and the BehaviorSubject-style cache. We do\n * NOT re-bind here — instead, we install a single listener per stream that\n * forwards into the handshake reducer.\n *\n * Generation discipline: every dispatched action carries the CURRENT manager\n * generation read at the moment the event arrives. The reducer enforces\n * `action.generation >= snapshot.generation`, so events arriving after a\n * `reload()` / `restart()` that bumped generation are dropped automatically.\n *\n * Hash: payloads are reduced to a compact, deterministic string so duplicates\n * (e.g. emulator triple-replay of initial state) become no-ops at the reducer.\n */\n\nimport type { GristAction, GristSyncStream } from \"../actions\"\nimport { parseGristRequiredAccess } from \"../access\"\nimport type { GristGeneration } from \"../types\"\nimport {\n newRecordEvent,\n optionsEvent,\n recordEvent,\n recordsEvent,\n} from \"../../subscriptions\"\n\nexport type SubscriptionsHandle = {\n cancel(): void\n}\n\n/**\n * Pluggable binder type. The default {@link defaultSubscriptionsBinder}\n * subscribes to the SDK's singleton {@link GristEvent} instances. Tests can\n * supply a stub that never touches the real `grist` global.\n */\nexport type SubscriptionsBinder = (ctx: SubscriptionsContext) => SubscriptionsHandle\n\nexport type SubscriptionsContext = {\n dispatch: (action: GristAction) => void\n /**\n * Snapshot generation provider. Must be read lazily (inside the callback)\n * because `generation` is monotone but mutates over the lifetime of the\n * subscription.\n */\n getGeneration: () => GristGeneration\n /** Hook for the mapping effect: receive payloads in arrival order. */\n onRecordPayload?: (mappings: Record<string, unknown> | null | undefined) => void\n onRecordsPayload?: (mappings: Record<string, unknown> | null | undefined) => void\n onOptionsPayload?: (options: Record<string, unknown> | null) => void\n onNewRecordPayload?: (mappings: Record<string, unknown> | null | undefined) => void\n}\n\nexport const bindSubscriptions: SubscriptionsBinder = defaultSubscriptionsBinder\n\nexport function defaultSubscriptionsBinder(ctx: SubscriptionsContext): SubscriptionsHandle {\n const offs: Array<() => void> = []\n\n offs.push(\n recordEvent.subscribe((payload) => {\n const hash = quickHash(\"record\", payload)\n dispatchStream(ctx, \"record\", hash)\n ctx.onRecordPayload?.(payload.mappings ?? null)\n })\n )\n\n offs.push(\n recordsEvent.subscribe((payload) => {\n const hash = quickHash(\"records\", payload)\n dispatchStream(ctx, \"records\", hash)\n ctx.onRecordsPayload?.(payload.mappings ?? null)\n })\n )\n\n offs.push(\n optionsEvent.subscribe((payload) => {\n const hash = quickHash(\"options\", payload)\n dispatchStream(ctx, \"options\", hash)\n const level = parseGristRequiredAccess(payload.interaction?.access_level)\n if (level !== null) {\n ctx.dispatch({\n type: \"AUTHZ_REPORT\",\n level,\n atMs: Date.now(),\n generation: ctx.getGeneration(),\n })\n }\n ctx.onOptionsPayload?.(payload.options ?? null)\n })\n )\n\n offs.push(\n newRecordEvent.subscribe((payload) => {\n const hash = quickHash(\"newRecord\", payload)\n dispatchStream(ctx, \"newRecord\", hash)\n ctx.onNewRecordPayload?.(payload.mappings ?? null)\n })\n )\n\n return {\n cancel() {\n for (const off of offs.splice(0)) off()\n },\n }\n}\n\n/**\n * No-op binder for tests that don't care about event streams. Returned handle's\n * `cancel()` is a no-op. Use {@link defaultSubscriptionsBinder} in production.\n */\nexport const noopSubscriptionsBinder: SubscriptionsBinder = () => ({\n cancel: () => {},\n})\n\nfunction dispatchStream(\n ctx: SubscriptionsContext,\n stream: GristSyncStream,\n payloadHash: string\n): void {\n ctx.dispatch({\n type: \"STREAM_RECEIVED\",\n stream,\n payloadHash,\n atMs: Date.now(),\n generation: ctx.getGeneration(),\n })\n}\n\n/**\n * Deterministic, low-cost hash of an event payload.\n *\n * We do NOT need cryptographic strength — only equality on identical inputs\n * for idempotence. A short, stable serialization is enough.\n */\nexport function quickHash(kind: string, payload: unknown): string {\n return `${kind}::${stableSerialize(payload)}`\n}\n\nfunction stableSerialize(value: unknown): string {\n if (value === null) return \"null\"\n if (value === undefined) return \"undefined\"\n const t = typeof value\n if (t === \"number\" || t === \"string\" || t === \"boolean\") return JSON.stringify(value)\n if (Array.isArray(value)) {\n return `[${value.map(stableSerialize).join(\",\")}]`\n }\n if (t === \"object\") {\n const obj = value as Record<string, unknown>\n const keys = Object.keys(obj).sort()\n const body = keys.map((k) => `${JSON.stringify(k)}:${stableSerialize(obj[k])}`).join(\",\")\n return `{${body}}`\n }\n return String(value)\n}\n","/**\n * Mappings effect — feed column-mapping payloads into the reducer from the\n * three Grist sources (`sectionApi.mappings()`, `onRecord`, `onRecords`,\n * `onNewRecord`) and trigger the mapping timeout if nothing arrives in time.\n *\n * Inputs:\n * - `declared` — `columns` from the provider options.\n * - `getGeneration()` — current snapshot generation (for stale-drop).\n * - `getGrist()` — environment accessor to `grist.sectionApi.mappings()`.\n *\n * Outputs:\n * - `MAPPING_DECLARE` (once at start when `declared.length > 0`)\n * - `MAPPING_REPORT` (one per source payload received)\n * - `MAPPING_TIMEOUT` (if no payload after `timeoutMs`)\n *\n * The effect does NOT subscribe to grist events directly — it consumes them\n * via the {@link SubscriptionsContext} payload callbacks. This keeps the\n * single-binding contract of `subscriptions.ts` intact (one `grist.onRecord`\n * binding per page).\n */\n\nimport type { GristEnvironment } from \"../environment\"\nimport type { GristAction } from \"../actions\"\nimport type { GristColumnsToMap, GristWidgetColumnMap } from \"../../../types\"\nimport type { GristGeneration } from \"../types\"\n\nexport type MappingsOptions = {\n /**\n * Time after `MAPPING_DECLARE` before falling back to `unreported` state.\n * Default 5 000 ms.\n */\n timeoutMs?: number\n /**\n * If true, dispatch `MAPPING_INVALIDATE` whenever a payload differs from\n * the last applied mappings. Default false — graceful invalidation requires\n * a `mappingsChange: true` envelope flag the SDK doesn't currently surface,\n * so we leave it opt-in for tests.\n */\n invalidateOnDiff?: boolean\n}\n\nexport const DEFAULT_MAPPING_TIMEOUT_MS = 5_000\n\nexport type MappingsHandle = {\n /** Ingest a payload from `onRecord` / `onRecords` / `onNewRecord`. */\n ingestStreamPayload(\n source: \"stream_record\" | \"stream_records\" | \"stream_new_record\",\n payload: unknown\n ): void\n /** Force a re-fetch of `sectionApi.mappings()` (e.g. on link recovery). */\n refetchSectionApi(): Promise<void>\n /** Cancel all pending timers. */\n cancel(): void\n}\n\n/**\n * Start the mapping effect. Returns a handle the manager wires into the\n * subscriptions effect's `on*Payload` callbacks.\n *\n * The effect is a no-op when `declared.length === 0` (the reducer's\n * `MAPPING_DECLARE` action turns it into `not_declared`).\n */\nexport function startMappingsEffect(\n env: GristEnvironment,\n dispatch: (action: GristAction) => void,\n signal: AbortSignal,\n declared: GristColumnsToMap,\n getGeneration: () => GristGeneration,\n options: MappingsOptions = {}\n): MappingsHandle {\n let timeoutHandle: ReturnType<GristEnvironment[\"setTimeout\"]> | null = null\n let cancelled = false\n\n const startMs = env.now()\n dispatch({ type: \"MAPPING_DECLARE\", declared, atMs: startMs, generation: getGeneration() })\n\n // No declaration → no timeout, no section_api fetch, no further work.\n if (declared.length === 0) {\n return {\n ingestStreamPayload: () => {},\n refetchSectionApi: async () => {},\n cancel: () => {},\n }\n }\n\n // Arm the unreported timeout.\n const timeoutMs = options.timeoutMs ?? DEFAULT_MAPPING_TIMEOUT_MS\n timeoutHandle = env.setTimeout(() => {\n if (cancelled || signal.aborted) return\n dispatch({ type: \"MAPPING_TIMEOUT\", atMs: env.now(), generation: getGeneration() })\n }, timeoutMs)\n\n signal.addEventListener(\"abort\", () => {\n cancelled = true\n if (timeoutHandle !== null) env.clearTimeout(timeoutHandle)\n })\n\n const ingestPayload = (\n source: \"section_api\" | \"stream_record\" | \"stream_records\" | \"stream_new_record\",\n raw: unknown\n ) => {\n if (cancelled || signal.aborted) return\n const payload = coerceMappingsPayload(raw)\n dispatch({\n type: \"MAPPING_REPORT\",\n source,\n payload,\n atMs: env.now(),\n generation: getGeneration(),\n })\n }\n\n // Kick off `sectionApi.mappings()` once.\n const refetchSectionApi = async () => {\n if (cancelled || signal.aborted) return\n const grist = env.getGrist()\n const api = grist?.sectionApi\n if (!api || typeof api.mappings !== \"function\") return\n try {\n const payload = await api.mappings()\n ingestPayload(\"section_api\", payload)\n } catch {\n ingestPayload(\"section_api\", null)\n }\n }\n void refetchSectionApi()\n\n return {\n ingestStreamPayload: (source, payload) => ingestPayload(source, payload),\n refetchSectionApi,\n cancel() {\n cancelled = true\n if (timeoutHandle !== null) env.clearTimeout(timeoutHandle)\n },\n }\n}\n\nfunction coerceMappingsPayload(raw: unknown): GristWidgetColumnMap | null {\n if (raw === null || raw === undefined) return null\n if (typeof raw !== \"object\") return null\n return raw as GristWidgetColumnMap\n}\n","/**\n * Heartbeat effect — periodically verify the RPC channel is alive.\n *\n * Strategy\n * --------\n * 1. Schedule a ping every `intervalMs` (default 30 s).\n * 2. The ping calls a cheap RPC (default `grist.docApi.getDocName()`).\n * 3. Success → `HEARTBEAT_OK`. Failure / timeout → `HEARTBEAT_FAIL`.\n * 4. Any externally-observed RPC success (the caller invokes\n * {@link HeartbeatHandle.recordRpcSuccess}) coalesces with the heartbeat\n * and reschedules the next ping — we don't burn a request when natural\n * traffic already proves the link.\n * 5. Tab hidden? Pause via `env.onVisibilityChange`. Resume on `visible`.\n *\n * The effect is **purely an observer**: it never mutates application state\n * directly — it only dispatches actions the reducer consumes.\n */\n\nimport type { GristEnvironment, TimerHandle } from \"../environment\"\nimport type { GristAction } from \"../actions\"\n\nexport type HeartbeatOptions = {\n /** How often to issue a probe when no natural RPC traffic occurred. */\n intervalMs?: number\n /** Max time to wait for the probe before counting it as a failure. */\n pingTimeoutMs?: number\n /** If true, pause the heartbeat when the document becomes hidden. */\n pauseWhenHidden?: boolean\n /**\n * Probe used when the interval elapses. Defaults to\n * `() => env.getGrist().docApi.getDocName()`. Tests pass a controlled stub.\n */\n probe?: () => Promise<unknown>\n}\n\nexport const DEFAULT_HEARTBEAT_INTERVAL_MS = 30_000\nexport const DEFAULT_HEARTBEAT_PING_TIMEOUT_MS = 10_000\n\nexport type HeartbeatHandle = {\n /** Stop scheduling and cancel any pending probe. Idempotent. */\n cancel(): void\n /**\n * Coalesce: tell the heartbeat that an unrelated RPC just succeeded. The\n * heartbeat treats it as a free `HEARTBEAT_OK` and pushes the next probe\n * out by a full interval.\n */\n recordRpcSuccess(): void\n /**\n * Coalesce: an unrelated RPC failed. The heartbeat dispatches\n * `HEARTBEAT_FAIL` without firing its own probe, so the link signal\n * degrades immediately rather than waiting for the interval.\n */\n recordRpcFailure(): void\n /** Force a probe now (e.g. on `online` event). */\n pingNow(): void\n}\n\nexport function startHeartbeatEffect(\n env: GristEnvironment,\n dispatch: (action: GristAction) => void,\n signal: AbortSignal,\n options: HeartbeatOptions = {}\n): HeartbeatHandle {\n const intervalMs = options.intervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS\n const pingTimeoutMs = options.pingTimeoutMs ?? DEFAULT_HEARTBEAT_PING_TIMEOUT_MS\n const pauseWhenHidden = options.pauseWhenHidden ?? true\n const probe = options.probe ?? defaultProbe(env)\n\n let cancelled = false\n let paused = false\n let intervalTimer: TimerHandle | null = null\n let inFlight = false\n let inFlightTimeoutTimer: TimerHandle | null = null\n\n const clearInterval = () => {\n if (intervalTimer !== null) {\n env.clearTimeout(intervalTimer)\n intervalTimer = null\n }\n }\n const clearInFlightTimeout = () => {\n if (inFlightTimeoutTimer !== null) {\n env.clearTimeout(inFlightTimeoutTimer)\n inFlightTimeoutTimer = null\n }\n }\n\n const scheduleNext = (delay = intervalMs) => {\n clearInterval()\n if (cancelled || paused) return\n intervalTimer = env.setTimeout(ping, delay)\n }\n\n const ping = () => {\n if (cancelled || paused) return\n intervalTimer = null\n if (inFlight) {\n // A previous probe still in flight; let it resolve before scheduling.\n return\n }\n inFlight = true\n\n // Timeout guard.\n inFlightTimeoutTimer = env.setTimeout(() => {\n if (!inFlight) return\n inFlight = false\n clearInFlightTimeout()\n if (!cancelled) {\n dispatch({ type: \"HEARTBEAT_FAIL\", atMs: env.now() })\n scheduleNext()\n }\n }, pingTimeoutMs)\n\n const handleOk = () => {\n if (!inFlight) return\n inFlight = false\n clearInFlightTimeout()\n if (cancelled) return\n dispatch({ type: \"HEARTBEAT_OK\", atMs: env.now() })\n scheduleNext()\n }\n const handleFail = () => {\n if (!inFlight) return\n inFlight = false\n clearInFlightTimeout()\n if (cancelled) return\n dispatch({ type: \"HEARTBEAT_FAIL\", atMs: env.now() })\n scheduleNext()\n }\n\n // Call probe synchronously so we collapse one microtask round. If it\n // throws synchronously or returns a non-thenable, settle immediately.\n let result: unknown\n try {\n result = probe()\n } catch {\n handleFail()\n return\n }\n if (\n result &&\n typeof (result as { then?: unknown }).then === \"function\"\n ) {\n ;(result as Promise<unknown>).then(handleOk, handleFail)\n } else {\n handleOk()\n }\n }\n\n // Visibility wiring.\n const offVisibility =\n pauseWhenHidden && env.onVisibilityChange\n ? env.onVisibilityChange((visible) => {\n if (cancelled) return\n if (visible) {\n if (paused) {\n paused = false\n // Refresh immediately on resume — a hidden tab might have\n // missed a long stale window.\n ping()\n }\n } else {\n paused = true\n clearInterval()\n // Note: an in-flight probe is allowed to settle naturally; we\n // just don't schedule another one.\n }\n })\n : null\n\n // Online recovery — when the network reports it's back, ping immediately.\n const offOnline =\n env.onOnline?.(() => {\n if (cancelled || paused) return\n ping()\n }) ?? null\n\n signal.addEventListener(\"abort\", () => {\n cancelled = true\n clearInterval()\n clearInFlightTimeout()\n inFlight = false\n offVisibility?.()\n offOnline?.()\n })\n\n // Boot: first probe after `intervalMs`. We don't ping at t=0 because the\n // very transition that started the heartbeat (e.g. NEGOTIATE_SUCCESS) just\n // proved the link is alive.\n scheduleNext()\n\n return {\n cancel() {\n cancelled = true\n clearInterval()\n clearInFlightTimeout()\n inFlight = false\n offVisibility?.()\n offOnline?.()\n },\n recordRpcSuccess() {\n if (cancelled || paused) return\n dispatch({ type: \"RPC_SUCCESS\", atMs: env.now() })\n scheduleNext()\n },\n recordRpcFailure() {\n if (cancelled || paused) return\n dispatch({ type: \"RPC_FAILURE\", atMs: env.now(), kind: \"other\" })\n // Don't reschedule — a real failure should keep the interval close so\n // we re-probe quickly to confirm.\n scheduleNext(Math.min(intervalMs, 1_000))\n },\n pingNow() {\n if (cancelled) return\n ping()\n },\n }\n}\n\nfunction defaultProbe(env: GristEnvironment): () => Promise<unknown> {\n return async () => {\n const g = env.getGrist()\n if (!g) throw new Error(\"grist global missing\")\n const docApi = g.docApi\n if (!docApi || typeof docApi.getDocName !== \"function\") {\n throw new Error(\"grist.docApi.getDocName unavailable\")\n }\n return docApi.getDocName()\n }\n}\n","/**\n * Re-apply the cached `grist.onOptions` interaction after `reload()`.\n *\n * `reload()` re-runs `grist.ready` (optimistic grant) but Grist often does not\n * send a fresh `onOptions`. The subscription singleton still holds the last\n * `access_level`, which must be checked once the handshake is `online` again.\n *\n * Only runs after `reload()` — not on the initial connect — so a stale\n * pre-`ready` cache cannot deny a first successful handshake.\n */\n\nimport { isAccessSufficient, parseGristRequiredAccess } from \"./access\"\nimport type { GristAction } from \"./actions\"\nimport type { GristEnvironment } from \"./environment\"\nimport type { GristWidgetSnapshot } from \"./types\"\nimport { optionsEvent } from \"../subscriptions\"\n\nexport function denyAuthzFromCachedOptionsIfInsufficient(\n snapshot: GristWidgetSnapshot,\n dispatch: (action: GristAction) => void,\n env: GristEnvironment\n): void {\n if (snapshot.lifecycle.phase !== \"online\") return\n if (snapshot.authz.state === \"denied\") return\n\n const payload = optionsEvent.peek()\n const level = parseGristRequiredAccess(payload?.interaction?.access_level)\n if (level === null) return\n\n const declared = snapshot.authz.declared\n if (isAccessSufficient(level, declared)) return\n\n dispatch({\n type: \"AUTHZ_REPORT\",\n level,\n atMs: env.now(),\n generation: snapshot.generation,\n })\n}\n","/**\n * Environment abstraction for the handshake effects layer.\n *\n * Why an abstraction\n * ------------------\n * The detect / negotiate / heartbeat effects need access to:\n * - the `grist` global (or absence thereof),\n * - `window.self !== window.top` to detect iframe embedding,\n * - timers (`setTimeout`, `clearTimeout`),\n * - the wall clock (`performance.now()` or `Date.now()`),\n * - `navigator.connection?.effectiveType` for adaptive policy.\n *\n * Putting these behind a small interface lets us test the manager and effects\n * **without touching `window`**, using deterministic fake timers and stubbed\n * detectors / negotiators.\n *\n * Production code calls {@link createBrowserEnvironment()} which returns an\n * adapter against `globalThis`. Tests pass {@link createTestEnvironment} or a\n * hand-rolled stub.\n */\n\nimport type { GristDocApi, GristReadyOptions, GristWidgetColumnMap } from \"../../types\"\n\n// ---------------------------------------------------------------------------\n// Public env interface\n// ---------------------------------------------------------------------------\n\nexport type GristEnvironment = {\n /** Wall-clock ms (monotonic preferred but not required). */\n now(): number\n /** Schedule a callback. */\n setTimeout(cb: () => void, ms: number): TimerHandle\n /** Cancel a timer. */\n clearTimeout(handle: TimerHandle): void\n\n /**\n * Probe whether the `grist` global is present in this realm.\n * Returns `{ embedded }` when the global exists; `null` otherwise.\n */\n probeGrist(): { embedded: boolean } | null\n\n /**\n * Snapshot of the `grist` global. Effects use this once detection has\n * succeeded; callers must NOT cache the result across `restart()`.\n */\n getGrist(): GristLike | null\n\n /** Adaptive detection budget hint (ms). `null` if unknown. */\n effectiveTypeHint(): EffectiveTypeHint | null\n\n /** Add a listener for `visibilitychange` / `online` / `focus`. Returns cleanup. */\n onVisibilityChange?(listener: (visible: boolean) => void): () => void\n onOnline?(listener: () => void): () => void\n}\n\nexport type TimerHandle = ReturnType<typeof setTimeout> | number | { __opaque: true }\n\nexport type EffectiveTypeHint = \"slow-2g\" | \"2g\" | \"3g\" | \"4g\" | \"5g\"\n\n/**\n * Subset of `window.grist` actually used by handshake effects. Mirrors the\n * declared shape in `grist-global.d.ts` but only the pieces we need.\n */\nexport type GristLike = {\n ready: (options?: GristReadyOptions) => Promise<void> | void\n docApi: GristDocApi | null | undefined\n onRecord?: (\n cb: (\n record: Record<string, unknown> | null,\n mappings?: GristWidgetColumnMap | null\n ) => void,\n options?: { keepEncoded?: boolean }\n ) => void\n onRecords?: (\n cb: (\n records: Record<string, unknown>[],\n mappings?: GristWidgetColumnMap | null\n ) => void,\n options?: { keepEncoded?: boolean; format?: string }\n ) => void\n onOptions?: (\n cb: (options: Record<string, unknown> | null, interaction: unknown) => void\n ) => void\n onNewRecord?: (cb: (mappings: GristWidgetColumnMap | null) => void) => void\n sectionApi?: {\n mappings: () => Promise<GristWidgetColumnMap | null>\n }\n}\n\n// ---------------------------------------------------------------------------\n// Browser environment factory\n// ---------------------------------------------------------------------------\n\nexport function createBrowserEnvironment(): GristEnvironment {\n return {\n now: () => (typeof performance !== \"undefined\" ? performance.now() : Date.now()),\n setTimeout: (cb, ms) => globalThis.setTimeout(cb, ms),\n clearTimeout: (h) => globalThis.clearTimeout(h as ReturnType<typeof setTimeout>),\n\n probeGrist() {\n if (typeof globalThis === \"undefined\") return null\n const g = (globalThis as { grist?: unknown }).grist\n if (typeof g === \"undefined\") return null\n const embedded =\n typeof window === \"undefined\" ? false : window.self !== window.top\n return { embedded }\n },\n\n getGrist() {\n const g = (globalThis as { grist?: GristLike }).grist\n return g ?? null\n },\n\n effectiveTypeHint() {\n if (typeof navigator === \"undefined\") return null\n const conn = (navigator as { connection?: { effectiveType?: string } }).connection\n const t = conn?.effectiveType\n if (t === \"slow-2g\" || t === \"2g\" || t === \"3g\" || t === \"4g\" || t === \"5g\") return t\n return null\n },\n\n onVisibilityChange(listener) {\n if (typeof document === \"undefined\") return () => {}\n const handler = () => listener(document.visibilityState === \"visible\")\n document.addEventListener(\"visibilitychange\", handler)\n return () => document.removeEventListener(\"visibilitychange\", handler)\n },\n\n onOnline(listener) {\n if (typeof window === \"undefined\") return () => {}\n const handler = () => listener()\n window.addEventListener(\"online\", handler)\n return () => window.removeEventListener(\"online\", handler)\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Test environment factory — fake timers, stubbed grist\n// ---------------------------------------------------------------------------\n\nexport type TestEnvironmentOptions = {\n /**\n * Provider for the `grist` stub. Called every `probeGrist()` / `getGrist()`.\n * Returning `null` simulates \"script not loaded yet\".\n */\n grist?: () => GristLike | null\n embedded?: () => boolean\n effectiveType?: EffectiveTypeHint\n}\n\nexport type TestEnvironment = GristEnvironment & {\n /** Manually advance virtual time. Fires due timers in order. */\n advance(ms: number): void\n /** Currently scheduled timers. */\n pendingTimers(): readonly { dueAt: number; cb: () => void }[]\n /** Trigger visibilitychange listeners. */\n emitVisibility(visible: boolean): void\n /** Trigger online listeners. */\n emitOnline(): void\n}\n\nexport function createTestEnvironment(opts: TestEnvironmentOptions = {}): TestEnvironment {\n let clock = 0\n const timers: Array<{ id: number; dueAt: number; cb: () => void }> = []\n let nextId = 1\n const visListeners = new Set<(v: boolean) => void>()\n const onlineListeners = new Set<() => void>()\n\n function advance(ms: number) {\n const target = clock + ms\n while (true) {\n timers.sort((a, b) => a.dueAt - b.dueAt)\n const due = timers.find((t) => t.dueAt <= target)\n if (!due) break\n timers.splice(timers.indexOf(due), 1)\n clock = due.dueAt\n due.cb()\n }\n clock = target\n }\n\n return {\n now: () => clock,\n setTimeout(cb, ms) {\n const id = nextId++\n timers.push({ id, dueAt: clock + ms, cb })\n return id as unknown as TimerHandle\n },\n clearTimeout(handle) {\n const id = handle as unknown as number\n const idx = timers.findIndex((t) => t.id === id)\n if (idx >= 0) timers.splice(idx, 1)\n },\n\n probeGrist() {\n const g = opts.grist?.() ?? null\n if (!g) return null\n const embedded = opts.embedded?.() ?? true\n return { embedded }\n },\n getGrist: () => opts.grist?.() ?? null,\n effectiveTypeHint: () => opts.effectiveType ?? null,\n\n onVisibilityChange(listener) {\n visListeners.add(listener)\n return () => visListeners.delete(listener)\n },\n onOnline(listener) {\n onlineListeners.add(listener)\n return () => onlineListeners.delete(listener)\n },\n\n advance,\n pendingTimers: () => timers.map((t) => ({ dueAt: t.dueAt, cb: t.cb })),\n emitVisibility(visible) {\n for (const l of visListeners) l(visible)\n },\n emitOnline() {\n for (const l of onlineListeners) l()\n },\n }\n}\n","/**\n * GristHandshakeManager — single owner of the handshake snapshot.\n *\n * Responsibilities\n * ----------------\n * 1. Hold the snapshot and the {@link GristReducerContext}.\n * 2. Run effects (detect → negotiate → subscriptions + mappings) in order.\n * 3. Bump generation on `reload()` / `restart()` and propagate cancellation\n * through a per-generation {@link AbortController}.\n * 4. Notify subscribers on each snapshot change (interface compatible with\n * React's `useSyncExternalStore`).\n *\n * The manager is a **class**, not a hook. React hooks subscribe to it via the\n * standard `subscribe` / `getSnapshot` pair so unit tests can drive the\n * manager directly with a virtual clock — no React needed.\n */\n\nimport {\n DEFAULT_REDUCER_CONFIG,\n createReducerContext,\n gristReducer,\n initialGristSnapshot,\n nextGeneration,\n type GristAction,\n type GristReducerContext,\n type GristWidgetSnapshot,\n type ReducerConfig,\n} from \"./index\"\nimport { startDetectEffect, type DetectHandle, type DetectOptions } from \"./effects/detect\"\nimport {\n startNegotiateEffect,\n type NegotiateHandle,\n type NegotiateOptions,\n} from \"./effects/negotiate\"\nimport {\n defaultSubscriptionsBinder,\n type SubscriptionsBinder,\n type SubscriptionsHandle,\n} from \"./effects/subscriptions\"\nimport {\n startMappingsEffect,\n type MappingsHandle,\n type MappingsOptions,\n} from \"./effects/mappings\"\nimport {\n startHeartbeatEffect,\n type HeartbeatHandle,\n type HeartbeatOptions,\n} from \"./effects/heartbeat\"\nimport { denyAuthzFromCachedOptionsIfInsufficient } from \"./authz-from-options-cache\"\nimport {\n createBrowserEnvironment,\n type GristEnvironment,\n} from \"./environment\"\nimport type { GristColumnsToMap, GristReadyOptions } from \"../../types\"\n\nexport type ManagerOptions = GristReadyOptions & {\n detect?: DetectOptions\n negotiate?: NegotiateOptions\n mappings?: MappingsOptions\n heartbeat?: HeartbeatOptions | false\n reducerConfig?: Partial<ReducerConfig>\n /**\n * Where to read `grist`, timers, and platform hints from. Defaults to a\n * browser adapter. Tests pass {@link createTestEnvironment}.\n */\n env?: GristEnvironment\n /**\n * Binder for grist event streams. Defaults to\n * {@link defaultSubscriptionsBinder} which wires the SDK's singleton\n * {@link \"../../subscriptions\"} module. Tests can pass `noopSubscriptionsBinder`\n * to avoid touching the real `grist` global.\n */\n subscriptionsBinder?: SubscriptionsBinder\n /**\n * Called when the manager catches an unexpected internal error.\n * Telemetry hook — does not affect reducer state.\n */\n onError?: (err: unknown, where: string) => void\n /**\n * Called from inside {@link ManagerControls.reload} BEFORE generation bump.\n * Production callers route `resetGristReadySingleton()` through here so\n * the page-level ready singleton is cleared and the next negotiate effect\n * issues a fresh `grist.ready` instead of re-using the cached promise.\n */\n onBeforeReload?: () => void\n}\n\nexport type ManagerControls = {\n /** Start the effects pipeline. Idempotent after `start()`. */\n start(): void\n /** Cancel all running effects and reset to `idle`. */\n stop(): void\n /**\n * Soft retry: bumps generation, cancels current effects, re-runs detect/\n * negotiate. Subscriptions are NOT re-bound — the underlying singletons\n * survive (and re-deliver cached values).\n */\n reload(): void\n /**\n * Hard reload of the host iframe. Browser-only — in the test env this is a\n * no-op. Production code may also call `window.location.reload()` directly.\n */\n restart(): void\n subscribe(listener: () => void): () => void\n getSnapshot(): GristWidgetSnapshot\n /**\n * Test/internal: inject an action directly. Used by integration tests that\n * want to simulate authz events, link failures, etc.\n */\n dispatch(action: GristAction): void\n /**\n * Coalesce: an external RPC succeeded — tell the heartbeat to skip the\n * next scheduled probe and reset the missed-pings counter. Returns\n * silently when the heartbeat is disabled.\n */\n recordRpcSuccess(): void\n /**\n * Coalesce: an external RPC failed — degrade the link signal immediately.\n * Returns silently when the heartbeat is disabled.\n */\n recordRpcFailure(): void\n}\n\nexport function createGristHandshakeManager(options: ManagerOptions = {}): ManagerControls {\n const env = options.env ?? createBrowserEnvironment()\n const reducerConfig: ReducerConfig = {\n ...DEFAULT_REDUCER_CONFIG,\n ...(options.reducerConfig ?? {}),\n }\n\n let snapshot = initialGristSnapshot()\n const ctx: GristReducerContext = createReducerContext()\n const listeners = new Set<() => void>()\n\n let started = false\n let abort: AbortController | null = null\n let detectHandle: DetectHandle | null = null\n let negotiateHandle: NegotiateHandle | null = null\n let subscriptionsHandle: SubscriptionsHandle | null = null\n let mappingsHandle: MappingsHandle | null = null\n let heartbeatHandle: HeartbeatHandle | null = null\n /** Set by `reload()`; cleared after one post-online cache check. */\n let recheckAuthzFromCacheAfterOnline = false\n\n const declared: GristColumnsToMap = options.columns ?? []\n const subscriptionsBinder = options.subscriptionsBinder ?? defaultSubscriptionsBinder\n\n const notify = () => {\n for (const l of listeners) {\n try {\n l()\n } catch (err) {\n options.onError?.(err, \"listener\")\n }\n }\n }\n\n const dispatch = (action: GristAction) => {\n const prev = snapshot\n const next = gristReducer(prev, action, ctx, reducerConfig)\n if (next !== prev) {\n snapshot = next\n // Run side-effects that depend on phase transitions BEFORE notifying.\n onTransition(prev, next)\n notify()\n }\n }\n\n /**\n * Effect-runner glue: react to lifecycle transitions inside the manager\n * (without polluting the pure reducer).\n *\n * Rationale: the reducer cannot start a `grist.ready()` because that's\n * impure. The reducer instead emits the transition (DETECT_AVAILABLE →\n * `lifecycle.phase === \"negotiating\"`) and the manager observes the\n * transition here to launch the next effect.\n */\n const onTransition = (prev: GristWidgetSnapshot, next: GristWidgetSnapshot) => {\n if (prev.lifecycle.phase !== \"negotiating\" && next.lifecycle.phase === \"negotiating\") {\n launchNegotiate()\n // Bind `grist.onRecord` before `grist.ready` resolves so the host's\n // post-ready cursor/data push is not dropped (iframe transport).\n launchSubscriptions()\n }\n if (prev.lifecycle.phase !== \"online\" && next.lifecycle.phase === \"online\") {\n launchMappings()\n launchHeartbeat()\n if (recheckAuthzFromCacheAfterOnline) {\n recheckAuthzFromCacheAfterOnline = false\n denyAuthzFromCachedOptionsIfInsufficient(next, dispatch, env)\n }\n }\n if (prev.lifecycle.phase === \"online\" && next.lifecycle.phase !== \"online\") {\n // Leaving the online phase — stop probing immediately.\n heartbeatHandle?.cancel()\n heartbeatHandle = null\n }\n }\n\n const launchDetect = () => {\n if (!abort) return\n detectHandle?.cancel()\n detectHandle = startDetectEffect(env, dispatch, abort.signal, options.detect)\n }\n\n const launchNegotiate = () => {\n if (!abort) return\n negotiateHandle?.cancel()\n negotiateHandle = startNegotiateEffect(\n env,\n dispatch,\n abort.signal,\n buildReadyPayload(options),\n options.negotiate\n )\n }\n\n const launchSubscriptions = () => {\n if (!abort) return\n if (subscriptionsHandle) return\n subscriptionsHandle = subscriptionsBinder({\n dispatch,\n getGeneration: () => snapshot.generation,\n onRecordPayload: (m) =>\n mappingsHandle?.ingestStreamPayload(\"stream_record\", m),\n onRecordsPayload: (m) =>\n mappingsHandle?.ingestStreamPayload(\"stream_records\", m),\n onNewRecordPayload: (m) =>\n mappingsHandle?.ingestStreamPayload(\"stream_new_record\", m),\n })\n }\n\n const launchMappings = () => {\n if (!abort) return\n launchSubscriptions()\n if (mappingsHandle) return\n mappingsHandle = startMappingsEffect(\n env,\n dispatch,\n abort.signal,\n declared,\n () => snapshot.generation,\n options.mappings\n )\n }\n\n const launchHeartbeat = () => {\n if (!abort) return\n if (options.heartbeat === false) return\n if (heartbeatHandle) return\n heartbeatHandle = startHeartbeatEffect(\n env,\n dispatch,\n abort.signal,\n options.heartbeat ?? {}\n )\n }\n\n const cancelAllEffects = () => {\n detectHandle?.cancel()\n detectHandle = null\n negotiateHandle?.cancel()\n negotiateHandle = null\n mappingsHandle?.cancel()\n mappingsHandle = null\n heartbeatHandle?.cancel()\n heartbeatHandle = null\n if (abort && !abort.signal.aborted) abort.abort()\n }\n\n const controls: ManagerControls = {\n start() {\n if (started) return\n started = true\n abort = new AbortController()\n launchDetect()\n },\n stop() {\n if (!started) return\n started = false\n cancelAllEffects()\n subscriptionsHandle?.cancel()\n subscriptionsHandle = null\n // Reset snapshot to `idle` so a subsequent `start()` is clean.\n snapshot = initialGristSnapshot()\n notify()\n },\n reload() {\n if (!started) {\n controls.start()\n return\n }\n recheckAuthzFromCacheAfterOnline = true\n try {\n options.onBeforeReload?.()\n } catch (err) {\n options.onError?.(err, \"onBeforeReload\")\n }\n cancelAllEffects()\n // Bump generation so any lingering microtasks / RPC resolutions get dropped.\n dispatch({ type: \"BUMP_GENERATION\", atMs: env.now(), reason: \"reload\" })\n // Reset snapshot to `idle` for the lifecycle restart.\n snapshot = {\n ...initialGristSnapshot(),\n generation: nextGeneration(snapshot.generation),\n }\n ctx.resolver = null\n abort = new AbortController()\n launchDetect()\n notify()\n },\n restart() {\n controls.reload()\n // Hard reload only makes sense in a real browser realm.\n if (typeof window !== \"undefined\" && typeof window.location?.reload === \"function\") {\n try {\n window.location.reload()\n } catch (err) {\n options.onError?.(err, \"restart\")\n }\n }\n },\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n getSnapshot: () => snapshot,\n dispatch,\n recordRpcSuccess() {\n heartbeatHandle?.recordRpcSuccess()\n },\n recordRpcFailure() {\n heartbeatHandle?.recordRpcFailure()\n },\n }\n\n return controls\n}\n\nfunction buildReadyPayload(options: ManagerOptions): GristReadyOptions {\n const payload: GristReadyOptions = {\n requiredAccess: options.requiredAccess ?? \"read table\",\n columns: options.columns ?? [],\n }\n if (options.allowSelectBy !== undefined) payload.allowSelectBy = options.allowSelectBy\n if (options.hasCustomOptions !== undefined) payload.hasCustomOptions = options.hasCustomOptions\n if (options.onEditOptions) payload.onEditOptions = options.onEditOptions\n return payload\n}\n","/**\n * Page-level embedded handshake manager for widgets that call `useGrist()` /\n * `useGristCore()` without mounting `<GristWidgetProvider>`.\n *\n * A single manager is shared across all hook instances on the page (ref-counted)\n * so mid-level hooks (`useGristActions`, `useGristRowsFromTable`, …) do not\n * spawn competing `grist.ready` handshakes.\n */\n\nimport {\n createGristHandshakeManager,\n type ManagerControls,\n type ManagerOptions,\n} from \"./handshake/manager\"\n\ntype EmbeddedSlot = {\n manager: ManagerControls\n refCount: number\n}\n\nlet embedded: EmbeddedSlot | null = null\n\nexport function acquireEmbeddedHandshakeManager(options: ManagerOptions): ManagerControls {\n if (!embedded) {\n embedded = {\n manager: createGristHandshakeManager(options),\n refCount: 0,\n }\n embedded.manager.start()\n }\n embedded.refCount += 1\n return embedded.manager\n}\n\nexport function releaseEmbeddedHandshakeManager(): void {\n if (!embedded) return\n embedded.refCount -= 1\n if (embedded.refCount <= 0) {\n embedded.manager.stop()\n embedded = null\n }\n}\n\n/** Test isolation — tear down the page-level embedded manager between cases. */\nexport function resetEmbeddedHandshakeManagerForTests(): void {\n if (embedded) {\n embedded.manager.stop()\n embedded = null\n }\n}\n","import { useGristCore, type UseGristCoreOptions } from \"../internal/hooks/use-grist-core\"\n\nexport type UseGristAvailabilityOptions = Pick<\n UseGristCoreOptions,\n \"availabilityMaxAttempts\" | \"availabilityIntervalMs\" | \"logWhenNotEmbedded\"\n>\n\n/**\n * Handshake-backed availability — projects `isAvailable` from the FSM.\n */\nexport function useGristAvailability(options?: UseGristAvailabilityOptions): boolean | null {\n const { isAvailable } = useGristCore(options)\n return isAvailable\n}\n","import { useGristCore, type UseGristCoreOptions } from \"../internal/hooks/use-grist-core\"\nimport type { GristDocApi, GristReadyOptions } from \"../types\"\n\nexport type UseGristReadyOptions = GristReadyOptions & {\n availabilityMaxAttempts?: number\n availabilityIntervalMs?: number\n logWhenNotEmbedded?: boolean\n}\n\nexport type UseGristReadyResult = {\n isAvailable: boolean | null\n docApi: GristDocApi | null\n error: string | null\n reload: () => Promise<void>\n}\n\n/**\n * Handshake-backed `grist.ready` — delegates to {@link useGristCore} (FSM).\n *\n * Prefer `<GristWidgetProvider>` + `useGrist()` for full widget state; use this\n * hook when you only need `docApi` and connection status.\n */\nexport function useGristReady(options?: UseGristReadyOptions): UseGristReadyResult {\n const { isAvailable, docApi, error, reload } = useGristCore(options)\n return { isAvailable, docApi, error, reload }\n}\n","/**\n * `useGristHandshake()` — React adapter over {@link GristHandshakeManager}.\n *\n * Exposes the full {@link GristWidgetSnapshot} plus derived\n * {@link GristCapabilities}, plus `reload()` / `restart()` controls.\n *\n * Identity stability\n * ------------------\n * The snapshot reference changes whenever the reducer produces a new\n * snapshot. Capabilities are memoized by snapshot identity, so consumers\n * branching on a single capability (e.g. `canWriteRecords`) avoid extra\n * re-renders unless that boolean actually flips.\n *\n * SSR-safe: `getServerSnapshot` returns the initial snapshot deterministically.\n */\n\nimport { useEffect, useMemo, useRef, useSyncExternalStore } from \"react\"\n\nimport {\n deriveCapabilities,\n deriveErrorMessage,\n deriveStatus,\n initialGristSnapshot,\n type GristCapabilities,\n type GristWidgetSnapshot,\n type GristWidgetStatus,\n} from \"../internal/handshake\"\nimport {\n createGristHandshakeManager,\n type ManagerControls,\n type ManagerOptions,\n} from \"../internal/handshake/manager\"\nimport { ensureGristReady, resetGristReadySingleton } from \"../internal/ready-singleton\"\n\nexport type UseGristHandshakeOptions = ManagerOptions\n\nexport type UseGristHandshakeResult = {\n snapshot: GristWidgetSnapshot\n status: GristWidgetStatus\n error: string | null\n capabilities: GristCapabilities\n reload: () => void\n restart: () => void\n}\n\n/**\n * Mount a handshake manager local to the component subtree. Multiple\n * widgets in the same page should NOT call this in parallel — instead,\n * mount a single `<GristWidgetProvider>` at the app root.\n *\n * In tests, pass `options.env` (see `createTestEnvironment`) to drive the\n * machine with a virtual clock.\n */\nexport function useGristHandshake(options: UseGristHandshakeOptions = {}): UseGristHandshakeResult {\n // Memoize the manager so it survives re-renders. Caller is responsible for\n // passing stable options; in practice this is set once at the provider.\n const optionsRef = useRef(options)\n optionsRef.current = options\n const managerRef = useRef<ManagerControls | null>(null)\n if (managerRef.current === null) {\n // Default to the page-level `ensureGristReady()` singleton so a\n // standalone `useGristHandshake()` (mounted alongside a real\n // `<GristWidgetProvider>` for its observable snapshot, per the \"custom\n // retry / observability\" pattern) can't call the real `grist.ready()`\n // independently and race the widget's own declared options. Callers\n // that pass their own `negotiate`/`onBeforeReload` still win.\n managerRef.current = createGristHandshakeManager({\n ...options,\n negotiate: {\n readyImpl: (payload) => ensureGristReady(payload),\n ...options.negotiate,\n },\n onBeforeReload: () => {\n resetGristReadySingleton()\n options.onBeforeReload?.()\n },\n })\n }\n const manager = managerRef.current\n\n useEffect(() => {\n manager.start()\n return () => {\n manager.stop()\n }\n }, [manager])\n\n const snapshot = useSyncExternalStore(\n manager.subscribe,\n manager.getSnapshot,\n initialGristSnapshot\n )\n\n const projected = useMemo(\n () => ({\n status: deriveStatus(snapshot),\n error: deriveErrorMessage(snapshot),\n capabilities: deriveCapabilities(snapshot),\n }),\n [snapshot]\n )\n\n return useMemo(\n () => ({\n snapshot,\n status: projected.status,\n error: projected.error,\n capabilities: projected.capabilities,\n reload: manager.reload,\n restart: manager.restart,\n }),\n [snapshot, projected, manager]\n )\n}\n\n/**\n * Convenience hook — equivalent to `useGristHandshake().capabilities` but\n * skips returning the full snapshot. Intended for leaf components that only\n * need to gate UI on `canRender` / `canWriteRecords` / etc.\n */\nexport function useGristCapabilities(\n options: UseGristHandshakeOptions = {}\n): GristCapabilities {\n return useGristHandshake(options).capabilities\n}\n","/**\n * `useAmbientGristHandshake()` — observe an already-mounted handshake\n * manager without ever creating one.\n *\n * `useGristHandshake()` / `<GristHandshakeProvider>` both mount their OWN\n * manager, which calls the real `grist.ready()` independently of any\n * `<GristWidgetProvider>` elsewhere on the page. That's fine for a widget's\n * single app-root provider, but wrong for read-only UI (a connection-status\n * chip, a debug overlay) that must share the *same* handshake as the real\n * widget rather than run a second, competing one. This hook only reads\n * whatever manager a `<GristWidgetProvider>` ancestor already published;\n * it returns `null` outside one instead of falling back to creating its own.\n */\n\nimport { useMemo, useSyncExternalStore } from \"react\"\n\nimport {\n deriveCapabilities,\n deriveErrorMessage,\n deriveStatus,\n initialGristSnapshot,\n type GristCapabilities,\n type GristWidgetSnapshot,\n type GristWidgetStatus,\n} from \"../internal/handshake\"\nimport { useGristManagerOptional } from \"../internal/handshake-manager-context\"\n\nexport type UseAmbientGristHandshakeResult = {\n snapshot: GristWidgetSnapshot\n status: GristWidgetStatus\n error: string | null\n capabilities: GristCapabilities\n reload: () => void\n restart: () => void\n}\n\nconst noopSubscribe = () => () => {}\nconst getInitialSnapshot = () => initialGristSnapshot()\n\n/**\n * Returns `null` when mounted outside a `<GristWidgetProvider>` — there is\n * no ambient manager to observe, and this hook intentionally never creates\n * one. Mount inside `<GristWidgetProvider>` to get a live value.\n */\nexport function useAmbientGristHandshake(): UseAmbientGristHandshakeResult | null {\n const manager = useGristManagerOptional()\n\n const snapshot = useSyncExternalStore(\n manager?.subscribe ?? noopSubscribe,\n manager?.getSnapshot ?? getInitialSnapshot,\n getInitialSnapshot\n )\n\n return useMemo(() => {\n if (!manager) return null\n return {\n snapshot,\n status: deriveStatus(snapshot),\n error: deriveErrorMessage(snapshot),\n capabilities: deriveCapabilities(snapshot),\n reload: manager.reload,\n restart: manager.restart,\n }\n }, [manager, snapshot])\n}\n","/**\n * `<GristHandshakeProvider>` — opt-in React provider that mounts a single\n * {@link GristHandshakeManager} at the app root and broadcasts its snapshot\n * to all descendants.\n *\n * Use this provider when you want the full SOTA handshake model (snapshot +\n * capabilities + heartbeat + reload/restart) without the slice composition\n * of `<GristWidgetProvider>`. The two providers are independent:\n *\n * - `<GristWidgetProvider>` (legacy) — full SDK surface, hooks slices.\n * - `<GristHandshakeProvider>` (new) — handshake state machine only.\n *\n * Both can coexist if you need both surfaces; `grist.ready` is deduped by\n * the SDK's ready singleton so concurrent calls are safe.\n */\n\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n type ReactNode,\n} from \"react\"\n\nimport {\n createGristHandshakeManager,\n type ManagerControls,\n type ManagerOptions,\n} from \"../internal/handshake/manager\"\nimport {\n deriveCapabilities,\n deriveErrorMessage,\n deriveStatus,\n initialGristSnapshot,\n type GristCapabilities,\n type GristWidgetSnapshot,\n type GristWidgetStatus,\n} from \"../internal/handshake\"\nimport { ensureGristReady, resetGristReadySingleton } from \"../internal/ready-singleton\"\n\nexport type GristHandshakeContextValue = {\n snapshot: GristWidgetSnapshot\n status: GristWidgetStatus\n error: string | null\n capabilities: GristCapabilities\n reload: () => void\n restart: () => void\n recordRpcSuccess: () => void\n recordRpcFailure: () => void\n}\n\nconst GristHandshakeReactContext = createContext<GristHandshakeContextValue | null>(null)\n\nexport type GristHandshakeProviderProps = {\n options?: ManagerOptions\n children: ReactNode\n}\n\n/**\n * Mount a single {@link GristHandshakeManager} for the React subtree and\n * expose its snapshot via context.\n *\n * The manager is created **once** on first mount; the `options` prop is\n * captured at that time. To re-create the manager with new options, unmount\n * and re-mount the provider (e.g. via a `key`).\n */\nexport function GristHandshakeProvider({ options, children }: GristHandshakeProviderProps) {\n // Manager is allocated once. Subsequent option changes are intentionally\n // ignored — handshake configuration should be stable for the lifetime of\n // the widget.\n const managerRef = useRef<ManagerControls | null>(null)\n if (managerRef.current === null) {\n // Default to the page-level `ensureGristReady()` singleton so a bare\n // `<GristHandshakeProvider>` (no options) can't win a race against a\n // sibling `<GristWidgetProvider>` by calling the real `grist.ready()`\n // directly with its own default payload. Callers that pass their own\n // `negotiate`/`onBeforeReload` still win. (Read-only observers of an\n // ancestor `<GristWidgetProvider>`, like the template's `GristStatusChip`,\n // should prefer `useAmbientGristHandshake()` instead of this provider —\n // it observes the existing manager rather than allocating a new one.)\n managerRef.current = createGristHandshakeManager({\n ...options,\n negotiate: {\n readyImpl: (payload) => ensureGristReady(payload),\n ...options?.negotiate,\n },\n onBeforeReload: () => {\n resetGristReadySingleton()\n options?.onBeforeReload?.()\n },\n })\n }\n const manager = managerRef.current\n\n useEffect(() => {\n manager.start()\n return () => {\n manager.stop()\n }\n }, [manager])\n\n const snapshot = useSyncExternalStore(\n manager.subscribe,\n manager.getSnapshot,\n initialGristSnapshot\n )\n\n const value = useMemo<GristHandshakeContextValue>(\n () => ({\n snapshot,\n status: deriveStatus(snapshot),\n error: deriveErrorMessage(snapshot),\n capabilities: deriveCapabilities(snapshot),\n reload: manager.reload,\n restart: manager.restart,\n recordRpcSuccess: manager.recordRpcSuccess,\n recordRpcFailure: manager.recordRpcFailure,\n }),\n [snapshot, manager]\n )\n\n return (\n <GristHandshakeReactContext.Provider value={value}>\n {children}\n </GristHandshakeReactContext.Provider>\n )\n}\n\n/**\n * Read the handshake context. Throws if no `<GristHandshakeProvider>` is\n * mounted in the tree.\n *\n * Use {@link useGristHandshakeContextOptional} when the provider is optional\n * (e.g. when the same component is sometimes used standalone).\n */\nexport function useGristHandshakeContext(): GristHandshakeContextValue {\n const ctx = useContext(GristHandshakeReactContext)\n if (!ctx) {\n throw new Error(\n \"useGristHandshakeContext() must be used inside <GristHandshakeProvider>\"\n )\n }\n return ctx\n}\n\n/** Non-throwing variant. */\nexport function useGristHandshakeContextOptional(): GristHandshakeContextValue | null {\n return useContext(GristHandshakeReactContext)\n}\n","import { useEffect, useRef, useState } from \"react\"\n\nimport { recordEvent } from \"../internal/subscriptions\"\nimport type { GristReadyOptions, GristWidgetColumnMap } from \"../types\"\nimport { useGristReady } from \"./use-grist-ready\"\n\nexport type UseGristRecordSubscriptionResult = {\n loading: boolean\n error: string | null\n /** Latest mappings from `grist.onRecord` (logical name → real column id, or `string[]` for `allowMultiple`). */\n mappings: GristWidgetColumnMap | null\n}\n\n// Subscription errors are logged inside the singleton; nothing surfaces here.\n\n/**\n * Subscribes to the currently-selected row in the widget's section.\n *\n * The Grist event is shared across all hook instances (no leak).\n */\nexport function useGristRecord(\n cb: (record: Record<string, unknown> | null, mappings: GristWidgetColumnMap | null) => void,\n options?: GristReadyOptions\n): UseGristRecordSubscriptionResult {\n const cbRef = useRef(cb)\n useEffect(() => {\n cbRef.current = cb\n }, [cb])\n\n const [mappings, setMappings] = useState<GristWidgetColumnMap | null>(null)\n const { docApi, error: readyError } = useGristReady({\n requiredAccess: options?.requiredAccess ?? \"read table\",\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n\n useEffect(() => {\n if (!docApi) return\n return recordEvent.subscribe(({ record, mappings: nextMappings }) => {\n setMappings(nextMappings)\n cbRef.current(record, nextMappings)\n })\n }, [docApi])\n\n return {\n loading: docApi === null && !readyError,\n error: readyError,\n mappings,\n }\n}\n","import { useEffect, useRef, useState } from \"react\"\n\nimport { recordsEvent } from \"../internal/subscriptions\"\nimport type { GristReadyOptions, GristWidgetColumnMap } from \"../types\"\nimport { useGristReady } from \"./use-grist-ready\"\nimport type { UseGristRecordSubscriptionResult } from \"./use-grist-record\"\n\n/**\n * Subscribes to the full row set visible in the widget's section\n * (filters, sorts, and linked rows applied).\n */\nexport function useGristRecords(\n cb: (records: Record<string, unknown>[], mappings: GristWidgetColumnMap | null) => void,\n options?: GristReadyOptions\n): UseGristRecordSubscriptionResult {\n const cbRef = useRef(cb)\n useEffect(() => {\n cbRef.current = cb\n }, [cb])\n\n const [mappings, setMappings] = useState<GristWidgetColumnMap | null>(null)\n const { docApi, error: readyError } = useGristReady({\n requiredAccess: options?.requiredAccess ?? \"read table\",\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n\n useEffect(() => {\n if (!docApi) return\n return recordsEvent.subscribe(({ records, mappings: nextMappings }) => {\n setMappings(nextMappings)\n cbRef.current(records, nextMappings)\n })\n }, [docApi])\n\n return {\n loading: docApi === null && !readyError,\n error: readyError,\n mappings,\n }\n}\n","import { useEffect, useRef, useState } from \"react\"\n\nimport { newRecordEvent } from \"../internal/subscriptions\"\nimport type { GristReadyOptions, GristWidgetColumnMap } from \"../types\"\nimport { useGristReady } from \"./use-grist-ready\"\n\nexport type UseGristNewRecordResult = {\n /** True when the user has selected the blank \"new row\" placeholder. */\n isNewRecord: boolean\n /** Mappings reported by Grist alongside the new-record event. */\n mappings: GristWidgetColumnMap | null\n loading: boolean\n error: string | null\n}\n\n/**\n * Subscribes to `grist.onNewRecord` — fires when the user selects the blank\n * row in the linked section. Useful for form-style widgets that need to\n * switch into \"create\" mode.\n *\n * `isNewRecord` is reset to `false` whenever a real `onRecord` payload arrives,\n * so callers don't need to manage that themselves.\n */\nexport function useGristNewRecord(options?: GristReadyOptions): UseGristNewRecordResult {\n const { docApi, error: readyError } = useGristReady({\n requiredAccess: options?.requiredAccess ?? \"read table\",\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n\n const [isNewRecord, setIsNewRecord] = useState(false)\n const [mappings, setMappings] = useState<GristWidgetColumnMap | null>(null)\n const subscribedRef = useRef(false)\n\n useEffect(() => {\n if (!docApi || subscribedRef.current) return\n if (typeof grist.onNewRecord !== \"function\") return\n subscribedRef.current = true\n return newRecordEvent.subscribe(({ mappings: nextMappings }) => {\n setIsNewRecord(true)\n setMappings(nextMappings)\n })\n }, [docApi])\n\n return {\n isNewRecord,\n mappings,\n loading: docApi === null && !readyError,\n error: readyError,\n }\n}\n","import { useCallback, useState } from \"react\"\n\nimport {\n gristAddRecordAction,\n gristBulkAddRecordAction,\n gristBulkRemoveRecordAction,\n gristBulkUpdateRecordAction,\n gristRemoveRecordAction,\n gristUpdateRecordAction,\n type GristUserActionTuple,\n} from \"../helpers/grist-user-actions\"\nimport { useRpcHeartbeatCoalesce } from \"../internal/hooks/use-rpc-heartbeat-coalesce\"\nimport { useGristReady } from \"../low-level-hooks/use-grist-ready\"\nimport type { GristApplyUserActionsOptions, GristReadyOptions } from \"../types\"\n\nexport type GristActionsStatus = \"idle\" | \"running\" | \"error\"\n\nexport type UseGristActionsOptions = Pick<\n GristReadyOptions,\n \"requiredAccess\" | \"columns\" | \"allowSelectBy\" | \"hasCustomOptions\" | \"onEditOptions\"\n>\n\nexport type UseGristActionsResult = {\n status: GristActionsStatus\n error: string | null\n /** Apply raw user actions. `applyOptions.desc` is shown in the document undo stack. */\n apply: (actions: GristUserActionTuple[], applyOptions?: GristApplyUserActionsOptions) => Promise<unknown>\n updateRecord: (\n tableId: string,\n recordId: number,\n patch: Record<string, unknown>,\n applyOptions?: GristApplyUserActionsOptions\n ) => Promise<unknown>\n addRecord: (\n tableId: string,\n values: Record<string, unknown>,\n applyOptions?: GristApplyUserActionsOptions\n ) => Promise<unknown>\n removeRecord: (\n tableId: string,\n recordId: number,\n applyOptions?: GristApplyUserActionsOptions\n ) => Promise<unknown>\n bulkAddRecord: (\n tableId: string,\n values: Record<string, unknown[]>,\n applyOptions?: GristApplyUserActionsOptions\n ) => Promise<unknown>\n bulkUpdateRecord: (\n tableId: string,\n rowIds: number[],\n columnarPatch: Record<string, unknown[]>,\n applyOptions?: GristApplyUserActionsOptions\n ) => Promise<unknown>\n bulkRemoveRecord: (\n tableId: string,\n rowIds: number[],\n applyOptions?: GristApplyUserActionsOptions\n ) => Promise<unknown>\n}\n\nexport function useGristActions(options?: UseGristActionsOptions): UseGristActionsResult {\n const requiredAccess = options?.requiredAccess ?? \"full\"\n const { docApi, error: readyError } = useGristReady({\n requiredAccess,\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n const [status, setStatus] = useState<GristActionsStatus>(\"idle\")\n const [actionError, setActionError] = useState<string | null>(null)\n const coalesceRpc = useRpcHeartbeatCoalesce()\n\n const apply = useCallback(\n async (actions: GristUserActionTuple[], applyOptions?: GristApplyUserActionsOptions): Promise<unknown> => {\n if (!docApi) throw new Error(\"Grist docApi is not ready\")\n setStatus(\"running\")\n setActionError(null)\n try {\n const out = await coalesceRpc(() => docApi.applyUserActions(actions, applyOptions))\n setStatus(\"idle\")\n return out\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n setActionError(message)\n setStatus(\"error\")\n throw err\n }\n },\n [docApi, coalesceRpc]\n )\n\n const updateRecord = useCallback(\n (tableId: string, recordId: number, patch: Record<string, unknown>, applyOptions?: GristApplyUserActionsOptions) =>\n apply([gristUpdateRecordAction(tableId, recordId, patch)], applyOptions),\n [apply]\n )\n\n const addRecord = useCallback(\n (tableId: string, values: Record<string, unknown>, applyOptions?: GristApplyUserActionsOptions) =>\n apply([gristAddRecordAction(tableId, values)], applyOptions),\n [apply]\n )\n\n const removeRecord = useCallback(\n (tableId: string, recordId: number, applyOptions?: GristApplyUserActionsOptions) =>\n apply([gristRemoveRecordAction(tableId, recordId)], applyOptions),\n [apply]\n )\n\n const bulkAddRecord = useCallback(\n (tableId: string, values: Record<string, unknown[]>, applyOptions?: GristApplyUserActionsOptions) =>\n apply([gristBulkAddRecordAction(tableId, values)], applyOptions),\n [apply]\n )\n\n const bulkUpdateRecord = useCallback(\n (tableId: string, rowIds: number[], columnarPatch: Record<string, unknown[]>, applyOptions?: GristApplyUserActionsOptions) =>\n apply([gristBulkUpdateRecordAction(tableId, rowIds, columnarPatch)], applyOptions),\n [apply]\n )\n\n const bulkRemoveRecord = useCallback(\n (tableId: string, rowIds: number[], applyOptions?: GristApplyUserActionsOptions) =>\n apply([gristBulkRemoveRecordAction(tableId, rowIds)], applyOptions),\n [apply]\n )\n\n return {\n status: readyError ? \"error\" : status,\n error: readyError ?? actionError,\n apply,\n updateRecord,\n addRecord,\n removeRecord,\n bulkAddRecord,\n bulkUpdateRecord,\n bulkRemoveRecord,\n }\n}\n","/**\n * Grist `docApi.applyUserActions` payloads are arrays of user-action tuples.\n * @see https://support.getgrist.com/code/interfaces/grist_plugin_api.GristDocAPI/#applyuseractions\n */\n\nexport type GristUserActionTuple = unknown[]\n\n/** `[\"UpdateRecord\", tableId, rowId, { col: value, ... }]` */\nexport function gristUpdateRecordAction(\n tableId: string,\n rowId: number,\n patch: Record<string, unknown>\n): GristUserActionTuple {\n return [\"UpdateRecord\", tableId, rowId, patch]\n}\n\n/** `[\"AddRecord\", tableId, null, { col: value, ... }]` */\nexport function gristAddRecordAction(tableId: string, values: Record<string, unknown>): GristUserActionTuple {\n return [\"AddRecord\", tableId, null, values]\n}\n\n/** `[\"RemoveRecord\", tableId, rowId]` */\nexport function gristRemoveRecordAction(tableId: string, rowId: number): GristUserActionTuple {\n return [\"RemoveRecord\", tableId, rowId]\n}\n\n/** `[\"BulkUpdateRecord\", tableId, rowIds, { col: value[], ... }]` — column arrays must align with `rowIds`. */\nexport function gristBulkUpdateRecordAction(\n tableId: string,\n rowIds: number[],\n columnarPatch: Record<string, unknown[]>\n): GristUserActionTuple {\n return [\"BulkUpdateRecord\", tableId, rowIds, columnarPatch]\n}\n\n/**\n * `[\"BulkAddRecord\", tableId, rowIds | nullArray, { col: value[], ... }]`.\n * Pass `null` (or omit) for `rowIds` to let Grist allocate ids; in user-action\n * format Grist expects an array of nulls of the same length as the value arrays.\n */\nexport function gristBulkAddRecordAction(\n tableId: string,\n values: Record<string, unknown[]>,\n rowIds?: Array<number | null>\n): GristUserActionTuple {\n const length = inferBulkLength(values)\n const ids = rowIds ?? new Array<number | null>(length).fill(null)\n return [\"BulkAddRecord\", tableId, ids, values]\n}\n\n/** `[\"BulkRemoveRecord\", tableId, rowIds]` */\nexport function gristBulkRemoveRecordAction(tableId: string, rowIds: number[]): GristUserActionTuple {\n return [\"BulkRemoveRecord\", tableId, rowIds]\n}\n\n/** `[\"AddColumn\", tableId, colId, colInfo]` — `colInfo` shape depends on Grist version / column type. */\nexport function gristAddColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple {\n return [\"AddColumn\", tableId, colId, colInfo]\n}\n\n/** `[\"AddVisibleColumn\", tableId, colId, colInfo]` — like `AddColumn` but also adds the column to the current view section. */\nexport function gristAddVisibleColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple {\n return [\"AddVisibleColumn\", tableId, colId, colInfo]\n}\n\n/** `[\"RenameColumn\", tableId, colId, newColId]` */\nexport function gristRenameColumnAction(tableId: string, colId: string, newColId: string): GristUserActionTuple {\n return [\"RenameColumn\", tableId, colId, newColId]\n}\n\n/** `[\"ModifyColumn\", tableId, colId, colInfo]` */\nexport function gristModifyColumnAction(tableId: string, colId: string, colInfo: Record<string, unknown>): GristUserActionTuple {\n return [\"ModifyColumn\", tableId, colId, colInfo]\n}\n\n/** `[\"RemoveColumn\", tableId, colId]` */\nexport function gristRemoveColumnAction(tableId: string, colId: string): GristUserActionTuple {\n return [\"RemoveColumn\", tableId, colId]\n}\n\n/** `[\"AddTable\", tableId, tableInfo]` */\nexport function gristAddTableAction(tableId: string, tableInfo: Record<string, unknown>): GristUserActionTuple {\n return [\"AddTable\", tableId, tableInfo]\n}\n\n/** `[\"RenameTable\", tableId, newTableId]` */\nexport function gristRenameTableAction(tableId: string, newTableId: string): GristUserActionTuple {\n return [\"RenameTable\", tableId, newTableId]\n}\n\n/** `[\"RemoveTable\", tableId]` */\nexport function gristRemoveTableAction(tableId: string): GristUserActionTuple {\n return [\"RemoveTable\", tableId]\n}\n\n/** `[\"DuplicateTable\", tableId, newTableId, options?]` */\nexport function gristDuplicateTableAction(\n tableId: string,\n newTableId: string,\n options?: Record<string, unknown>\n): GristUserActionTuple {\n return options ? [\"DuplicateTable\", tableId, newTableId, options] : [\"DuplicateTable\", tableId, newTableId]\n}\n\n/** `[\"ReplaceTableData\", tableId, rowIds, columnarData]` */\nexport function gristReplaceTableDataAction(\n tableId: string,\n rowIds: Array<number | null>,\n columnarData: Record<string, unknown[]>\n): GristUserActionTuple {\n return [\"ReplaceTableData\", tableId, rowIds, columnarData]\n}\n\n/** `[\"SetDocumentInfo\", info]` */\nexport function gristSetDocumentInfoAction(info: Record<string, unknown>): GristUserActionTuple {\n return [\"SetDocumentInfo\", info]\n}\n\nfunction inferBulkLength(values: Record<string, unknown[]>): number {\n for (const arr of Object.values(values)) if (Array.isArray(arr)) return arr.length\n return 0\n}\n","/**\n * `useRpcHeartbeatCoalesce()` — coalesce real RPC traffic with the handshake\n * heartbeat.\n *\n * When a {@link GristHandshakeManager} is mounted (via `<GristWidgetProvider>`\n * or `<GristHandshakeProvider>`), every successful Grist RPC call is treated\n * as a natural heartbeat: the manager's `recordRpcSuccess()` resets the\n * missed-ping counter and pushes the next probe out by a full interval, so\n * a chatty widget never burns dedicated heartbeat probes.\n *\n * Failures are NOT coalesced: semantic errors (validation, \"row not found\",\n * \"no such column\") would briefly flash a connection-degraded indicator even\n * though the channel is alive. Instead we let the regular heartbeat probe\n * detect real transport failures on its own schedule (~30 s). The user\n * already sees the action error immediately, so the heartbeat indicator\n * doesn't need to race.\n *\n * Outside a provider (no manager in context), the wrapper is a zero-cost\n * passthrough — every slice hook keeps working exactly like the pre-FSM\n * release.\n */\n\nimport { useCallback } from \"react\"\n\nimport { useGristManagerOptional } from \"../handshake-manager-context\"\n\nexport type RpcHeartbeatCoalesce = <T>(fn: () => Promise<T>) => Promise<T>\n\nexport function useRpcHeartbeatCoalesce(): RpcHeartbeatCoalesce {\n const manager = useGristManagerOptional()\n return useCallback<RpcHeartbeatCoalesce>(\n async (fn) => {\n try {\n const result = await fn()\n manager?.recordRpcSuccess()\n return result\n } catch (err) {\n throw err\n }\n },\n [manager]\n )\n}\n","import { useCallback, useEffect, useState } from \"react\"\n\nimport { recordEvent } from \"../internal/subscriptions\"\nimport { useGristReady } from \"../low-level-hooks/use-grist-ready\"\nimport type { GristReadyOptions, GristWidgetColumnMap } from \"../types\"\n\nexport type UseGristMappedRecordResult<T> = {\n rawRecord: Record<string, unknown> | null\n mappedRecord: T | null\n /** Current column mappings reported by Grist. */\n mappings: GristWidgetColumnMap | null\n loading: boolean\n error: string | null\n /**\n * Reverse-map a logical patch (`{ logicalName: value }`) into a real-column\n * patch (`{ realColId: value }`) ready for `applyUserActions` /\n * `useGristTableOps.update`.\n * Falls back to `grist.mapColumnNamesBack` when present, otherwise resolves\n * names manually from `mappings` (single-mapped columns only).\n */\n mapBack: (patch: Partial<T>) => Record<string, unknown>\n /** Resolve a single logical name to its current real column id. */\n resolveColumnId: (logicalName: keyof T & string) => string | undefined\n}\n\nfunction manualMapBack<T>(patch: Partial<T>, mappings: GristWidgetColumnMap | null): Record<string, unknown> {\n if (!mappings) return { ...(patch as Record<string, unknown>) }\n const out: Record<string, unknown> = {}\n for (const [name, value] of Object.entries(patch as Record<string, unknown>)) {\n const target = mappings[name]\n if (Array.isArray(target)) {\n // allowMultiple: cannot decide which underlying column to write — skip and warn.\n if (typeof console !== \"undefined\") {\n console.warn(`[lib/grist] mapBack: cannot reverse-map allowMultiple field \"${name}\" without grist.mapColumnNamesBack`)\n }\n continue\n }\n out[target ?? name] = value\n }\n return out\n}\n\n/**\n * Subscribes to the selected row, applies `grist.mapColumnNames` so widget code\n * sees logical column names, and exposes `mapBack` for safe writes.\n */\nexport function useGristMappedRecord<T = Record<string, unknown>>(\n options?: GristReadyOptions\n): UseGristMappedRecordResult<T> {\n const requiredAccess = options?.requiredAccess ?? \"read table\"\n const { docApi, error: readyError } = useGristReady({\n requiredAccess,\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n const [rawRecord, setRawRecord] = useState<Record<string, unknown> | null>(null)\n const [mappedRecord, setMappedRecord] = useState<T | null>(null)\n const [mappings, setMappings] = useState<GristWidgetColumnMap | null>(null)\n\n useEffect(() => {\n if (!docApi) return\n return recordEvent.subscribe(({ record, mappings: nextMappings }) => {\n setRawRecord(record)\n setMappings(nextMappings)\n if (!record) {\n setMappedRecord(null)\n return\n }\n if (typeof grist.mapColumnNames === \"function\") {\n const mapped = grist.mapColumnNames(record)\n setMappedRecord(\n mapped !== null && mapped !== undefined ? (mapped as T) : null\n )\n return\n }\n setMappedRecord(record as T)\n })\n }, [docApi])\n\n const mapBack = useCallback(\n (patch: Partial<T>): Record<string, unknown> => {\n if (typeof grist.mapColumnNamesBack === \"function\") {\n return grist.mapColumnNamesBack(patch as Record<string, unknown>)\n }\n return manualMapBack<T>(patch, mappings)\n },\n [mappings]\n )\n\n const resolveColumnId = useCallback(\n (logicalName: keyof T & string): string | undefined => {\n const value = mappings?.[logicalName]\n if (Array.isArray(value)) return undefined\n return value ?? undefined\n },\n [mappings]\n )\n\n return {\n rawRecord,\n mappedRecord,\n mappings,\n loading: docApi === null && !readyError,\n error: readyError,\n mapBack,\n resolveColumnId,\n }\n}\n","import { useCallback, useState } from \"react\"\n\nimport { useRpcHeartbeatCoalesce } from \"../internal/hooks/use-rpc-heartbeat-coalesce\"\nimport { useGristReady } from \"../low-level-hooks/use-grist-ready\"\nimport type {\n GristMinimalRecord,\n GristReadyOptions,\n GristTableNewRecord,\n GristTableOpOptions,\n GristTableOperations,\n GristTableUpdateRecord,\n GristTableUpsertOptions,\n GristTableUpsertRecord,\n} from \"../types\"\n\nexport type GristTableOpsStatus = \"idle\" | \"running\" | \"error\"\n\nexport type UseGristTableOpsOptions = {\n /**\n * Table to operate on:\n * - `\"selected\"` (default): use `grist.selectedTable` (the widget's current table).\n * - any other string: use `grist.getTable(tableId)`.\n */\n tableId?: \"selected\" | string\n /** Defaults to `\"full\"` since these operations write to the document. */\n requiredAccess?: GristReadyOptions[\"requiredAccess\"]\n} & Pick<GristReadyOptions, \"columns\" | \"allowSelectBy\" | \"hasCustomOptions\" | \"onEditOptions\">\n\nexport type UseGristTableOpsResult = {\n status: GristTableOpsStatus\n error: string | null\n /** Resolves the runtime tableId (only meaningful when `tableId` is `\"selected\"`). */\n getTableId: () => Promise<string>\n create: (\n records: GristTableNewRecord | GristTableNewRecord[],\n opOptions?: GristTableOpOptions\n ) => Promise<GristMinimalRecord | GristMinimalRecord[]>\n update: (\n records: GristTableUpdateRecord | GristTableUpdateRecord[],\n opOptions?: GristTableOpOptions\n ) => Promise<void>\n upsert: (\n records: GristTableUpsertRecord | GristTableUpsertRecord[],\n opOptions?: GristTableUpsertOptions\n ) => Promise<void>\n destroy: (recordIds: number | number[]) => Promise<void>\n}\n\nfunction resolveOps(tableId: \"selected\" | string): GristTableOperations {\n if (tableId === \"selected\") {\n if (!grist.selectedTable) throw new Error(\"grist.selectedTable is not available\")\n return grist.selectedTable\n }\n if (typeof grist.getTable !== \"function\") {\n throw new Error(\"grist.getTable is not available in this Grist version\")\n }\n return grist.getTable(tableId)\n}\n\n/**\n * Strongly-typed CRUD wrapper around Grist's `TableOperations` (`grist.selectedTable`\n * or `grist.getTable(tableId)`). Prefer this over hand-rolled `applyUserActions`.\n *\n * @see https://support.getgrist.com/code/interfaces/TableOperations.TableOperations/\n */\nexport function useGristTableOps(options?: UseGristTableOpsOptions): UseGristTableOpsResult {\n const requiredAccess = options?.requiredAccess ?? \"full\"\n const tableIdOption: \"selected\" | string = options?.tableId ?? \"selected\"\n const { docApi, error: readyError } = useGristReady({\n requiredAccess,\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n\n const [status, setStatus] = useState<GristTableOpsStatus>(\"idle\")\n const [actionError, setActionError] = useState<string | null>(null)\n const coalesceRpc = useRpcHeartbeatCoalesce()\n\n // `resolveOps` is synchronous and idempotent (it only reads `grist.selectedTable`\n // / `grist.getTable`), so derive `ops` during render instead of via effect.\n let ops: GristTableOperations | null = null\n let resolveError: string | null = null\n if (docApi) {\n try {\n ops = resolveOps(tableIdOption)\n } catch (err) {\n resolveError = err instanceof Error ? err.message : String(err)\n }\n }\n\n const wrap = useCallback(\n async <T>(run: (ops: GristTableOperations) => Promise<T>): Promise<T> => {\n if (!ops) throw new Error(\"Grist table operations are not ready\")\n setStatus(\"running\")\n setActionError(null)\n try {\n const out = await coalesceRpc(() => run(ops))\n setStatus(\"idle\")\n return out\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n setActionError(message)\n setStatus(\"error\")\n throw err\n }\n },\n [ops, coalesceRpc]\n )\n\n const getTableId = useCallback(async () => {\n if (!ops) throw new Error(\"Grist table operations are not ready\")\n return ops.getTableId()\n }, [ops])\n\n const create = useCallback(\n (records: GristTableNewRecord | GristTableNewRecord[], opOptions?: GristTableOpOptions) =>\n wrap((o) => o.create(records, opOptions)),\n [wrap]\n )\n\n const update = useCallback(\n (records: GristTableUpdateRecord | GristTableUpdateRecord[], opOptions?: GristTableOpOptions) =>\n wrap((o) => o.update(records, opOptions)),\n [wrap]\n )\n\n const upsert = useCallback(\n (records: GristTableUpsertRecord | GristTableUpsertRecord[], opOptions?: GristTableUpsertOptions) =>\n wrap((o) => o.upsert(records, opOptions)),\n [wrap]\n )\n\n const destroy = useCallback(\n (recordIds: number | number[]) => wrap((o) => o.destroy(recordIds)),\n [wrap]\n )\n\n return {\n status: readyError || resolveError ? \"error\" : status,\n error: readyError ?? resolveError ?? actionError,\n getTableId,\n create,\n update,\n upsert,\n destroy,\n }\n}\n","import { useCallback, useContext, useEffect, useRef, useState } from \"react\"\n\nimport { tableDataToRows, type GristColumnarTable } from \"../helpers/grist-table-data\"\nimport { GristReadsContext } from \"../internal/contexts\"\nimport { useRpcHeartbeatCoalesce } from \"../internal/hooks/use-rpc-heartbeat-coalesce\"\nimport { useGristReady } from \"../low-level-hooks/use-grist-ready\"\nimport type { GristReadyOptions, GristRowRecord } from \"../types\"\n\nexport type UseGristRowsFromTableOptions<T> = {\n tableId: string\n /** Re-fetch on a fixed cadence (ms). Default: disabled. */\n pollIntervalMs?: number\n /** Map raw rows to a domain shape (e.g. `decodeGristValue` your columns). */\n transform?: (row: GristRowRecord) => GristRowRecord<T>\n requiredAccess?: GristReadyOptions[\"requiredAccess\"]\n} & Pick<GristReadyOptions, \"columns\" | \"allowSelectBy\" | \"hasCustomOptions\" | \"onEditOptions\">\n\nexport type UseGristRowsFromTableResult<T> = {\n rows: GristRowRecord<T>[] | null\n loading: boolean\n error: string | null\n /** Force a re-fetch (call after writes that should be reflected immediately). */\n refresh: () => Promise<void>\n}\n\n/**\n * Subscribes to a full table's rows via `docApi.fetchTable`.\n *\n * Unlike `useGristRecords` (which is bound to the widget's selected section),\n * this hook works against any table id and **doesn't auto-update on Grist\n * writes from elsewhere** — call `refresh()` after your own writes, or set\n * `pollIntervalMs` for polling.\n */\nexport function useGristRowsFromTable<T = Record<string, unknown>>(\n options: UseGristRowsFromTableOptions<T>\n): UseGristRowsFromTableResult<T> {\n const requiredAccess = options.requiredAccess ?? \"read table\"\n const { docApi, error: readyError } = useGristReady({\n requiredAccess,\n columns: options.columns,\n allowSelectBy: options.allowSelectBy,\n hasCustomOptions: options.hasCustomOptions,\n onEditOptions: options.onEditOptions,\n })\n\n // When inside a GristWidgetProvider, delegate to the provider's guarded\n // fetchTable so access-insufficient errors flow through readError → alerts.\n const providerFetchTable = useContext(GristReadsContext)?.fetchTable\n\n const [rows, setRows] = useState<GristRowRecord<T>[] | null>(null)\n const [loading, setLoading] = useState(false)\n const [fetchError, setFetchError] = useState<string | null>(null)\n const transformRef = useRef(options.transform)\n transformRef.current = options.transform\n const coalesceRpc = useRpcHeartbeatCoalesce()\n\n const refresh = useCallback(async () => {\n if (!docApi) return\n setLoading(true)\n setFetchError(null)\n try {\n const data = providerFetchTable\n ? await providerFetchTable(options.tableId)\n : ((await coalesceRpc(\n () => docApi.fetchTable(options.tableId) as Promise<GristColumnarTable>\n )) as GristColumnarTable)\n const raw = tableDataToRows(data)\n const t = transformRef.current\n setRows(t ? raw.map((r) => t(r)) : (raw as GristRowRecord<T>[]))\n } catch (err) {\n setFetchError(err instanceof Error ? err.message : String(err))\n setRows(null)\n } finally {\n setLoading(false)\n }\n }, [docApi, providerFetchTable, options.tableId, coalesceRpc])\n\n useEffect(() => {\n if (docApi) void refresh()\n }, [docApi, refresh])\n\n useEffect(() => {\n if (!docApi || !options.pollIntervalMs || options.pollIntervalMs <= 0) return\n const id = window.setInterval(() => void refresh(), options.pollIntervalMs)\n return () => window.clearInterval(id)\n }, [docApi, options.pollIntervalMs, refresh])\n\n return {\n rows,\n loading,\n error: readyError ?? fetchError,\n refresh,\n }\n}\n","import type { GristDocApi, GristReplicaColumn, GristRowRecord } from \"../types\"\nimport { decodeGristValue } from \"./grist-value-codec\"\nimport {\n type GristSafeParseCellResult,\n type GristSafeTableCellMeta,\n safeParseGristTableData,\n type GristSafeTableCellIssue,\n type SafeParseGristTableDataOptions,\n} from \"./grist-safe-parse\"\n\n/**\n * Column-oriented table payload from `grist.docApi.fetchTable`.\n */\nexport type GristColumnarTable = Record<string, unknown[]>\nexport type GristTableRowsOrder = \"none\" | \"id\" | \"grist\"\nexport type FetchGristTableRowsOptions = {\n /** Optional schema metadata map to decode cells (except `id`) with `decodeGristValue`. */\n columns?: Record<string, GristReplicaColumn>\n /**\n * Row ordering strategy.\n * - `grist` (default): `manualSort` ascending when present, fallback `id`.\n * - `id`: numeric `id` ascending.\n * - `none`: preserve raw `fetchTable` row order.\n */\n order?: GristTableRowsOrder\n /**\n * Enable safe parsing with issue reporting.\n * - `true`: use safe parsing with defaults.\n * - object: pass safe-parse options (`displayHelpersByRef`, `refMode`, ...).\n */\n safeParse?: boolean | Omit<SafeParseGristTableDataOptions, \"columns\" | \"order\">\n /** Optional callback fired with per-cell parse issues when `safeParse` is enabled. */\n onSafeParseIssues?: (issues: GristSafeTableCellIssue[]) => void\n /** Optional callback fired with enriched per-cell safe-parse metadata. */\n onSafeParseCellMeta?: (meta: GristSafeTableCellMeta[]) => void\n /** Optional callback fired with keyed per-cell metadata (`${rowId}:${colId}`). */\n onSafeParseCellMetaByKey?: (metaByKey: Record<string, GristSafeTableCellMeta>) => void\n}\n\nfunction inferDisplayHelpersByNameFromData(data: GristColumnarTable): Record<string, string> {\n const out: Record<string, string> = {}\n for (const colId of Object.keys(data)) {\n if (!colId.startsWith(\"gristHelper_\")) continue\n const baseColId = colId.slice(\"gristHelper_\".length)\n if (!baseColId) continue\n if (!Array.isArray(data[baseColId])) continue\n out[baseColId] = colId\n }\n return out\n}\n\nfunction enrichSafeParseColumnsFromData(\n data: GristColumnarTable,\n columns?: Record<string, GristReplicaColumn>\n): Record<string, GristReplicaColumn> {\n const out: Record<string, GristReplicaColumn> = { ...(columns ?? {}) }\n const keys = Object.keys(data)\n const positionByColId = new Map<string, number>(keys.map((colId, index) => [colId, index + 1]))\n\n // Ensure helper columns exist in safe-parse metadata even when caller schema\n // excluded them (common for UI schemas). Parent positions are synthesized from\n // fetchTable key order so helper/ref fallback matching still works.\n for (const colId of keys) {\n if (!colId.startsWith(\"gristHelper_\")) continue\n if (out[colId]) continue\n out[colId] = {\n type: \"Any\",\n label: colId,\n parentPos: positionByColId.get(colId),\n }\n }\n return out\n}\n\nfunction rowsFromSafeCellResults(\n parsedByColumn: Record<string, GristSafeParseCellResult[]>\n): GristRowRecord<Record<string, unknown>>[] {\n const colIds = Object.keys(parsedByColumn)\n const rowCount = colIds.reduce((max, colId) => {\n const len = parsedByColumn[colId]?.length ?? 0\n return Math.max(max, len)\n }, 0)\n const rows: GristRowRecord<Record<string, unknown>>[] = []\n for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {\n const row: Record<string, unknown> = {}\n for (const colId of colIds) {\n row[colId] = parsedByColumn[colId]?.[rowIndex] ?? null\n }\n rows.push(row as GristRowRecord<Record<string, unknown>>)\n }\n return rows\n}\n\nfunction safeCellRowId(row: Record<string, unknown>): number | null {\n const idCell = row.id\n if (!idCell || typeof idCell !== \"object\" || !(\"typedValue\" in idCell)) return null\n const typed = (idCell as { typedValue?: unknown }).typedValue\n return typeof typed === \"number\" && Number.isFinite(typed) ? typed : null\n}\n\nfunction orderSafeCellRowsByParsedRows(\n rows: GristRowRecord<Record<string, unknown>>[],\n orderedParsedRows: ReadonlyArray<Record<string, unknown>>\n): GristRowRecord<Record<string, unknown>>[] {\n const byId = new Map<number, GristRowRecord<Record<string, unknown>>[]>()\n for (const row of rows) {\n const id = safeCellRowId(row)\n if (id === null) continue\n const bucket = byId.get(id)\n if (bucket) bucket.push(row)\n else byId.set(id, [row])\n }\n const out: GristRowRecord<Record<string, unknown>>[] = []\n for (const parsedRow of orderedParsedRows) {\n const id = typeof parsedRow.id === \"number\" ? parsedRow.id : null\n if (id === null) continue\n const bucket = byId.get(id)\n if (!bucket || bucket.length === 0) continue\n const next = bucket.shift()\n if (next) out.push(next)\n }\n // Preserve any unexpected leftovers (defensive).\n for (const bucket of byId.values()) {\n out.push(...bucket)\n }\n return out\n}\n\n/**\n * Project safe-parse rows to UI display rows by taking each cell's `displayValue`.\n * Useful for grids that render human-friendly values while keeping safe-cell\n * objects available for logic and metadata.\n */\nexport function safeParseRowsToDisplayRows(\n rows: ReadonlyArray<Record<string, unknown>>\n): GristRowRecord<Record<string, unknown>>[] {\n return rows.map((row) => {\n const out: Record<string, unknown> = {}\n for (const [colId, cell] of Object.entries(row)) {\n if (cell && typeof cell === \"object\" && \"displayValue\" in cell) {\n out[colId] = (cell as { displayValue: unknown }).displayValue\n } else {\n out[colId] = cell\n }\n }\n return out as GristRowRecord<Record<string, unknown>>\n })\n}\n\n/** Row count from columnar `fetchTable` data (prefers `id` column length). */\nexport function computeRowCount(data: Record<string, unknown[]>): number {\n const idCol = data.id\n if (Array.isArray(idCol)) return idCol.length\n for (const key of Object.keys(data)) {\n const col = data[key]\n if (Array.isArray(col)) return col.length\n }\n return 0\n}\n\n/**\n * Decode columnar `fetchTable` into replica row objects (same convention as the playground grid).\n */\nexport function decodeColumnarToReplicaRows(\n data: GristColumnarTable,\n columnIds: string[],\n columns: Record<string, GristReplicaColumn>,\n maxRows?: number\n): GristRowRecord[] {\n const nFull = computeRowCount(data)\n const n = maxRows !== undefined ? Math.min(nFull, maxRows) : nFull\n const out: GristRowRecord[] = []\n for (let i = 0; i < n; i++) {\n const row: Record<string, unknown> = {}\n for (const k of columnIds) {\n const arr = data[k]\n const raw = Array.isArray(arr) ? arr[i] : undefined\n if (k === \"id\") {\n row[k] = raw\n } else {\n row[k] = decodeGristValue(raw, columns[k]) as unknown\n }\n }\n out.push(row as GristRowRecord)\n }\n return out\n}\n\n/** Turn `fetchTable` output into row objects (order follows the `id` column). */\nexport function tableDataToRows<T = Record<string, unknown>>(\n data: GristColumnarTable\n): GristRowRecord<T>[] {\n const idCol = data.id\n if (!Array.isArray(idCol) || idCol.length === 0) return []\n const keys = Object.keys(data)\n const n = idCol.length\n const rows: GristRowRecord<T>[] = []\n for (let i = 0; i < n; i++) {\n const row: Record<string, unknown> = {}\n for (const k of keys) {\n const col = data[k]\n row[k] = Array.isArray(col) ? col[i] : undefined\n }\n rows.push(row as GristRowRecord<T>)\n }\n return rows\n}\n\nfunction manualSortKey(row: Record<string, unknown>): number | null {\n const v = row.manualSort\n if (typeof v === \"number\" && !Number.isNaN(v)) return v\n if (typeof v === \"string\" && v.trim() !== \"\") {\n const n = Number(v)\n if (!Number.isNaN(n)) return n\n }\n return null\n}\n\n/**\n * Sort rows by Grist's display order: `manualSort` ascending when present on\n * any row, otherwise numeric `id` ascending.\n */\nexport function sortRowsByGristOrder<T = Record<string, unknown>>(\n rows: readonly GristRowRecord<T>[]\n): GristRowRecord<T>[] {\n const hasManual = rows.some((r) => manualSortKey(r as Record<string, unknown>) !== null)\n if (!hasManual) {\n return [...rows].sort((a, b) => {\n const ida = typeof a.id === \"number\" ? a.id : 0\n const idb = typeof b.id === \"number\" ? b.id : 0\n return ida - idb\n })\n }\n return [...rows].sort((a, b) => {\n const ma = manualSortKey(a as Record<string, unknown>)\n const mb = manualSortKey(b as Record<string, unknown>)\n if (ma !== null && mb !== null && ma !== mb) return ma - mb\n if (ma !== null && mb === null) return -1\n if (ma === null && mb !== null) return 1\n const ida = typeof a.id === \"number\" ? a.id : 0\n const idb = typeof b.id === \"number\" ? b.id : 0\n return ida - idb\n })\n}\n\n/** Row index where `id` matches `rowId`, or `null`. */\nexport function findRowIndexById(data: GristColumnarTable, rowId: number | string): number | null {\n const idCol = data.id\n if (!Array.isArray(idCol)) return null\n const idx = idCol.findIndex((v) => v === rowId || (typeof v === \"number\" && v === Number(rowId)))\n return idx === -1 ? null : idx\n}\n\n/** Single row at `index`, or `null` if out of range. */\nexport function rowAtIndex<T = Record<string, unknown>>(\n data: GristColumnarTable,\n index: number\n): GristRowRecord<T> | null {\n const idCol = data.id\n if (!Array.isArray(idCol) || index < 0 || index >= idCol.length) return null\n const keys = Object.keys(data)\n const row: Record<string, unknown> = {}\n for (const k of keys) {\n const col = data[k]\n row[k] = Array.isArray(col) ? col[index] : undefined\n }\n return row as GristRowRecord<T>\n}\n\n/**\n * Fetch a single row from any table by id. Convenience around\n * `docApi.fetchTable` + {@link findRowIndexById} + {@link rowAtIndex}.\n */\nexport async function fetchGristRow<T = Record<string, unknown>>(\n docApi: GristDocApi,\n tableId: string,\n rowId: number\n): Promise<GristRowRecord<T> | null> {\n const data = (await docApi.fetchTable(tableId)) as GristColumnarTable\n const index = findRowIndexById(data, rowId)\n if (index === null) return null\n return rowAtIndex<T>(data, index)\n}\n\n/**\n * Same row materialization as {@link fetchGristTableRows} after `fetchTable`\n * returns columnar data. Hosts that already have columnar data (e.g. the\n * emulator building from in-memory rows) can use this and skip `docApi.fetchTable`.\n */\nexport function materializeGristRowsFromColumnar<T extends Record<string, unknown> = Record<string, unknown>>(\n data: GristColumnarTable,\n options?: FetchGristTableRowsOptions\n): GristRowRecord<T>[] {\n const order = options?.order ?? \"grist\"\n if (options?.safeParse) {\n const safeOptions = options.safeParse === true ? {} : options.safeParse\n const columnsForSafeParse = enrichSafeParseColumnsFromData(data, options?.columns)\n const displayHelpersByRef = {\n ...inferDisplayHelpersByNameFromData(data),\n ...(safeOptions.displayHelpersByRef ?? {}),\n }\n const parsed = safeParseGristTableData<T>(data, {\n ...safeOptions,\n columns: columnsForSafeParse,\n displayHelpersByRef,\n order,\n })\n options.onSafeParseIssues?.(parsed.cellIssues)\n options.onSafeParseCellMeta?.(parsed.cellMeta)\n options.onSafeParseCellMetaByKey?.(parsed.cellMetaByKey)\n const rawSafeRows = rowsFromSafeCellResults(parsed.cellResultsByColumn)\n const orderedSafeRows =\n order === \"none\"\n ? rawSafeRows\n : orderSafeCellRowsByParsedRows(rawSafeRows, parsed.rows as ReadonlyArray<Record<string, unknown>>)\n return orderedSafeRows as GristRowRecord<T>[]\n }\n\n let rows: GristRowRecord<T>[]\n if (options?.columns) {\n const columnIds = Object.keys(data)\n rows = decodeColumnarToReplicaRows(data, columnIds, options.columns) as GristRowRecord<T>[]\n } else {\n rows = tableDataToRows<T>(data)\n }\n\n if (order === \"none\") return rows\n if (order === \"id\") {\n return [...rows].sort((a, b) => {\n const ida = typeof a.id === \"number\" ? a.id : 0\n const idb = typeof b.id === \"number\" ? b.id : 0\n return ida - idb\n })\n }\n return sortRowsByGristOrder(rows)\n}\n\n/**\n * Fetch a table and return rows as objects, optionally decoded from schema and sorted.\n */\nexport async function fetchGristTableRows<T extends Record<string, unknown> = Record<string, unknown>>(\n docApi: GristDocApi,\n tableId: string,\n options: FetchGristTableRowsOptions & { safeParse: true | Omit<SafeParseGristTableDataOptions, \"columns\" | \"order\"> }\n): Promise<GristRowRecord<{ [K in keyof T]: GristSafeParseCellResult<T[K]> | null }>[]>\nexport async function fetchGristTableRows<T extends Record<string, unknown> = Record<string, unknown>>(\n docApi: GristDocApi,\n tableId: string,\n options?: FetchGristTableRowsOptions\n): Promise<GristRowRecord<T>[]>\nexport async function fetchGristTableRows<T extends Record<string, unknown> = Record<string, unknown>>(\n docApi: GristDocApi,\n tableId: string,\n options?: FetchGristTableRowsOptions\n): Promise<GristRowRecord<T>[]> {\n const data = (await docApi.fetchTable(tableId)) as GristColumnarTable\n return materializeGristRowsFromColumnar<T>(data, options)\n}\n","import { createContext, useContext, useMemo, type Context } from \"react\"\n\nimport type { ComposedGristWidgetSlices } from \"./compose-grist-widget\"\nimport { mergeGristResultFromSlices } from \"./merge-grist-result\"\nimport type { UseGristResult } from \"../use-grist-types\"\nimport type { UseGristAttachmentsRestResult } from \"./hooks/use-grist-attachments-rest\"\nimport type { UseGristCoreResult } from \"./hooks/use-grist-core\"\nimport type { UseGristCurrentTableResult } from \"./hooks/use-grist-current-table\"\nimport type { UseGristLinkingResult } from \"./hooks/use-grist-linking\"\nimport type { UseGristReadsResult } from \"./hooks/use-grist-reads\"\nimport type { UseGristSectionApiResult } from \"./hooks/use-grist-section-api\"\nimport type { UseGristSelectionResult } from \"./hooks/use-grist-selection\"\nimport type { UseGristTableOpsBoundResult } from \"./hooks/use-grist-table-ops-bound\"\nimport type { UseGristThemeResult } from \"./hooks/use-grist-theme\"\nimport type { UseGristWidgetOptionsApiResult } from \"./hooks/use-grist-widget-options-api\"\n\nexport type GristCoreContextValue = UseGristCoreResult & {\n tableError: string | null\n}\n\nexport type GristStatusSliceValue = GristCoreContextValue & UseGristCurrentTableResult\n\nexport const GristCoreContext = createContext<GristCoreContextValue | null>(null)\nexport const GristSelectionContext = createContext<UseGristSelectionResult | null>(null)\nexport const GristCurrentTableContext = createContext<UseGristCurrentTableResult | null>(null)\nexport const GristWritesContext = createContext<UseGristTableOpsBoundResult | null>(null)\nexport const GristReadsContext = createContext<UseGristReadsResult | null>(null)\nexport const GristWidgetOptionsApiContext = createContext<UseGristWidgetOptionsApiResult | null>(null)\nexport const GristLinkingContext = createContext<UseGristLinkingResult | null>(null)\nexport const GristAttachmentsRestContext = createContext<UseGristAttachmentsRestResult | null>(null)\nexport const GristThemeContext = createContext<UseGristThemeResult | null>(null)\nexport const GristSectionApiContext = createContext<UseGristSectionApiResult | null>(null)\n\nfunction useRequiredContext<T>(ctx: Context<T | null>, name: string): T {\n const value = useContext(ctx)\n if (!value) {\n throw new Error(`${name} must be used inside <GristWidgetProvider>`)\n }\n return value\n}\n\nexport function useGristCoreContext(): GristCoreContextValue {\n return useRequiredContext(GristCoreContext, \"useGristCoreContext()\")\n}\n\nexport function useGristSelectionContext<\n TRow = Record<string, unknown>,\n TMapped = Record<string, unknown>,\n>(): UseGristSelectionResult<TRow, TMapped> {\n return useRequiredContext(GristSelectionContext, \"useGristSelectionContext()\") as UseGristSelectionResult<\n TRow,\n TMapped\n >\n}\n\nexport function useGristCurrentTableContext(): UseGristCurrentTableResult {\n return useRequiredContext(GristCurrentTableContext, \"useGristCurrentTableContext()\")\n}\n\nexport function useGristWritesContext(): UseGristTableOpsBoundResult {\n return useRequiredContext(GristWritesContext, \"useGristWritesContext()\")\n}\n\nexport function useGristReadsContext(): UseGristReadsResult {\n return useRequiredContext(GristReadsContext, \"useGristReadsContext()\")\n}\n\nexport function useGristWidgetOptionsApiContext(): UseGristWidgetOptionsApiResult {\n return useRequiredContext(GristWidgetOptionsApiContext, \"useGristWidgetOptionsApiContext()\")\n}\n\nexport function useGristLinkingContext(): UseGristLinkingResult {\n return useRequiredContext(GristLinkingContext, \"useGristLinkingContext()\")\n}\n\nexport function useGristAttachmentsRestContext(): UseGristAttachmentsRestResult {\n return useRequiredContext(GristAttachmentsRestContext, \"useGristAttachmentsRestContext()\")\n}\n\nexport function useGristThemeContext(): UseGristThemeResult {\n return useRequiredContext(GristThemeContext, \"useGristThemeContext()\")\n}\n\nexport function useGristSectionApiContext(): UseGristSectionApiResult {\n return useRequiredContext(GristSectionApiContext, \"useGristSectionApiContext()\")\n}\n\n/**\n * Live merge of all provider slices — used by `useGrist()` / `useGristContext()` so\n * cursor-driven `onRecord` updates are not lost behind a stale memoized snapshot.\n */\nexport function useGristFromProvider<\n TRow = Record<string, unknown>,\n TMapped = Record<string, unknown>,\n>(): UseGristResult<TRow, TMapped> {\n const core = useGristCoreContext()\n const selection = useGristSelectionContext<TRow, TMapped>()\n const currentTable = useGristCurrentTableContext()\n const writes = useGristWritesContext()\n const reads = useGristReadsContext()\n const widgetOptionsApi = useGristWidgetOptionsApiContext()\n const linking = useGristLinkingContext()\n const attachmentsRest = useGristAttachmentsRestContext()\n const theme = useGristThemeContext()\n const sectionApi = useGristSectionApiContext()\n\n return useMemo(\n () =>\n mergeGristResultFromSlices<TRow, TMapped>({\n core,\n selection,\n currentTable,\n writes,\n reads,\n widgetOptionsApi,\n linking,\n attachmentsRest,\n theme,\n sectionApi,\n } as ComposedGristWidgetSlices),\n [\n core,\n selection,\n currentTable,\n writes,\n reads,\n widgetOptionsApi,\n linking,\n attachmentsRest,\n theme,\n sectionApi,\n ]\n )\n}\n","import { useCallback, useEffect, useState } from \"react\"\n\nimport {\n fetchWithGristAuth as fetchWithGristAuthCore,\n getGristAccessToken,\n type GristAccessToken,\n} from \"../helpers/grist-fetch-with-auth\"\nimport { useGristReady } from \"../low-level-hooks/use-grist-ready\"\nimport type { GristReadyOptions } from \"../types\"\n\nexport type UseGristAccessTokenOptions = {\n /** Default for all token requests issued by this hook. */\n readOnly?: boolean\n /** Pre-fetch a token on mount so `token` is non-null when widgets render. */\n prefetch?: boolean\n /** Defaults to `\"full\"` since tokens are used for REST calls. */\n requiredAccess?: GristReadyOptions[\"requiredAccess\"]\n} & Pick<GristReadyOptions, \"columns\" | \"allowSelectBy\" | \"hasCustomOptions\" | \"onEditOptions\">\n\nexport type UseGristAccessTokenResult = {\n token: GristAccessToken | null\n loading: boolean\n error: string | null\n /** Request a fresh token (Grist tokens are short-lived; do not cache aggressively). */\n refresh: (overrides?: { readOnly?: boolean }) => Promise<GristAccessToken>\n /** `fetch()` against Grist's REST API with auth attached. */\n fetch: (path: string, init?: RequestInit & { readOnly?: boolean }) => Promise<Response>\n}\n\n/**\n * Exposes `docApi.getAccessToken` plus a ready-to-use `fetch` for calling\n * Grist's REST API (e.g. attachments, records, sql) from a widget.\n */\nexport function useGristAccessToken(options?: UseGristAccessTokenOptions): UseGristAccessTokenResult {\n const requiredAccess = options?.requiredAccess ?? \"full\"\n const readOnlyDefault = options?.readOnly ?? false\n const prefetch = options?.prefetch ?? false\n const { docApi, error: readyError } = useGristReady({\n requiredAccess,\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n\n const [token, setToken] = useState<GristAccessToken | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const refresh = useCallback(\n async (overrides?: { readOnly?: boolean }): Promise<GristAccessToken> => {\n if (!docApi) throw new Error(\"Grist is not ready\")\n setLoading(true)\n setError(null)\n try {\n const next = await getGristAccessToken(docApi, { readOnly: overrides?.readOnly ?? readOnlyDefault })\n setToken(next)\n return next\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n setError(message)\n throw err\n } finally {\n setLoading(false)\n }\n },\n [docApi, readOnlyDefault]\n )\n\n const fetchFn = useCallback(\n (path: string, init?: RequestInit & { readOnly?: boolean }) => {\n if (!docApi) throw new Error(\"Grist is not ready\")\n return fetchWithGristAuthCore(docApi, path, { readOnly: readOnlyDefault, ...init })\n },\n [docApi, readOnlyDefault]\n )\n\n useEffect(() => {\n if (docApi && prefetch) void refresh()\n }, [docApi, prefetch, refresh])\n\n return {\n token,\n loading,\n error: readyError ?? error,\n refresh,\n fetch: fetchFn,\n }\n}\n","import type { GristDocApi } from \"../types\"\n\nexport type GristAccessToken = {\n token: string\n baseUrl: string\n}\n\ntype TokenCacheEntry = {\n token: GristAccessToken\n fetchedAt: number\n}\n\nconst TOKEN_TTL_MS = 45_000\nconst cache = new Map<string, TokenCacheEntry>()\nconst inFlight = new Map<string, Promise<GristAccessToken>>()\n\nfunction cacheKey(readOnly: boolean): string {\n return readOnly ? \"ro\" : \"rw\"\n}\n\n/**\n * Returns an access token suitable for calling Grist's REST API directly.\n * Tokens are short-lived; results are cached briefly and coalesced per readOnly flag.\n */\nexport async function getGristAccessToken(\n docApi: GristDocApi,\n options?: { readOnly?: boolean }\n): Promise<GristAccessToken> {\n if (!docApi.getAccessToken) {\n throw new Error(\"getAccessToken is not available on docApi (need higher access level)\")\n }\n\n const readOnly = options?.readOnly ?? false\n const key = cacheKey(readOnly)\n const now = Date.now()\n const hit = cache.get(key)\n if (hit && now - hit.fetchedAt < TOKEN_TTL_MS) {\n return hit.token\n }\n\n const pending = inFlight.get(key)\n if (pending) return pending\n\n const next = docApi.getAccessToken({ readOnly }).then((token) => {\n cache.set(key, { token, fetchedAt: Date.now() })\n inFlight.delete(key)\n return token\n })\n\n inFlight.set(key, next)\n return next\n}\n\n/** Clear cached tokens (tests or after auth errors). */\nexport function clearGristAccessTokenCache(): void {\n cache.clear()\n inFlight.clear()\n}\n\n/**\n * `fetch()` against Grist's REST API with the doc access token attached.\n */\nexport async function fetchWithGristAuth(\n docApi: GristDocApi,\n path: string,\n init?: RequestInit & { readOnly?: boolean }\n): Promise<Response> {\n const readOnly = init?.readOnly ?? false\n let token: GristAccessToken\n try {\n token = await getGristAccessToken(docApi, { readOnly })\n } catch (err) {\n clearGristAccessTokenCache()\n throw err\n }\n\n const isAbsolute = /^https?:\\/\\//i.test(path)\n const cleanPath = isAbsolute ? path : path.startsWith(\"/\") ? path : `/${path}`\n const method = (init?.method ?? \"GET\").toUpperCase()\n\n const url = isAbsolute\n ? new URL(path)\n : new URL(`${token.baseUrl.replace(/\\/$/, \"\")}${cleanPath}`)\n\n // Grist signs widget REST calls with a short-lived `auth` query parameter.\n // Do not send `Authorization: Bearer` from the browser: many Grist deployments\n // omit it from Access-Control-Allow-Headers, which breaks POST (e.g. attachment upload).\n if (!url.searchParams.has(\"auth\")) url.searchParams.set(\"auth\", token.token)\n\n const headers = new Headers(init?.headers ?? {})\n headers.delete(\"Authorization\")\n\n const response = await fetch(url.toString(), { ...init, headers })\n if (response.status === 401) {\n clearGristAccessTokenCache()\n const retryToken = await getGristAccessToken(docApi, { readOnly })\n url.searchParams.set(\"auth\", retryToken.token)\n return fetch(url.toString(), { ...init, headers })\n }\n return response\n}\n","import { useCallback, useEffect, useState } from \"react\"\n\nimport { useGristReady } from \"../low-level-hooks/use-grist-ready\"\nimport type { GristReadyOptions, GristWidgetColumnMap } from \"../types\"\n\nexport type UseGristSectionResult = {\n /** Current widget ↔ section column mappings, refreshed on demand. */\n mappings: GristWidgetColumnMap | null\n loading: boolean\n error: string | null\n /** Re-read mappings from `sectionApi`. */\n refresh: () => Promise<void>\n /**\n * Update the widget's declared `ColumnsToMap` (and other ready options) after\n * mount. Use this to dynamically request more columns once the user has\n * configured the widget.\n */\n configure: (options: GristReadyOptions) => Promise<void>\n}\n\n/**\n * Wraps Grist's `sectionApi` (`mappings()` + `configure()`) so widgets can\n * inspect/refresh the column mapping and re-declare requirements without\n * touching `grist` directly.\n */\nexport function useGristSection(options?: GristReadyOptions): UseGristSectionResult {\n const { docApi, error: readyError } = useGristReady({\n requiredAccess: options?.requiredAccess ?? \"read table\",\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n\n const [mappings, setMappings] = useState<GristWidgetColumnMap | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const refresh = useCallback(async () => {\n if (!docApi) return\n if (!grist.sectionApi?.mappings) {\n setError(\"grist.sectionApi.mappings is not available in this Grist version\")\n return\n }\n setLoading(true)\n setError(null)\n try {\n const next = await grist.sectionApi.mappings()\n setMappings(next)\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n } finally {\n setLoading(false)\n }\n }, [docApi])\n\n const configure = useCallback(async (next: GristReadyOptions) => {\n if (!grist.sectionApi?.configure) {\n throw new Error(\"grist.sectionApi.configure is not available in this Grist version\")\n }\n await grist.sectionApi.configure(next)\n }, [])\n\n useEffect(() => {\n if (docApi) void refresh()\n }, [docApi, refresh])\n\n return {\n mappings,\n loading,\n error: readyError ?? error,\n refresh,\n configure,\n }\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\n\nimport { optionsEvent } from \"../internal/subscriptions\"\nimport { useGristReady } from \"../low-level-hooks/use-grist-ready\"\nimport type { GristReadyOptions } from \"../types\"\n\nexport type UseGristWidgetOptionsConfig<T> = {\n /** Default values returned before Grist responds and merged with stored options. */\n defaults: T\n /** Debounce window for `setOptions` writes (ms). Default: 250. */\n debounceMs?: number\n /**\n * Optional namespace inside `grist.setOptions`. When set, this hook reads/writes\n * `grist.setOption(namespace, value)` instead of the whole options blob — useful\n * to share the options bag with `hasCustomOptions` / unrelated widget state.\n */\n namespace?: string\n requiredAccess?: GristReadyOptions[\"requiredAccess\"]\n} & Pick<GristReadyOptions, \"columns\" | \"allowSelectBy\" | \"hasCustomOptions\" | \"onEditOptions\">\n\nexport type UseGristWidgetOptionsResult<T> = {\n options: T\n /** True until Grist has responded with the initial `onOptions` payload. */\n loading: boolean\n error: string | null\n /** Replace the options object (debounced write to Grist). */\n setOptions: (next: T) => void\n /** Patch a subset of keys (debounced write to Grist). */\n patchOptions: (patch: Partial<T>) => void\n /** Reset to `defaults` (immediate write to Grist). */\n reset: () => Promise<void>\n}\n\nfunction mergeWithDefaults<T>(defaults: T, stored: unknown, namespace?: string): T {\n if (stored === null || stored === undefined) return { ...defaults }\n const source = (namespace ? (stored as Record<string, unknown>)[namespace] : stored) as Partial<T> | undefined\n if (!source || typeof source !== \"object\") return { ...defaults }\n return { ...defaults, ...source }\n}\n\n/**\n * Persist a typed object of widget settings via `grist.onOptions` / `setOptions`.\n *\n * - Defaults are returned immediately (no flicker before Grist responds).\n * - Writes are debounced and batched.\n * - `hasCustomOptions` defaults to `true` so Grist exposes the gear icon — pass\n * `hasCustomOptions: false` to opt out.\n */\nexport function useGristWidgetOptions<T extends Record<string, unknown>>(\n config: UseGristWidgetOptionsConfig<T>\n): UseGristWidgetOptionsResult<T> {\n const requiredAccess = config.requiredAccess ?? \"read table\"\n const debounceMs = config.debounceMs ?? 250\n const hasCustomOptions = config.hasCustomOptions ?? true\n const { docApi, error: readyError } = useGristReady({\n requiredAccess,\n columns: config.columns,\n allowSelectBy: config.allowSelectBy,\n hasCustomOptions,\n onEditOptions: config.onEditOptions,\n })\n\n const defaultsRef = useRef(config.defaults)\n useEffect(() => {\n defaultsRef.current = config.defaults\n }, [config.defaults])\n\n const [options, setOptionsState] = useState<T>(() => ({ ...config.defaults }))\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n const lastWrittenRef = useRef<T | null>(null)\n const debounceRef = useRef<number | null>(null)\n\n // Subscribe to remote changes (initial + external edits)\n useEffect(() => {\n if (!docApi) return\n return optionsEvent.subscribe(({ options: stored }) => {\n const merged = mergeWithDefaults(defaultsRef.current, stored, config.namespace)\n lastWrittenRef.current = merged\n setOptionsState(merged)\n setLoading(false)\n })\n }, [docApi, config.namespace])\n\n const flushWrite = useCallback(\n async (value: T) => {\n try {\n if (config.namespace) {\n await grist.setOption(config.namespace, value)\n } else {\n await grist.setOptions({ ...value })\n }\n lastWrittenRef.current = value\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n }\n },\n [config.namespace]\n )\n\n const scheduleWrite = useCallback(\n (value: T) => {\n if (debounceRef.current) window.clearTimeout(debounceRef.current)\n debounceRef.current = window.setTimeout(() => {\n debounceRef.current = null\n void flushWrite(value)\n }, debounceMs)\n },\n [debounceMs, flushWrite]\n )\n\n useEffect(() => {\n return () => {\n if (debounceRef.current) window.clearTimeout(debounceRef.current)\n }\n }, [])\n\n const setOptionsApi = useCallback(\n (next: T) => {\n setOptionsState(next)\n scheduleWrite(next)\n },\n [scheduleWrite]\n )\n\n const patchOptionsApi = useCallback(\n (patch: Partial<T>) => {\n setOptionsState((prev) => {\n const next = { ...prev, ...patch }\n scheduleWrite(next)\n return next\n })\n },\n [scheduleWrite]\n )\n\n const reset = useCallback(async () => {\n if (debounceRef.current) {\n window.clearTimeout(debounceRef.current)\n debounceRef.current = null\n }\n const next = { ...defaultsRef.current }\n setOptionsState(next)\n await flushWrite(next)\n }, [flushWrite])\n\n return useMemo(\n () => ({\n options,\n loading,\n error: readyError ?? error,\n setOptions: setOptionsApi,\n patchOptions: patchOptionsApi,\n reset,\n }),\n [options, loading, readyError, error, setOptionsApi, patchOptionsApi, reset]\n )\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\n\nimport {\n useGristSelectionContext,\n useGristWidgetOptionsApiContext,\n} from \"../internal/contexts\"\n\nexport type UseGristWidgetOptionsFromContextConfig<T> = {\n /** Default values returned before Grist responds and merged with stored options. */\n defaults: T\n /** Debounce window for `setOptions` writes (ms). Default: 250. */\n debounceMs?: number\n /**\n * Optional namespace key. When set, this hook reads/writes\n * `options[namespace]` instead of the whole options blob.\n */\n namespace?: string\n}\n\nexport type UseGristWidgetOptionsFromContextResult<T> = {\n options: T\n /** True until Grist has responded with the initial `onOptions` payload. */\n loading: boolean\n /** Replace the options object (debounced write to Grist). */\n setOptions: (next: T) => void\n /** Patch a subset of keys (debounced write to Grist). */\n patchOptions: (patch: Partial<T>) => void\n /** Reset to `defaults` (immediate write to Grist). */\n reset: () => Promise<void>\n}\n\nfunction mergeWithDefaults<T>(defaults: T, stored: unknown, namespace?: string): T {\n if (stored === null || stored === undefined) return { ...defaults }\n const source = (namespace ? (stored as Record<string, unknown>)[namespace] : stored) as Partial<T> | undefined\n if (!source || typeof source !== \"object\") return { ...defaults }\n return { ...defaults, ...source }\n}\n\n/**\n * Typed widget options hook designed for use **inside a `<GristWidgetProvider>`**.\n *\n * Unlike `useGristWidgetOptions()` (which calls `useGristReady()` and conflicts\n * with the provider), this hook reads from the provider's existing context —\n * `widgetOptions` for reactive reads, and `patchWidgetOptions` / `setWidgetOptions`\n * for writes.\n *\n * ```tsx\n * function MyConfigPanel() {\n * const { options, patchOptions } = useGristWidgetOptionsFromContext({\n * defaults: { color: '#FF0000', showLabels: true },\n * })\n * return <input value={options.color} onChange={e => patchOptions({ color: e.target.value })} />\n * }\n * ```\n */\nexport function useGristWidgetOptionsFromContext<T extends Record<string, unknown>>(\n config: UseGristWidgetOptionsFromContextConfig<T>\n): UseGristWidgetOptionsFromContextResult<T> {\n const debounceMs = config.debounceMs ?? 250\n const selection = useGristSelectionContext()\n const optionsApi = useGristWidgetOptionsApiContext()\n\n const defaultsRef = useRef(config.defaults)\n useEffect(() => {\n defaultsRef.current = config.defaults\n }, [config.defaults])\n\n const rawOptions = selection.widgetOptions\n const hasReceived = rawOptions !== null\n\n const merged = useMemo(\n () => mergeWithDefaults(defaultsRef.current, rawOptions, config.namespace),\n [rawOptions, config.namespace]\n )\n\n const [localOverride, setLocalOverride] = useState<T | null>(null)\n const options = localOverride ?? merged\n\n useEffect(() => {\n setLocalOverride(null)\n }, [rawOptions])\n\n const debounceRef = useRef<number | null>(null)\n\n const flushWrite = useCallback(\n async (value: T) => {\n if (config.namespace) {\n await optionsApi.setWidgetOption(config.namespace, value)\n } else {\n await optionsApi.setWidgetOptions({ ...value })\n }\n },\n [config.namespace, optionsApi]\n )\n\n const scheduleWrite = useCallback(\n (value: T) => {\n if (debounceRef.current) window.clearTimeout(debounceRef.current)\n debounceRef.current = window.setTimeout(() => {\n debounceRef.current = null\n void flushWrite(value)\n }, debounceMs)\n },\n [debounceMs, flushWrite]\n )\n\n useEffect(() => {\n return () => {\n if (debounceRef.current) window.clearTimeout(debounceRef.current)\n }\n }, [])\n\n const setOptions = useCallback(\n (next: T) => {\n setLocalOverride(next)\n scheduleWrite(next)\n },\n [scheduleWrite]\n )\n\n const patchOptions = useCallback(\n (patch: Partial<T>) => {\n setLocalOverride((prev) => {\n const base = prev ?? merged\n const next = { ...base, ...patch }\n scheduleWrite(next)\n return next\n })\n },\n [merged, scheduleWrite]\n )\n\n const reset = useCallback(async () => {\n if (debounceRef.current) {\n window.clearTimeout(debounceRef.current)\n debounceRef.current = null\n }\n const next = { ...defaultsRef.current }\n setLocalOverride(null)\n await flushWrite(next)\n }, [flushWrite])\n\n return useMemo(\n () => ({\n options,\n loading: !hasReceived,\n setOptions,\n patchOptions,\n reset,\n }),\n [options, hasReceived, setOptions, patchOptions, reset]\n )\n}\n","import { useCallback, useEffect, useState } from \"react\"\n\nimport type { GristColumnarTable } from \"./helpers/grist-table-data\"\nimport { useGristReady } from \"./low-level-hooks/use-grist-ready\"\nimport type { GristFetchSelectedOptions, GristReadyOptions, GristRequiredAccess } from \"./types\"\n\nexport type UseGristTableOptions = {\n requiredAccess?: GristRequiredAccess\n /**\n * `'current'` (default): follow the widget’s selected table.\n * Any other string: fixed table id (no `grist.selectedTable` polling).\n */\n tableId?: \"current\" | string\n} & Pick<GristReadyOptions, \"columns\" | \"allowSelectBy\" | \"hasCustomOptions\" | \"onEditOptions\">\n\nexport type UseGristTableResult = {\n tableId: string | null\n loading: boolean\n error: string | null\n refresh: () => Promise<void>\n fetchTable: (targetTableId: string) => Promise<GristColumnarTable>\n listTables: () => Promise<string[]>\n getDocName: () => Promise<string>\n fetchSelectedTable: (fetchOptions?: GristFetchSelectedOptions) => Promise<GristColumnarTable>\n fetchSelectedRecord: (rowId: number, fetchOptions?: GristFetchSelectedOptions) => Promise<Record<string, unknown>>\n}\n\nfunction isCurrentMode(option: string | undefined): boolean {\n return option === undefined || option === \"current\"\n}\n\n/**\n * Selected or fixed table id plus document reads — no raw `docApi` on the result.\n */\nexport function useGristTable(options?: UseGristTableOptions): UseGristTableResult {\n const requiredAccess = options?.requiredAccess ?? \"read table\"\n const tableIdOption = options?.tableId ?? \"current\"\n const { docApi, error: readyError } = useGristReady({\n requiredAccess,\n columns: options?.columns,\n allowSelectBy: options?.allowSelectBy,\n hasCustomOptions: options?.hasCustomOptions,\n onEditOptions: options?.onEditOptions,\n })\n const [tableId, setTableId] = useState<string | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const fetchTable = useCallback(\n async (targetTableId: string) => {\n if (!docApi) throw new Error(\"Grist is not ready\")\n return docApi.fetchTable(targetTableId) as Promise<GristColumnarTable>\n },\n [docApi]\n )\n\n const listTables = useCallback(async () => {\n if (!docApi) throw new Error(\"Grist is not ready\")\n const out = await Promise.resolve(docApi.listTables())\n return [...out]\n }, [docApi])\n\n const getDocName = useCallback(async () => {\n if (!docApi) throw new Error(\"Grist is not ready\")\n return String(await Promise.resolve(docApi.getDocName()))\n }, [docApi])\n\n const fetchSelectedTable = useCallback(\n async (fetchOptions?: GristFetchSelectedOptions) => {\n if (!docApi) throw new Error(\"Grist is not ready\")\n const impl = docApi.fetchSelectedTable ?? grist.fetchSelectedTable\n if (!impl) throw new Error(\"fetchSelectedTable is not available in this Grist version\")\n const merged: GristFetchSelectedOptions = { keepEncoded: false, format: \"rows\", ...fetchOptions }\n return (await impl(merged)) as GristColumnarTable\n },\n [docApi]\n )\n\n const fetchSelectedRecord = useCallback(\n async (rowId: number, fetchOptions?: GristFetchSelectedOptions) => {\n if (!docApi) throw new Error(\"Grist is not ready\")\n const impl = docApi.fetchSelectedRecord ?? grist.fetchSelectedRecord\n if (!impl) throw new Error(\"fetchSelectedRecord is not available in this Grist version\")\n const merged: GristFetchSelectedOptions = { keepEncoded: false, format: \"rows\", ...fetchOptions }\n return (await impl(rowId, merged)) as Record<string, unknown>\n },\n [docApi]\n )\n\n const refresh = useCallback(async () => {\n if (!docApi) return\n if (!isCurrentMode(tableIdOption)) {\n setTableId(tableIdOption)\n setError(null)\n return\n }\n setLoading(true)\n setError(null)\n try {\n const next = await grist.selectedTable?.getTableId()\n setTableId(next ?? null)\n } catch (err) {\n setTableId(null)\n setError(err instanceof Error ? err.message : String(err))\n } finally {\n setLoading(false)\n }\n }, [docApi, tableIdOption])\n\n useEffect(() => {\n if (docApi) void refresh()\n }, [docApi, refresh])\n\n return {\n tableId,\n loading,\n error: readyError ?? error,\n refresh,\n fetchTable,\n listTables,\n getDocName,\n fetchSelectedTable,\n fetchSelectedRecord,\n }\n}\n"],"mappings":"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","names":["import_react","nextAuthz","ACCESS_RANK","grist","grist","inFlight","embedded","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react","inFlight","import_react","import_react","import_react","mergeWithDefaults","import_react"]}
|