grist-widget-sdk 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +730 -0
- package/LICENSE +15 -0
- package/README.md +249 -0
- package/dist/advanced.cjs +2749 -0
- package/dist/advanced.cjs.map +1 -0
- package/dist/advanced.d.cts +790 -0
- package/dist/advanced.d.ts +790 -0
- package/dist/advanced.js +825 -0
- package/dist/advanced.js.map +1 -0
- package/dist/chunk-64SVRWW7.js +758 -0
- package/dist/chunk-64SVRWW7.js.map +1 -0
- package/dist/chunk-BNBV24WM.js +479 -0
- package/dist/chunk-BNBV24WM.js.map +1 -0
- package/dist/chunk-ELGVOHFD.js +32 -0
- package/dist/chunk-ELGVOHFD.js.map +1 -0
- package/dist/chunk-GTTD5HPO.js +36 -0
- package/dist/chunk-GTTD5HPO.js.map +1 -0
- package/dist/chunk-JASMM2YC.js +243 -0
- package/dist/chunk-JASMM2YC.js.map +1 -0
- package/dist/chunk-PM3B4WYV.js +12 -0
- package/dist/chunk-PM3B4WYV.js.map +1 -0
- package/dist/chunk-QKXKZ27D.js +1189 -0
- package/dist/chunk-QKXKZ27D.js.map +1 -0
- package/dist/chunk-QZG7SNUG.js +463 -0
- package/dist/chunk-QZG7SNUG.js.map +1 -0
- package/dist/chunk-REV5AUXD.js +25 -0
- package/dist/chunk-REV5AUXD.js.map +1 -0
- package/dist/chunk-XE2SKNLH.js +1641 -0
- package/dist/chunk-XE2SKNLH.js.map +1 -0
- package/dist/create-grist-emulator-C_0I4wax.d.ts +80 -0
- package/dist/create-grist-emulator-DBS6gmiI.d.cts +80 -0
- package/dist/emulator/devtools.cjs +1367 -0
- package/dist/emulator/devtools.cjs.map +1 -0
- package/dist/emulator/devtools.d.cts +46 -0
- package/dist/emulator/devtools.d.ts +46 -0
- package/dist/emulator/devtools.js +480 -0
- package/dist/emulator/devtools.js.map +1 -0
- package/dist/emulator/testing.cjs +2061 -0
- package/dist/emulator/testing.cjs.map +1 -0
- package/dist/emulator/testing.d.cts +50 -0
- package/dist/emulator/testing.d.ts +50 -0
- package/dist/emulator/testing.js +124 -0
- package/dist/emulator/testing.js.map +1 -0
- package/dist/emulator/transports.cjs +1132 -0
- package/dist/emulator/transports.cjs.map +1 -0
- package/dist/emulator/transports.d.cts +37 -0
- package/dist/emulator/transports.d.ts +37 -0
- package/dist/emulator/transports.js +12 -0
- package/dist/emulator/transports.js.map +1 -0
- package/dist/emulator.cjs +1796 -0
- package/dist/emulator.cjs.map +1 -0
- package/dist/emulator.d.cts +107 -0
- package/dist/emulator.d.ts +107 -0
- package/dist/emulator.js +26 -0
- package/dist/emulator.js.map +1 -0
- package/dist/index.cjs +5511 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +825 -0
- package/dist/index.d.ts +825 -0
- package/dist/index.js +2447 -0
- package/dist/index.js.map +1 -0
- package/dist/inline-CRwQQ-GW.d.cts +311 -0
- package/dist/inline-QitZwXKP.d.ts +311 -0
- package/dist/templates-BC7FoSdW.d.ts +24 -0
- package/dist/templates-DBq719O_.d.cts +24 -0
- package/dist/types-Fb2wplkv.d.cts +228 -0
- package/dist/types-Fb2wplkv.d.ts +228 -0
- package/dist/use-grist-widget-options-from-context-BFhQBGRY.d.ts +522 -0
- package/dist/use-grist-widget-options-from-context-qpFMdoBJ.d.cts +522 -0
- package/package.json +131 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sdk/helpers/grist-fetch-with-auth.ts","../src/sdk/internal/hooks/use-grist-core.ts","../src/sdk/internal/handshake-manager-context.ts","../src/sdk/internal/build-manager-options.ts","../src/sdk/low-level-hooks/use-grist-ready.ts","../src/sdk/mid-level-hooks/use-grist-widget-options-from-context.ts","../src/sdk/internal/contexts.tsx","../src/sdk/internal/merge-grist-result.ts","../src/sdk/internal/hooks/use-rpc-heartbeat-coalesce.ts"],"sourcesContent":["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, 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 * 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","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","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","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 { 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 type { ComposedGristWidgetSlices } from \"./compose-grist-widget\"\nimport type { UseGristResult } from \"../use-grist-types\"\n\nexport function mergeGristResultFromSlices<TRow = Record<string, unknown>, TMapped = Record<string, unknown>>(\n slices: ComposedGristWidgetSlices\n): UseGristResult<TRow, TMapped> {\n const { core, selection, currentTable, writes, reads, widgetOptionsApi, linking, attachmentsRest, theme, sectionApi, suppressAlerts, allowSelectBy } =\n slices\n return {\n status: core.status,\n isAvailable: core.isAvailable,\n isReady: core.isReady,\n error: core.error,\n capabilities: core.capabilities,\n record: selection.record as UseGristResult<TRow, TMapped>[\"record\"],\n records: selection.records as UseGristResult<TRow, TMapped>[\"records\"],\n mappedRecord: selection.mappedRecord as TMapped | null,\n mappings: selection.mappings,\n recordsMappings: selection.recordsMappings,\n columnMappingStatus: selection.columnMappingStatus,\n mode: selection.mode,\n resolveMappedColumnId: selection.resolveMappedColumnId,\n mapBack: selection.mapBack as (patch: Partial<TMapped>) => Record<string, unknown>,\n mapBackSkipped: selection.mapBackSkipped,\n isNewRecord: selection.isNewRecord,\n newRecordMappings: selection.newRecordMappings,\n currentTableId: currentTable.currentTableId,\n currentTableLoading: currentTable.currentTableLoading,\n refreshCurrentTable: currentTable.refreshCurrentTable,\n table: writes.table,\n getTable: writes.getTable,\n actionStatus: writes.actionStatus,\n actionError: writes.actionError,\n applyActions: writes.applyActions,\n reload: core.reload,\n fetchTable: reads.fetchTable,\n fetchTableRows: reads.fetchTableRows,\n fetchRow: reads.fetchRow,\n listTables: reads.listTables,\n listColumns: reads.listColumns,\n getDocName: reads.getDocName,\n buildReplicaDocumentFromDocApi: reads.buildReplicaDocumentFromDocApi,\n getAttachmentUrl: attachmentsRest.getAttachmentUrl,\n fetchAttachmentBase64: attachmentsRest.fetchAttachmentBase64,\n fetchAttachmentBlob: attachmentsRest.fetchAttachmentBlob,\n widgetOptions: selection.widgetOptions,\n widgetInteraction: selection.widgetInteraction,\n getWidgetOptions: widgetOptionsApi.getWidgetOptions,\n getWidgetOption: widgetOptionsApi.getWidgetOption,\n setWidgetOption: widgetOptionsApi.setWidgetOption,\n setWidgetOptions: widgetOptionsApi.setWidgetOptions,\n patchWidgetOptions: widgetOptionsApi.patchWidgetOptions,\n clearWidgetOptions: widgetOptionsApi.clearWidgetOptions,\n fetchSelectedTable: reads.fetchSelectedTable,\n fetchSelectedRecord: reads.fetchSelectedRecord,\n readError: reads.readError ?? attachmentsRest.readError,\n setCursorPosition: linking.setCursorPosition,\n setLinkedRowSelection: linking.setLinkedRowSelection,\n getAccessToken: attachmentsRest.getAccessToken,\n fetchWithAuth: attachmentsRest.fetchWithAuth,\n uploadAttachment: attachmentsRest.uploadAttachment,\n theme: theme.theme,\n configure: sectionApi.configure,\n refreshMappings: sectionApi.refreshMappings,\n suppressAlerts,\n allowSelectBy,\n }\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"],"mappings":"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","names":["useCallback","useEffect","useMemo","useRef","createContext","useContext","useMemo","createContext","useContext","useMemo","useRef","useEffect","useMemo","useCallback","useCallback","useCallback"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// src/emulator/core/columnar.ts
|
|
2
|
+
function rowsToColumnar(table, rows, options = {}) {
|
|
3
|
+
const cols = Object.keys(table.columns);
|
|
4
|
+
const out = { id: rows.map((r) => r.id) };
|
|
5
|
+
for (const col of cols) {
|
|
6
|
+
out[col] = rows.map((r) => encodeForWire(r[col], table.columns[col]?.type, options.keepEncoded));
|
|
7
|
+
}
|
|
8
|
+
return out;
|
|
9
|
+
}
|
|
10
|
+
function rowToRecord(table, row, options = {}) {
|
|
11
|
+
const out = { id: row.id };
|
|
12
|
+
for (const col of Object.keys(table.columns)) {
|
|
13
|
+
out[col] = encodeForWire(row[col], table.columns[col]?.type, options.keepEncoded);
|
|
14
|
+
}
|
|
15
|
+
return out;
|
|
16
|
+
}
|
|
17
|
+
function encodeForWire(value, type, keepEncoded) {
|
|
18
|
+
if (value instanceof Date) {
|
|
19
|
+
const epoch = Math.floor(value.getTime() / 1e3);
|
|
20
|
+
if (keepEncoded) {
|
|
21
|
+
return type && type.startsWith("DateTime") ? ["D", epoch, "UTC"] : ["d", epoch];
|
|
22
|
+
}
|
|
23
|
+
return epoch;
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
rowsToColumnar,
|
|
30
|
+
rowToRecord
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=chunk-ELGVOHFD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/emulator/core/columnar.ts"],"sourcesContent":["/**\n * Helpers to project an {@link EmulatorTable}'s row-array storage into Grist's\n * columnar wire format expected by `fetchTable` / `fetchSelectedTable`:\n *\n * { id: number[], colA: unknown[], colB: unknown[], ... }\n *\n * When `keepEncoded` is `false` (Grist's default for these APIs), values are\n * passed through as-is — JS-native primitives (string / number / boolean /\n * Date / arrays) are valid Grist plugin values. When `keepEncoded` is `true`,\n * Date columns are emitted in tuple form (`[\"d\", epoch]`) so callers can\n * round-trip them.\n */\n\nimport type { GristRowRecord } from \"../../sdk/types\"\nimport type { EmulatorTable } from \"./types\"\n\nexport type ColumnarTable = Record<string, unknown[]>\n\nexport type ToColumnarOptions = {\n keepEncoded?: boolean\n}\n\nexport function rowsToColumnar(\n table: EmulatorTable,\n rows: readonly GristRowRecord[],\n options: ToColumnarOptions = {}\n): ColumnarTable {\n const cols = Object.keys(table.columns)\n const out: ColumnarTable = { id: rows.map((r) => r.id) }\n for (const col of cols) {\n out[col] = rows.map((r) => encodeForWire(r[col], table.columns[col]?.type, options.keepEncoded))\n }\n return out\n}\n\nexport function rowToRecord(\n table: EmulatorTable,\n row: GristRowRecord,\n options: ToColumnarOptions = {}\n): Record<string, unknown> {\n const out: Record<string, unknown> = { id: row.id }\n for (const col of Object.keys(table.columns)) {\n out[col] = encodeForWire(row[col], table.columns[col]?.type, options.keepEncoded)\n }\n return out\n}\n\nfunction encodeForWire(value: unknown, type: string | undefined, keepEncoded: boolean | undefined): unknown {\n if (value instanceof Date) {\n const epoch = Math.floor(value.getTime() / 1000)\n if (keepEncoded) {\n return type && type.startsWith(\"DateTime\") ? [\"D\", epoch, \"UTC\"] : [\"d\", epoch]\n }\n return epoch\n }\n return value\n}\n"],"mappings":";AAsBO,SAAS,eACd,OACA,MACA,UAA6B,CAAC,GACf;AACf,QAAM,OAAO,OAAO,KAAK,MAAM,OAAO;AACtC,QAAM,MAAqB,EAAE,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE;AACvD,aAAW,OAAO,MAAM;AACtB,QAAI,GAAG,IAAI,KAAK,IAAI,CAAC,MAAM,cAAc,EAAE,GAAG,GAAG,MAAM,QAAQ,GAAG,GAAG,MAAM,QAAQ,WAAW,CAAC;AAAA,EACjG;AACA,SAAO;AACT;AAEO,SAAS,YACd,OACA,KACA,UAA6B,CAAC,GACL;AACzB,QAAM,MAA+B,EAAE,IAAI,IAAI,GAAG;AAClD,aAAW,OAAO,OAAO,KAAK,MAAM,OAAO,GAAG;AAC5C,QAAI,GAAG,IAAI,cAAc,IAAI,GAAG,GAAG,MAAM,QAAQ,GAAG,GAAG,MAAM,QAAQ,WAAW;AAAA,EAClF;AACA,SAAO;AACT;AAEA,SAAS,cAAc,OAAgB,MAA0B,aAA2C;AAC1G,MAAI,iBAAiB,MAAM;AACzB,UAAM,QAAQ,KAAK,MAAM,MAAM,QAAQ,IAAI,GAAI;AAC/C,QAAI,aAAa;AACf,aAAO,QAAQ,KAAK,WAAW,UAAU,IAAI,CAAC,KAAK,OAAO,KAAK,IAAI,CAAC,KAAK,KAAK;AAAA,IAChF;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// src/sdk/helpers/replica-normalize.ts
|
|
2
|
+
function normalizeReplicaTableInput(table) {
|
|
3
|
+
return { ...table, rows: table.rows ?? [] };
|
|
4
|
+
}
|
|
5
|
+
function normalizeReplicaDocumentInput(doc) {
|
|
6
|
+
return {
|
|
7
|
+
...doc,
|
|
8
|
+
tables: Object.fromEntries(
|
|
9
|
+
Object.entries(doc.tables).map(([tableId, t]) => [tableId, normalizeReplicaTableInput(t)])
|
|
10
|
+
)
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function buildReplicaJsonPayload(doc, options) {
|
|
14
|
+
const limit = options.sampleRowLimit ?? 10;
|
|
15
|
+
const tables = Object.fromEntries(
|
|
16
|
+
Object.entries(doc.tables).map(([tid, t]) => {
|
|
17
|
+
if (options.includeRows === "none") {
|
|
18
|
+
const { rows: _r, ...rest } = t;
|
|
19
|
+
return [tid, rest];
|
|
20
|
+
}
|
|
21
|
+
if (options.includeRows === "samples") {
|
|
22
|
+
const r = t.rows ?? [];
|
|
23
|
+
return [tid, { ...t, rows: r.slice(0, limit) }];
|
|
24
|
+
}
|
|
25
|
+
return [tid, { ...t, rows: t.rows }];
|
|
26
|
+
})
|
|
27
|
+
);
|
|
28
|
+
return { ...doc, tables };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
normalizeReplicaTableInput,
|
|
33
|
+
normalizeReplicaDocumentInput,
|
|
34
|
+
buildReplicaJsonPayload
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=chunk-GTTD5HPO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sdk/helpers/replica-normalize.ts"],"sourcesContent":["import type { GristReplicaDocument, GristReplicaRow, GristReplicaTable } from \"../replica-document\"\n\n/**\n * Ensure `rows` is a concrete array for emulator / merge logic.\n * Call at host boundaries (`createDocument`, tests); JSON may still omit `rows` for schema-only.\n */\nexport function normalizeReplicaTableInput<T extends GristReplicaTable>(table: T): T & { rows: GristReplicaRow[] } {\n return { ...table, rows: table.rows ?? [] }\n}\n\n/** Apply {@link normalizeReplicaTableInput} to every table; shallow-copies the document shell. */\nexport function normalizeReplicaDocumentInput<T extends GristReplicaDocument>(doc: T): T {\n return {\n ...doc,\n tables: Object.fromEntries(\n Object.entries(doc.tables).map(([tableId, t]) => [tableId, normalizeReplicaTableInput(t)])\n ),\n } as T\n}\n\n/** How row arrays appear in serialized replica JSON from {@link buildReplicaJsonPayload}. */\nexport type ReplicaJsonIncludeRows = \"none\" | \"samples\" | \"full\"\n\n/**\n * Clone a replica document for JSON output: strip rows, cap to first N, or pass through.\n * Does not mutate the source document.\n */\nexport function buildReplicaJsonPayload(\n doc: GristReplicaDocument,\n options: { includeRows: ReplicaJsonIncludeRows; sampleRowLimit?: number }\n): GristReplicaDocument {\n const limit = options.sampleRowLimit ?? 10\n const tables = Object.fromEntries(\n Object.entries(doc.tables).map(([tid, t]) => {\n if (options.includeRows === \"none\") {\n const { rows: _r, ...rest } = t\n return [tid, rest]\n }\n if (options.includeRows === \"samples\") {\n const r = t.rows ?? []\n return [tid, { ...t, rows: r.slice(0, limit) }]\n }\n return [tid, { ...t, rows: t.rows }]\n })\n ) as GristReplicaDocument[\"tables\"]\n return { ...doc, tables }\n}\n"],"mappings":";AAMO,SAAS,2BAAwD,OAA2C;AACjH,SAAO,EAAE,GAAG,OAAO,MAAM,MAAM,QAAQ,CAAC,EAAE;AAC5C;AAGO,SAAS,8BAA8D,KAAW;AACvF,SAAO;AAAA,IACL,GAAG;AAAA,IACH,QAAQ,OAAO;AAAA,MACb,OAAO,QAAQ,IAAI,MAAM,EAAE,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,2BAA2B,CAAC,CAAC,CAAC;AAAA,IAC3F;AAAA,EACF;AACF;AASO,SAAS,wBACd,KACA,SACsB;AACtB,QAAM,QAAQ,QAAQ,kBAAkB;AACxC,QAAM,SAAS,OAAO;AAAA,IACpB,OAAO,QAAQ,IAAI,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM;AAC3C,UAAI,QAAQ,gBAAgB,QAAQ;AAClC,cAAM,EAAE,MAAM,IAAI,GAAG,KAAK,IAAI;AAC9B,eAAO,CAAC,KAAK,IAAI;AAAA,MACnB;AACA,UAAI,QAAQ,gBAAgB,WAAW;AACrC,cAAM,IAAI,EAAE,QAAQ,CAAC;AACrB,eAAO,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,EAAE,MAAM,GAAG,KAAK,EAAE,CAAC;AAAA,MAChD;AACA,aAAO,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,EAAE,KAAK,CAAC;AAAA,IACrC,CAAC;AAAA,EACH;AACA,SAAO,EAAE,GAAG,KAAK,OAAO;AAC1B;","names":[]}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import {
|
|
2
|
+
decodeGristValue,
|
|
3
|
+
safeParseGristTableData
|
|
4
|
+
} from "./chunk-64SVRWW7.js";
|
|
5
|
+
|
|
6
|
+
// src/sdk/helpers/grist-table-data.ts
|
|
7
|
+
function inferDisplayHelpersByNameFromData(data) {
|
|
8
|
+
const out = {};
|
|
9
|
+
for (const colId of Object.keys(data)) {
|
|
10
|
+
if (!colId.startsWith("gristHelper_")) continue;
|
|
11
|
+
const baseColId = colId.slice("gristHelper_".length);
|
|
12
|
+
if (!baseColId) continue;
|
|
13
|
+
if (!Array.isArray(data[baseColId])) continue;
|
|
14
|
+
out[baseColId] = colId;
|
|
15
|
+
}
|
|
16
|
+
return out;
|
|
17
|
+
}
|
|
18
|
+
function enrichSafeParseColumnsFromData(data, columns) {
|
|
19
|
+
const out = { ...columns ?? {} };
|
|
20
|
+
const keys = Object.keys(data);
|
|
21
|
+
const positionByColId = new Map(keys.map((colId, index) => [colId, index + 1]));
|
|
22
|
+
for (const colId of keys) {
|
|
23
|
+
if (!colId.startsWith("gristHelper_")) continue;
|
|
24
|
+
if (out[colId]) continue;
|
|
25
|
+
out[colId] = {
|
|
26
|
+
type: "Any",
|
|
27
|
+
label: colId,
|
|
28
|
+
parentPos: positionByColId.get(colId)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
function rowsFromSafeCellResults(parsedByColumn) {
|
|
34
|
+
const colIds = Object.keys(parsedByColumn);
|
|
35
|
+
const rowCount = colIds.reduce((max, colId) => {
|
|
36
|
+
const len = parsedByColumn[colId]?.length ?? 0;
|
|
37
|
+
return Math.max(max, len);
|
|
38
|
+
}, 0);
|
|
39
|
+
const rows = [];
|
|
40
|
+
for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
|
|
41
|
+
const row = {};
|
|
42
|
+
for (const colId of colIds) {
|
|
43
|
+
row[colId] = parsedByColumn[colId]?.[rowIndex] ?? null;
|
|
44
|
+
}
|
|
45
|
+
rows.push(row);
|
|
46
|
+
}
|
|
47
|
+
return rows;
|
|
48
|
+
}
|
|
49
|
+
function safeCellRowId(row) {
|
|
50
|
+
const idCell = row.id;
|
|
51
|
+
if (!idCell || typeof idCell !== "object" || !("typedValue" in idCell)) return null;
|
|
52
|
+
const typed = idCell.typedValue;
|
|
53
|
+
return typeof typed === "number" && Number.isFinite(typed) ? typed : null;
|
|
54
|
+
}
|
|
55
|
+
function orderSafeCellRowsByParsedRows(rows, orderedParsedRows) {
|
|
56
|
+
const byId = /* @__PURE__ */ new Map();
|
|
57
|
+
for (const row of rows) {
|
|
58
|
+
const id = safeCellRowId(row);
|
|
59
|
+
if (id === null) continue;
|
|
60
|
+
const bucket = byId.get(id);
|
|
61
|
+
if (bucket) bucket.push(row);
|
|
62
|
+
else byId.set(id, [row]);
|
|
63
|
+
}
|
|
64
|
+
const out = [];
|
|
65
|
+
for (const parsedRow of orderedParsedRows) {
|
|
66
|
+
const id = typeof parsedRow.id === "number" ? parsedRow.id : null;
|
|
67
|
+
if (id === null) continue;
|
|
68
|
+
const bucket = byId.get(id);
|
|
69
|
+
if (!bucket || bucket.length === 0) continue;
|
|
70
|
+
const next = bucket.shift();
|
|
71
|
+
if (next) out.push(next);
|
|
72
|
+
}
|
|
73
|
+
for (const bucket of byId.values()) {
|
|
74
|
+
out.push(...bucket);
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
function safeParseRowsToDisplayRows(rows) {
|
|
79
|
+
return rows.map((row) => {
|
|
80
|
+
const out = {};
|
|
81
|
+
for (const [colId, cell] of Object.entries(row)) {
|
|
82
|
+
if (cell && typeof cell === "object" && "displayValue" in cell) {
|
|
83
|
+
out[colId] = cell.displayValue;
|
|
84
|
+
} else {
|
|
85
|
+
out[colId] = cell;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function computeRowCount(data) {
|
|
92
|
+
const idCol = data.id;
|
|
93
|
+
if (Array.isArray(idCol)) return idCol.length;
|
|
94
|
+
for (const key of Object.keys(data)) {
|
|
95
|
+
const col = data[key];
|
|
96
|
+
if (Array.isArray(col)) return col.length;
|
|
97
|
+
}
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
function decodeColumnarToReplicaRows(data, columnIds, columns, maxRows) {
|
|
101
|
+
const nFull = computeRowCount(data);
|
|
102
|
+
const n = maxRows !== void 0 ? Math.min(nFull, maxRows) : nFull;
|
|
103
|
+
const out = [];
|
|
104
|
+
for (let i = 0; i < n; i++) {
|
|
105
|
+
const row = {};
|
|
106
|
+
for (const k of columnIds) {
|
|
107
|
+
const arr = data[k];
|
|
108
|
+
const raw = Array.isArray(arr) ? arr[i] : void 0;
|
|
109
|
+
if (k === "id") {
|
|
110
|
+
row[k] = raw;
|
|
111
|
+
} else {
|
|
112
|
+
row[k] = decodeGristValue(raw, columns[k]);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
out.push(row);
|
|
116
|
+
}
|
|
117
|
+
return out;
|
|
118
|
+
}
|
|
119
|
+
function tableDataToRows(data) {
|
|
120
|
+
const idCol = data.id;
|
|
121
|
+
if (!Array.isArray(idCol) || idCol.length === 0) return [];
|
|
122
|
+
const keys = Object.keys(data);
|
|
123
|
+
const n = idCol.length;
|
|
124
|
+
const rows = [];
|
|
125
|
+
for (let i = 0; i < n; i++) {
|
|
126
|
+
const row = {};
|
|
127
|
+
for (const k of keys) {
|
|
128
|
+
const col = data[k];
|
|
129
|
+
row[k] = Array.isArray(col) ? col[i] : void 0;
|
|
130
|
+
}
|
|
131
|
+
rows.push(row);
|
|
132
|
+
}
|
|
133
|
+
return rows;
|
|
134
|
+
}
|
|
135
|
+
function manualSortKey(row) {
|
|
136
|
+
const v = row.manualSort;
|
|
137
|
+
if (typeof v === "number" && !Number.isNaN(v)) return v;
|
|
138
|
+
if (typeof v === "string" && v.trim() !== "") {
|
|
139
|
+
const n = Number(v);
|
|
140
|
+
if (!Number.isNaN(n)) return n;
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
function sortRowsByGristOrder(rows) {
|
|
145
|
+
const hasManual = rows.some((r) => manualSortKey(r) !== null);
|
|
146
|
+
if (!hasManual) {
|
|
147
|
+
return [...rows].sort((a, b) => {
|
|
148
|
+
const ida = typeof a.id === "number" ? a.id : 0;
|
|
149
|
+
const idb = typeof b.id === "number" ? b.id : 0;
|
|
150
|
+
return ida - idb;
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
return [...rows].sort((a, b) => {
|
|
154
|
+
const ma = manualSortKey(a);
|
|
155
|
+
const mb = manualSortKey(b);
|
|
156
|
+
if (ma !== null && mb !== null && ma !== mb) return ma - mb;
|
|
157
|
+
if (ma !== null && mb === null) return -1;
|
|
158
|
+
if (ma === null && mb !== null) return 1;
|
|
159
|
+
const ida = typeof a.id === "number" ? a.id : 0;
|
|
160
|
+
const idb = typeof b.id === "number" ? b.id : 0;
|
|
161
|
+
return ida - idb;
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function findRowIndexById(data, rowId) {
|
|
165
|
+
const idCol = data.id;
|
|
166
|
+
if (!Array.isArray(idCol)) return null;
|
|
167
|
+
const idx = idCol.findIndex((v) => v === rowId || typeof v === "number" && v === Number(rowId));
|
|
168
|
+
return idx === -1 ? null : idx;
|
|
169
|
+
}
|
|
170
|
+
function rowAtIndex(data, index) {
|
|
171
|
+
const idCol = data.id;
|
|
172
|
+
if (!Array.isArray(idCol) || index < 0 || index >= idCol.length) return null;
|
|
173
|
+
const keys = Object.keys(data);
|
|
174
|
+
const row = {};
|
|
175
|
+
for (const k of keys) {
|
|
176
|
+
const col = data[k];
|
|
177
|
+
row[k] = Array.isArray(col) ? col[index] : void 0;
|
|
178
|
+
}
|
|
179
|
+
return row;
|
|
180
|
+
}
|
|
181
|
+
async function fetchGristRow(docApi, tableId, rowId) {
|
|
182
|
+
const data = await docApi.fetchTable(tableId);
|
|
183
|
+
const index = findRowIndexById(data, rowId);
|
|
184
|
+
if (index === null) return null;
|
|
185
|
+
return rowAtIndex(data, index);
|
|
186
|
+
}
|
|
187
|
+
function materializeGristRowsFromColumnar(data, options) {
|
|
188
|
+
const order = options?.order ?? "grist";
|
|
189
|
+
if (options?.safeParse) {
|
|
190
|
+
const safeOptions = options.safeParse === true ? {} : options.safeParse;
|
|
191
|
+
const columnsForSafeParse = enrichSafeParseColumnsFromData(data, options?.columns);
|
|
192
|
+
const displayHelpersByRef = {
|
|
193
|
+
...inferDisplayHelpersByNameFromData(data),
|
|
194
|
+
...safeOptions.displayHelpersByRef ?? {}
|
|
195
|
+
};
|
|
196
|
+
const parsed = safeParseGristTableData(data, {
|
|
197
|
+
...safeOptions,
|
|
198
|
+
columns: columnsForSafeParse,
|
|
199
|
+
displayHelpersByRef,
|
|
200
|
+
order
|
|
201
|
+
});
|
|
202
|
+
options.onSafeParseIssues?.(parsed.cellIssues);
|
|
203
|
+
options.onSafeParseCellMeta?.(parsed.cellMeta);
|
|
204
|
+
options.onSafeParseCellMetaByKey?.(parsed.cellMetaByKey);
|
|
205
|
+
const rawSafeRows = rowsFromSafeCellResults(parsed.cellResultsByColumn);
|
|
206
|
+
const orderedSafeRows = order === "none" ? rawSafeRows : orderSafeCellRowsByParsedRows(rawSafeRows, parsed.rows);
|
|
207
|
+
return orderedSafeRows;
|
|
208
|
+
}
|
|
209
|
+
let rows;
|
|
210
|
+
if (options?.columns) {
|
|
211
|
+
const columnIds = Object.keys(data);
|
|
212
|
+
rows = decodeColumnarToReplicaRows(data, columnIds, options.columns);
|
|
213
|
+
} else {
|
|
214
|
+
rows = tableDataToRows(data);
|
|
215
|
+
}
|
|
216
|
+
if (order === "none") return rows;
|
|
217
|
+
if (order === "id") {
|
|
218
|
+
return [...rows].sort((a, b) => {
|
|
219
|
+
const ida = typeof a.id === "number" ? a.id : 0;
|
|
220
|
+
const idb = typeof b.id === "number" ? b.id : 0;
|
|
221
|
+
return ida - idb;
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
return sortRowsByGristOrder(rows);
|
|
225
|
+
}
|
|
226
|
+
async function fetchGristTableRows(docApi, tableId, options) {
|
|
227
|
+
const data = await docApi.fetchTable(tableId);
|
|
228
|
+
return materializeGristRowsFromColumnar(data, options);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export {
|
|
232
|
+
safeParseRowsToDisplayRows,
|
|
233
|
+
computeRowCount,
|
|
234
|
+
decodeColumnarToReplicaRows,
|
|
235
|
+
tableDataToRows,
|
|
236
|
+
sortRowsByGristOrder,
|
|
237
|
+
findRowIndexById,
|
|
238
|
+
rowAtIndex,
|
|
239
|
+
fetchGristRow,
|
|
240
|
+
materializeGristRowsFromColumnar,
|
|
241
|
+
fetchGristTableRows
|
|
242
|
+
};
|
|
243
|
+
//# sourceMappingURL=chunk-JASMM2YC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sdk/helpers/grist-table-data.ts"],"sourcesContent":["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"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// src/emulator/core/mappings.ts
|
|
2
|
+
function resolveMappings(state) {
|
|
3
|
+
if (state.selection.mappings !== void 0 && state.selection.mappings !== null) {
|
|
4
|
+
return state.selection.mappings;
|
|
5
|
+
}
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
resolveMappings
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=chunk-PM3B4WYV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/emulator/core/mappings.ts"],"sourcesContent":["/**\n * Resolves the `mappings` payload that `CustomSectionAPI.mappings()` returns.\n *\n * The playground emulator does **not** infer logical → real columns from\n * names. Mappings come only from `state.selection.mappings` (playground\n * document inspector or tests). When unset, returns `null`.\n */\n\nimport type { GristWidgetColumnMap } from \"../../sdk/types\"\nimport type { EmulatorDocumentState } from \"./types\"\n\nexport function resolveMappings(state: EmulatorDocumentState): GristWidgetColumnMap | null {\n if (state.selection.mappings !== undefined && state.selection.mappings !== null) {\n return state.selection.mappings\n }\n return null\n}\n"],"mappings":";AAWO,SAAS,gBAAgB,OAA2D;AACzF,MAAI,MAAM,UAAU,aAAa,UAAa,MAAM,UAAU,aAAa,MAAM;AAC/E,WAAO,MAAM,UAAU;AAAA,EACzB;AACA,SAAO;AACT;","names":[]}
|