@kymoscope/sdk 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/LICENSE.md +69 -0
  2. package/README.md +138 -2
  3. package/dist/assets/duckdb-browser-eh.worker--3HUwN9R.js +14 -0
  4. package/dist/assets/duckdb-eh-D-nnNw7L.wasm.gz +0 -0
  5. package/dist/assets/h5wasmModule-BCQWU2Gw.js +24295 -0
  6. package/dist/assets/importer.worker-D4is7BLf.js +6 -0
  7. package/dist/assets/offscreenLineWorker-mShhnyXX.js +79 -0
  8. package/dist/assets/parseDataset.worker-PynRsPD8.js +5 -0
  9. package/dist/importers-DivmF3tx.js +5 -0
  10. package/dist/krc-CbE28CjD.js +9 -0
  11. package/dist/kymoscope-embed.js +633 -0
  12. package/dist/kymoscope-sdk.js +616 -0
  13. package/dist/react.js +2 -0
  14. package/dist/styles/charts.css +207 -0
  15. package/dist/styles/fonts/OFL.txt +93 -0
  16. package/dist/styles/fonts/plus-jakarta-sans-italic-latin-ext.woff2 +0 -0
  17. package/dist/styles/fonts/plus-jakarta-sans-italic-latin.woff2 +0 -0
  18. package/dist/styles/fonts/plus-jakarta-sans-latin-ext.woff2 +0 -0
  19. package/dist/styles/fonts/plus-jakarta-sans-latin.woff2 +0 -0
  20. package/dist/styles/tokens.css +218 -0
  21. package/dist/types/charts/AlarmOverlay.d.ts +11 -0
  22. package/dist/types/charts/AnnotationLayer.d.ts +29 -0
  23. package/dist/types/charts/ChartBody.d.ts +52 -0
  24. package/dist/types/charts/PerfHud.d.ts +20 -0
  25. package/dist/types/charts/beacon.d.ts +27 -0
  26. package/dist/types/charts/chartWindow.d.ts +31 -0
  27. package/dist/types/charts/liveOverlay.d.ts +16 -0
  28. package/dist/types/charts/runtime.d.ts +63 -0
  29. package/dist/types/charts/useWorksheetData.d.ts +159 -0
  30. package/dist/types/components/AreaChart.d.ts +21 -0
  31. package/dist/types/components/BarChart.d.ts +30 -0
  32. package/dist/types/components/BoxPlotChart.d.ts +16 -0
  33. package/dist/types/components/BubbleChart.d.ts +24 -0
  34. package/dist/types/components/CalibrationChart.d.ts +15 -0
  35. package/dist/types/components/CdfChart.d.ts +15 -0
  36. package/dist/types/components/ConfusionChart.d.ts +11 -0
  37. package/dist/types/components/ControlChart.d.ts +17 -0
  38. package/dist/types/components/DuckDbMultiBandPanel.d.ts +35 -0
  39. package/dist/types/components/EmbeddingChart.d.ts +25 -0
  40. package/dist/types/components/FlameGraphChart.d.ts +15 -0
  41. package/dist/types/components/GanttChart.d.ts +15 -0
  42. package/dist/types/components/GaugeChart.d.ts +27 -0
  43. package/dist/types/components/HeatmapChart.d.ts +41 -0
  44. package/dist/types/components/HistogramChart.d.ts +15 -0
  45. package/dist/types/components/LossCurveChart.d.ts +17 -0
  46. package/dist/types/components/MultiBandCanvasChart.d.ts +156 -0
  47. package/dist/types/components/ParallelCoordsChart.d.ts +21 -0
  48. package/dist/types/components/PolarChart.d.ts +17 -0
  49. package/dist/types/components/QQPlotChart.d.ts +16 -0
  50. package/dist/types/components/RadarChart.d.ts +17 -0
  51. package/dist/types/components/RocChart.d.ts +12 -0
  52. package/dist/types/components/SankeyChart.d.ts +15 -0
  53. package/dist/types/components/SpectrogramChart.d.ts +16 -0
  54. package/dist/types/components/SpectrogramWaterfall.d.ts +14 -0
  55. package/dist/types/components/SpectrumChart.d.ts +17 -0
  56. package/dist/types/components/TreemapChart.d.ts +14 -0
  57. package/dist/types/components/ViolinChart.d.ts +17 -0
  58. package/dist/types/components/XYScatterChart.d.ts +47 -0
  59. package/dist/types/components/XYZMeshChart.d.ts +68 -0
  60. package/dist/types/components/dashboardGridLayout.d.ts +50 -0
  61. package/dist/types/components/liveFollowEase.d.ts +10 -0
  62. package/dist/types/components/liveWindow.d.ts +63 -0
  63. package/dist/types/components/multiBandHover.d.ts +44 -0
  64. package/dist/types/embed/Attribution.d.ts +5 -0
  65. package/dist/types/embed/license.d.ts +29 -0
  66. package/dist/types/embed/standalone.d.ts +5 -0
  67. package/dist/types/engine/columns.d.ts +44 -0
  68. package/dist/types/engine/createEngine.d.ts +394 -0
  69. package/dist/types/engine/derive.d.ts +27 -0
  70. package/dist/types/engine/emitter.d.ts +6 -0
  71. package/dist/types/engine/errors.d.ts +7 -0
  72. package/dist/types/engine/history.d.ts +29 -0
  73. package/dist/types/engine/kinds.d.ts +9 -0
  74. package/dist/types/engine/liveController.d.ts +27 -0
  75. package/dist/types/engine/store.d.ts +8 -0
  76. package/dist/types/engine/types.d.ts +194 -0
  77. package/dist/types/index.d.ts +69 -0
  78. package/dist/types/lib/annotations.d.ts +37 -0
  79. package/dist/types/lib/beacon.d.ts +62 -0
  80. package/dist/types/lib/colorScheme.d.ts +5 -0
  81. package/dist/types/lib/colormap.d.ts +9 -0
  82. package/dist/types/lib/columnTable.d.ts +37 -0
  83. package/dist/types/lib/data/arrowDecimation.d.ts +11 -0
  84. package/dist/types/lib/data/binnedHeatmap.d.ts +27 -0
  85. package/dist/types/lib/data/columnCaches.d.ts +6 -0
  86. package/dist/types/lib/data/columnarDataSource.d.ts +34 -0
  87. package/dist/types/lib/data/createDataSource.d.ts +23 -0
  88. package/dist/types/lib/data/dataSource.d.ts +75 -0
  89. package/dist/types/lib/data/decimate.d.ts +25 -0
  90. package/dist/types/lib/data/duckDbDataSource.d.ts +42 -0
  91. package/dist/types/lib/data/envelope.d.ts +16 -0
  92. package/dist/types/lib/data/inMemoryDataSource.d.ts +25 -0
  93. package/dist/types/lib/data/liveDataSource.d.ts +77 -0
  94. package/dist/types/lib/data/lruCache.d.ts +11 -0
  95. package/dist/types/lib/data/openDataset.d.ts +24 -0
  96. package/dist/types/lib/data/opfsParquet.d.ts +4 -0
  97. package/dist/types/lib/data/pointColumns.d.ts +59 -0
  98. package/dist/types/lib/data/schemaColumns.d.ts +5 -0
  99. package/dist/types/lib/data/useSeriesPoints.d.ts +48 -0
  100. package/dist/types/lib/data/xyColumns.d.ts +77 -0
  101. package/dist/types/lib/dataBounds.d.ts +10 -0
  102. package/dist/types/lib/datasetColors.d.ts +19 -0
  103. package/dist/types/lib/datasetColumns.d.ts +3 -0
  104. package/dist/types/lib/datasetMerge.d.ts +8 -0
  105. package/dist/types/lib/duckdb/duckdbInstance.d.ts +7 -0
  106. package/dist/types/lib/duckdb/fileIdentity.d.ts +9 -0
  107. package/dist/types/lib/duckdb/sql.d.ts +124 -0
  108. package/dist/types/lib/fft.d.ts +15 -0
  109. package/dist/types/lib/folderTree.d.ts +29 -0
  110. package/dist/types/lib/gaugeColors.d.ts +32 -0
  111. package/dist/types/lib/importers/arrowSink.d.ts +35 -0
  112. package/dist/types/lib/importers/blobReader.d.ts +22 -0
  113. package/dist/types/lib/importers/bytes.d.ts +11 -0
  114. package/dist/types/lib/importers/combine.d.ts +30 -0
  115. package/dist/types/lib/importers/comtrade/format.d.ts +2 -0
  116. package/dist/types/lib/importers/comtrade/importer.d.ts +25 -0
  117. package/dist/types/lib/importers/dbc.d.ts +45 -0
  118. package/dist/types/lib/importers/edf/format.d.ts +2 -0
  119. package/dist/types/lib/importers/edf/importer.d.ts +7 -0
  120. package/dist/types/lib/importers/expr.d.ts +10 -0
  121. package/dist/types/lib/importers/formats.d.ts +23 -0
  122. package/dist/types/lib/importers/hdf5/format.d.ts +4 -0
  123. package/dist/types/lib/importers/hdf5/h5.d.ts +76 -0
  124. package/dist/types/lib/importers/hdf5/h5wasmModule.d.ts +1 -0
  125. package/dist/types/lib/importers/hdf5/importer.d.ts +2 -0
  126. package/dist/types/lib/importers/importer.worker.d.ts +5 -0
  127. package/dist/types/lib/importers/importers.d.ts +4 -0
  128. package/dist/types/lib/importers/limits.d.ts +28 -0
  129. package/dist/types/lib/importers/mat/format.d.ts +2 -0
  130. package/dist/types/lib/importers/mat/group.d.ts +19 -0
  131. package/dist/types/lib/importers/mat/importer.d.ts +2 -0
  132. package/dist/types/lib/importers/mat/v5.d.ts +24 -0
  133. package/dist/types/lib/importers/mat/v73.d.ts +13 -0
  134. package/dist/types/lib/importers/mdf/blocks3.d.ts +3 -0
  135. package/dist/types/lib/importers/mdf/blocks4.d.ts +7 -0
  136. package/dist/types/lib/importers/mdf/bus.d.ts +59 -0
  137. package/dist/types/lib/importers/mdf/conversion.d.ts +48 -0
  138. package/dist/types/lib/importers/mdf/decode.d.ts +15 -0
  139. package/dist/types/lib/importers/mdf/format.d.ts +2 -0
  140. package/dist/types/lib/importers/mdf/importer.d.ts +19 -0
  141. package/dist/types/lib/importers/mdf/model.d.ts +75 -0
  142. package/dist/types/lib/importers/mdf/records.d.ts +31 -0
  143. package/dist/types/lib/importers/mdf/stream.d.ts +42 -0
  144. package/dist/types/lib/importers/memorySink.d.ts +11 -0
  145. package/dist/types/lib/importers/names.d.ts +2 -0
  146. package/dist/types/lib/importers/openImport.d.ts +33 -0
  147. package/dist/types/lib/importers/registry.d.ts +24 -0
  148. package/dist/types/lib/importers/run.d.ts +35 -0
  149. package/dist/types/lib/importers/session.d.ts +28 -0
  150. package/dist/types/lib/importers/tdms/format.d.ts +2 -0
  151. package/dist/types/lib/importers/tdms/importer.d.ts +10 -0
  152. package/dist/types/lib/importers/types.d.ts +172 -0
  153. package/dist/types/lib/importers/workerCore.d.ts +44 -0
  154. package/dist/types/lib/importers/xdf/format.d.ts +2 -0
  155. package/dist/types/lib/importers/xdf/importer.d.ts +2 -0
  156. package/dist/types/lib/importers/zlib.d.ts +13 -0
  157. package/dist/types/lib/kde.d.ts +2 -0
  158. package/dist/types/lib/live/buffer.d.ts +265 -0
  159. package/dist/types/lib/live/columnsWire.d.ts +24 -0
  160. package/dist/types/lib/live/crypto.d.ts +157 -0
  161. package/dist/types/lib/live/liveMathChannels.d.ts +11 -0
  162. package/dist/types/lib/live/recorder.d.ts +32 -0
  163. package/dist/types/lib/live/stream.d.ts +33 -0
  164. package/dist/types/lib/live/timeBuckets.d.ts +71 -0
  165. package/dist/types/lib/live/types.d.ts +72 -0
  166. package/dist/types/lib/live/wire.d.ts +25 -0
  167. package/dist/types/lib/mathChannels/compile.d.ts +22 -0
  168. package/dist/types/lib/mathChannels/evaluator.d.ts +40 -0
  169. package/dist/types/lib/mathChannels/functions/conditional.d.ts +2 -0
  170. package/dist/types/lib/mathChannels/functions/filters.d.ts +7 -0
  171. package/dist/types/lib/mathChannels/functions/index.d.ts +3 -0
  172. package/dist/types/lib/mathChannels/functions/reference.d.ts +7 -0
  173. package/dist/types/lib/mathChannels/functions/windowOps.d.ts +11 -0
  174. package/dist/types/lib/mathChannels/index.d.ts +13 -0
  175. package/dist/types/lib/mathChannels/io.d.ts +18 -0
  176. package/dist/types/lib/mathChannels/mathjsSetup.d.ts +7 -0
  177. package/dist/types/lib/mathChannels/parseMath.d.ts +12 -0
  178. package/dist/types/lib/mathChannels/parser.d.ts +22 -0
  179. package/dist/types/lib/mathChannels/types.d.ts +34 -0
  180. package/dist/types/lib/mathChannels/usedChannels.d.ts +14 -0
  181. package/dist/types/lib/moduleWorker.d.ts +7 -0
  182. package/dist/types/lib/panelChartSettings.d.ts +438 -0
  183. package/dist/types/lib/parseDataset.d.ts +74 -0
  184. package/dist/types/lib/parseDataset.worker.d.ts +1 -0
  185. package/dist/types/lib/parseDatasetWorkerClient.d.ts +32 -0
  186. package/dist/types/lib/perfLog.d.ts +4 -0
  187. package/dist/types/lib/quantiles.d.ts +10 -0
  188. package/dist/types/lib/recordings/csvWriter.d.ts +10 -0
  189. package/dist/types/lib/recordings/export.d.ts +26 -0
  190. package/dist/types/lib/recordings/importer.d.ts +2 -0
  191. package/dist/types/lib/recordings/keys.d.ts +23 -0
  192. package/dist/types/lib/recordings/krc.d.ts +38 -0
  193. package/dist/types/lib/recordings/mf4Writer.d.ts +20 -0
  194. package/dist/types/lib/render/chartFonts.d.ts +10 -0
  195. package/dist/types/lib/render/colorBucket.d.ts +6 -0
  196. package/dist/types/lib/render/contours.d.ts +16 -0
  197. package/dist/types/lib/render/cssColor.d.ts +2 -0
  198. package/dist/types/lib/render/frames.d.ts +46 -0
  199. package/dist/types/lib/render/gl3d.d.ts +63 -0
  200. package/dist/types/lib/render/glLines.d.ts +62 -0
  201. package/dist/types/lib/render/glPlot.d.ts +36 -0
  202. package/dist/types/lib/render/glPoints.d.ts +37 -0
  203. package/dist/types/lib/render/glSeries.d.ts +67 -0
  204. package/dist/types/lib/render/heatGrid.d.ts +9 -0
  205. package/dist/types/lib/render/hitGrid.d.ts +16 -0
  206. package/dist/types/lib/render/liveZoomBus.d.ts +18 -0
  207. package/dist/types/lib/render/margins.d.ts +14 -0
  208. package/dist/types/lib/render/offscreenLineRenderer.d.ts +53 -0
  209. package/dist/types/lib/render/offscreenLineWorker.d.ts +1 -0
  210. package/dist/types/lib/render/plotFrame.d.ts +36 -0
  211. package/dist/types/lib/render/resample.d.ts +18 -0
  212. package/dist/types/lib/render/sharedGl.d.ts +38 -0
  213. package/dist/types/lib/render/useGlPlot.d.ts +7 -0
  214. package/dist/types/lib/savedLayout.d.ts +45 -0
  215. package/dist/types/lib/stft.d.ts +37 -0
  216. package/dist/types/lib/workspaces/encrypted.d.ts +50 -0
  217. package/dist/types/lib/workspaces/io.d.ts +120 -0
  218. package/dist/types/lib/xyFit.d.ts +14 -0
  219. package/dist/types/lib/yAxisTicks.d.ts +28 -0
  220. package/dist/types/react/index.d.ts +32 -0
  221. package/dist/types/types/loadedDataset.d.ts +42 -0
  222. package/dist/types/types/plotSeries.d.ts +27 -0
  223. package/package.json +63 -4
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Offline license check. A key is `b64url(JSON payload).b64url(Ed25519 sig)`
3
+ * where the signature covers the first segment and the payload is
4
+ * `{ sub, domains, exp, kid? }` (`exp` in unix seconds; a domain may be `*.x.com`;
5
+ * `kid` picks the public key, "1" when absent).
6
+ * Keys are minted with `scripts/license.mjs`; only the public key ships here.
7
+ *
8
+ * Every host needs a key that lists it — including localhost, so a desktop
9
+ * shell or intranet tool served from localhost is not licensed for free. A
10
+ * failed check never breaks the workspace; it shows the attribution badge.
11
+ * Enforcement beyond that is contractual (see LICENSE.md): code that runs in
12
+ * a browser can always be patched.
13
+ */
14
+ /** Ed25519 public keys (raw, base64url) by key id. "1" is the original key; a key without `kid` uses "1".
15
+ * To rotate: add the next key here, release the SDK, then switch the server's signing key and kid. */
16
+ export declare const KYMOSCOPE_LICENSE_PUBLIC_KEYS: Record<string, string>;
17
+ export type LicenseResult = {
18
+ ok: true;
19
+ sub: string;
20
+ exp: number;
21
+ } | {
22
+ ok: false;
23
+ reason: 'missing' | 'malformed' | 'bad_signature' | 'expired' | 'domain' | 'unsupported';
24
+ };
25
+ /** Verify against explicit public keys (by kid, or a single key used for every kid) and clock.
26
+ * Internal: tests use it with their own keypair. */
27
+ export declare function verifyLicenseWith(key: string | undefined | null, hostname: string, publicKeys: Record<string, string> | string, nowSec: number): Promise<LicenseResult>;
28
+ /** Check a Kymoscope license key for `hostname` (usually `location.hostname`). */
29
+ export declare function verifyLicense(key: string | undefined | null, hostname: string): Promise<LicenseResult>;
@@ -0,0 +1,5 @@
1
+ /** Entry of the standalone `<script>` build: `window.Kymoscope.createEngine(...)`. */
2
+ export { createEngine } from '../engine/createEngine';
3
+ export { KymoscopeError } from '../engine/errors';
4
+ export { CHART_KINDS, chartLabel } from '../engine/kinds';
5
+ export { verifyLicense } from './license';
@@ -0,0 +1,44 @@
1
+ import type { LoadedDataset } from '../types/loadedDataset';
2
+ /** Columnar numeric data handed over by the host. */
3
+ export interface ColumnsInput {
4
+ name: string;
5
+ columns: Record<string, ArrayLike<number>>;
6
+ units?: Record<string, string>;
7
+ /**
8
+ * Hand the columns' Float64Arrays over instead of having them copied: the
9
+ * dataset keeps them as they are (non-finite values are turned into NaN in
10
+ * place). The host must not touch them afterwards. For data replaced many
11
+ * times a second, where the copy is most of the cost.
12
+ */
13
+ transfer?: boolean;
14
+ }
15
+ /**
16
+ * Above this many rows host columns stay columnar (no row objects at all):
17
+ * decimated per viewport like a large file, but no laps and math channels on a
18
+ * sample. Below it the dataset has rows (index-only objects over the columns),
19
+ * so laps and math channels see every row.
20
+ */
21
+ export declare const COLUMNAR_ROWS = 1000000;
22
+ /** Build a dataset from host-provided numeric columns (each is copied once). */
23
+ export declare function datasetFromColumns(id: string, input: ColumnsInput): LoadedDataset;
24
+ /**
25
+ * New values for `names`, written by `fill` straight into arrays the dataset
26
+ * reuses: the ones these columns held two writes ago (double buffering), so a
27
+ * dataset rewritten every frame copies nothing and leaves no garbage. `fill`
28
+ * gets one Float64Array per name, of the dataset's length, and must set every
29
+ * value; it may read the current values (`current`). The reused arrays are
30
+ * first dropped from every cache that assumed them unchanging.
31
+ */
32
+ export declare function writeColumns(ds: LoadedDataset, names: string[], fill: (cols: Record<string, Float64Array>, current: Record<string, Float64Array | undefined>) => void): LoadedDataset;
33
+ /**
34
+ * `ds` with the given columns replaced (or added); the others are kept as they
35
+ * are, not copied. Same id, name and units, so everything keyed by the dataset
36
+ * (selection, colour, offset, laps, charts) carries over.
37
+ */
38
+ export declare function replaceColumns(ds: LoadedDataset, input: Record<string, ArrayLike<number>>, transfer?: boolean): LoadedDataset;
39
+ /**
40
+ * The whole dataset as CSV, for a dataset built from columns (it keeps only a
41
+ * preview): what `raw()` hands out and what a re-parse reads. Made on demand
42
+ * (cached), never on load or on each replaceColumns.
43
+ */
44
+ export declare function columnsText(ds: LoadedDataset): string;
@@ -0,0 +1,394 @@
1
+ /**
2
+ * The Kymoscope engine: every capability of the Kymoscope app as a UI-free API.
3
+ *
4
+ * Domain state lives in one immutable store. Operations replace it; `subscribe`
5
+ * and `on('change')` fire once per effective change. Charts are mounted with
6
+ * `engine.charts.mount()` (see charts/), which reads the same state.
7
+ */
8
+ import type { Worksheet } from '../lib/savedLayout';
9
+ import { type ChartAnnotation } from '../lib/annotations';
10
+ import type { LoadedDataset } from '../types/loadedDataset';
11
+ import type { PlotSeries } from '../types/plotSeries';
12
+ import { type PanelChartSettings } from '../lib/panelChartSettings';
13
+ import type { MathChannel, MathChannelPatch } from '../lib/mathChannels/types';
14
+ import type { EvalResult } from '../lib/mathChannels/types';
15
+ import type { LiveConnection, LiveStreamDescriptor, LiveStreamOptions, LocalLiveStream, StreamKeyProvider } from '../lib/live/types';
16
+ import type { ChannelChooser } from '../lib/importers/types';
17
+ import type { RecordingKey } from '../lib/recordings/keys';
18
+ import { DATASET_PALETTE } from '../lib/datasetColors';
19
+ import { type BeaconConfig } from '../lib/beacon';
20
+ import { type Dataset, type DatasetParseConfig } from '../lib/parseDataset';
21
+ import { parseKymoscopeFile, type WorkbookBody } from '../lib/workspaces/io';
22
+ import { type WorkbookFileOptions } from '../lib/workspaces/encrypted';
23
+ import { canPickDirectory, isImportable, pickDirectoryEntries, type DirNode, type FolderEntry } from '../lib/folderTree';
24
+ import { type ChartView, type MountOptions } from '../charts/runtime';
25
+ import { type ChartWindow, type ChartWindowOptions } from '../charts/chartWindow';
26
+ import { type ColumnsInput } from './columns';
27
+ import { createDerivations } from './derive';
28
+ import { chartLabel, type ChartKindInfo } from './kinds';
29
+ import { createLiveController } from './liveController';
30
+ import { createStore } from './store';
31
+ import { type ChartKind, type ChartModel, type EngineEvents, type EngineOptions, type EngineState, type KymoscopeLimits } from './types';
32
+ export declare const DEFAULT_X = "ET";
33
+ /** Default series / scatter colours (matches the app). */
34
+ export declare const PALETTE: string[];
35
+ /** Internal domain state. Worksheets keep the persisted shape (panels + settings maps). */
36
+ export interface InternalState {
37
+ datasets: LoadedDataset[];
38
+ selection: string[];
39
+ worksheets: Worksheet[];
40
+ activeWorksheetId: string;
41
+ xRanges: Record<string, [number, number] | null>;
42
+ offsets: Record<string, number>;
43
+ colors: Record<string, string>;
44
+ liveWindowSeconds: number | null;
45
+ live: LiveConnection[];
46
+ mathChannels: MathChannel[];
47
+ limits: KymoscopeLimits;
48
+ palette: string[];
49
+ importing: number;
50
+ /** null until the licence check finishes. */
51
+ licensed: boolean | null;
52
+ history: EngineState['history'];
53
+ }
54
+ export type ImportSource = File | FolderEntry;
55
+ export interface ImportResult {
56
+ ids: string[];
57
+ errors: {
58
+ file: string;
59
+ error: Error;
60
+ }[];
61
+ /** The signal aborted: the file being read was dropped and later ones skipped. */
62
+ cancelled?: boolean;
63
+ }
64
+ export interface ImportOptions {
65
+ /** Cancels the import (KymoscopeError 'cancelled' for the file being read; later files are skipped). */
66
+ signal?: AbortSignal;
67
+ /** Asked before reading a binary file with more than 2,000 channels or an ambiguous structure (HDF5); null cancels it. Without it every channel is read. */
68
+ chooseChannels?: ChannelChooser;
69
+ /** Ask `chooseChannels` for every binary file. */
70
+ pickChannels?: boolean;
71
+ }
72
+ /** A cloud recording to open: its chunks in seq order (Blobs straight from the downloads) and its keys. */
73
+ export interface RecordingInput {
74
+ recordingId: string;
75
+ name: string;
76
+ chunks: Blob[];
77
+ keys: RecordingKey[];
78
+ startTime: string | null;
79
+ }
80
+ export interface Snapshot {
81
+ datasets: LoadedDataset[];
82
+ worksheets: Worksheet[];
83
+ activeWorksheetId: string;
84
+ selection: string[];
85
+ }
86
+ export interface Exported {
87
+ json: unknown;
88
+ blob: Blob;
89
+ filename: string;
90
+ }
91
+ export type ParsedFile = Awaited<ReturnType<typeof parseKymoscopeFile>>;
92
+ export type ChartSettingsPatch = Partial<PanelChartSettings> & Record<string, unknown>;
93
+ export declare function createEngine(options?: EngineOptions): {
94
+ getState: () => EngineState;
95
+ subscribe(listener: () => void): () => void;
96
+ on: <K extends keyof EngineEvents>(event: K, cb: (payload: EngineEvents[K]) => void) => () => void;
97
+ /** Resolves to whether this page holds a valid license, after the first check (of licenseKey, or of the
98
+ * first license.get when given). Later renewals update isLicensed() and getState().licensed. */
99
+ licensed: Promise<boolean>;
100
+ /** null until the license check finishes. */
101
+ isLicensed: () => boolean | null;
102
+ /** Update host options at runtime (e.g. limits once the user's plan loads). */
103
+ setOptions(next: Partial<Omit<EngineOptions, "licenseKey" | "license">>): void;
104
+ datasets: {
105
+ importFiles: (sources: ImportSource[], options?: ImportOptions) => Promise<ImportResult>;
106
+ /**
107
+ * Get the next import ready (one-shot): its worker starts now and, for 'hdf5' / 'mat' in
108
+ * `formats`, loads h5wasm (~4.8 MB, fetched once). Call it on idle, or when the user
109
+ * shows import intent (drag over, file picker); after it is used, another is prepared on idle.
110
+ */
111
+ warmImport(options?: {
112
+ formats?: string[];
113
+ }): void;
114
+ /** A combined multi-rate import as one dataset per group (each read again from its file). Returns the new ids. */
115
+ splitByRate(id: string): Promise<string[]>;
116
+ /** A binary import read again with the channels `chooser` picks (it starts from what was read). Null: cancelled, the dataset stays. */
117
+ chooseChannels(id: string, chooser: ChannelChooser): Promise<string[] | null>;
118
+ /** The datasets split from one file (pass any of them), combined again into one. Returns its id. */
119
+ combine(id: string): Promise<string>;
120
+ /**
121
+ * A cloud recording as a dataset: its encrypted chunks (KRC1, in order) decrypted with `keys` in the import
122
+ * worker, in memory or out of core like a file. Returns the id.
123
+ */
124
+ openRecording(input: RecordingInput): Promise<string>;
125
+ /** New chunks of an open recording: the dataset keeps its id, selection, colour, offset and laps. */
126
+ reopenRecording(id: string, input: RecordingInput): Promise<void>;
127
+ /** Add numeric columns from the host (selected immediately). */
128
+ addColumns(input: ColumnsInput): string;
129
+ /**
130
+ * New data for a dataset added with `addColumns`, in place: pass the columns
131
+ * that changed (the others are kept, not copied); new names add columns. The
132
+ * dataset keeps its id, selection, colour, offset and laps, and every chart
133
+ * draws the new data on its next frame — for data that changes many times
134
+ * a second (a simulation, a sweep), instead of adding a dataset per update.
135
+ * `transfer`: hand the Float64Arrays over instead of having them copied
136
+ * (see ColumnsInput.transfer); the host must not touch them afterwards.
137
+ */
138
+ replaceColumns(id: string, columns: Record<string, ArrayLike<number>>, opts?: {
139
+ transfer?: boolean;
140
+ }): void;
141
+ /**
142
+ * New values for columns of a dataset added with `addColumns`, written by
143
+ * `fill` straight into arrays the engine reuses (no copy, no garbage): for
144
+ * data rewritten every frame. `fill(cols, current)` gets one Float64Array
145
+ * per name, of the dataset's length, and must set every value; `current`
146
+ * holds the values being replaced (read-only).
147
+ */
148
+ writeColumns(id: string, names: string[], fill: (cols: Record<string, Float64Array>, current: Record<string, Float64Array | undefined>) => void): void;
149
+ /** Add delimited text (CSV/TSV/.dat), parsed like an imported file. */
150
+ addText(text: string, name?: string): Promise<string>;
151
+ remove(id: string): void;
152
+ /** Remove every dataset. Worksheets and charts are kept. */
153
+ clear(): void;
154
+ /** Re-parse with another config and/or split into laps (both need an in-memory dataset; `beacon: null` always works). */
155
+ /** `selectCycles` ('all' by default): when a selected dataset is split, plot
156
+ * every cycle, or only the one with the lowest value (see bestCycleIndex). */
157
+ configure(id: string, change: {
158
+ parseConfig?: DatasetParseConfig;
159
+ beacon?: BeaconConfig | null;
160
+ selectCycles?: "all" | "best";
161
+ }): Promise<void>;
162
+ setColor(id: string, color: string | null): void;
163
+ setOffset(id: string, x: number): void;
164
+ resetOffsets(): void;
165
+ /** Replace the plotted ids (dataset, lap or live ids). */
166
+ select(ids: string[]): void;
167
+ /** Toggle one id. A dataset split into laps toggles all its laps (on unless all are on). */
168
+ toggle(id: string): void;
169
+ selectAll(): void;
170
+ deselectAll(): void;
171
+ /**
172
+ * The parsed content of one dataset (for re-configure dialogs, previews).
173
+ * `rawText` is made when read: a dataset from columns has no text and builds
174
+ * its CSV then (cached until its data changes), so reading only `data` or
175
+ * `parseConfig` never pays for it.
176
+ */
177
+ raw(id: string): {
178
+ rawText: string;
179
+ data: Dataset;
180
+ parseConfig: DatasetParseConfig | null;
181
+ kind: "memory" | "duckdb" | "columnar";
182
+ beacon: BeaconConfig | null;
183
+ };
184
+ /** Min/max of a column across the plotted data (out-of-core and live included). */
185
+ columnBounds(column: string): Promise<{
186
+ min: number;
187
+ max: number;
188
+ } | null>;
189
+ };
190
+ folders: {
191
+ fromFileList: (files: FileList | File[]) => FolderEntry[];
192
+ /** Entries from a drag-and-drop (folders are walked recursively). */
193
+ fromDataTransfer(dt: DataTransfer): Promise<FolderEntry[]>;
194
+ canPick: typeof canPickDirectory;
195
+ /** Native directory picker; null when cancelled or unsupported. */
196
+ pick: typeof pickDirectoryEntries;
197
+ tree: (entries: FolderEntry[], rootName?: string) => DirNode;
198
+ isImportable: typeof isImportable;
199
+ };
200
+ worksheets: {
201
+ add(name?: string): string;
202
+ rename(id: string, name: string): void;
203
+ remove(id: string): void;
204
+ reorder(from: number, to: number): void;
205
+ activate(id: string): void;
206
+ /** Change the shared X column. Drops series on it and clears zoom, offsets and colours. */
207
+ setXKey(id: string, key: string): void;
208
+ };
209
+ charts: {
210
+ /** Render a chart into `element` (it fills the element). */
211
+ mount(chartId: string, element: HTMLElement, options?: MountOptions): ChartView;
212
+ /**
213
+ * Open the chart in its own browser window (call it from a click: browsers block other popups).
214
+ * Opening an already open chart focuses its window. Throws `popup_blocked` when the browser refuses.
215
+ */
216
+ openWindow(chartId: string, options?: ChartWindowOptions): ChartWindow;
217
+ /** Font families for every chart's text (canvas text cannot inherit CSS fonts). */
218
+ setFonts(fonts: {
219
+ sans?: string;
220
+ mono?: string;
221
+ }): void;
222
+ kinds: () => ChartKindInfo[];
223
+ label: typeof chartLabel;
224
+ add(kind: ChartKind, o?: {
225
+ worksheetId?: string;
226
+ title?: string;
227
+ settings?: ChartSettingsPatch;
228
+ layout?: ChartModel["layout"];
229
+ }): string;
230
+ remove(chartId: string): void;
231
+ rename(chartId: string, title: string | null): void;
232
+ /**
233
+ * Merge a settings patch (the kind cannot change). Changing an X-Y scatter
234
+ * column auto-scales that axis unless the patch sets the scale too.
235
+ */
236
+ update(chartId: string, patch: ChartSettingsPatch): void;
237
+ /** Replace the whole settings object (same kind). */
238
+ replaceSettings(chartId: string, settings: PanelChartSettings): void;
239
+ setBackground(chartId: string, color: string | null): void;
240
+ /** Add a mark over the chart; returns its id (the one given, or a new one). */
241
+ addAnnotation(chartId: string, annotation: Omit<ChartAnnotation, "id"> & {
242
+ id?: string;
243
+ }): string;
244
+ /** Change one annotation (its kind and id stay). */
245
+ updateAnnotation(chartId: string, annotationId: string, patch: Partial<Omit<ChartAnnotation, "id" | "kind">>): void;
246
+ removeAnnotation(chartId: string, annotationId: string): void;
247
+ /** Replace every annotation of a chart ([] clears them); invalid entries are dropped. */
248
+ setAnnotations(chartId: string, annotations: ChartAnnotation[]): void;
249
+ setLayout(chartId: string, layout: ChartModel["layout"]): void;
250
+ /** Set several layouts at once (e.g. after a grid drag). */
251
+ setLayouts(worksheetId: string, layouts: Record<string, ChartModel["layout"]>): void;
252
+ /** Re-flow a worksheet's charts onto the automatic grid. */
253
+ autoLayout(worksheetId?: string): void;
254
+ get(chartId: string): ChartModel;
255
+ list(worksheetId?: string): ChartModel[];
256
+ addSeries(chartId: string, column?: string): Promise<string | null>;
257
+ /** Patch a series; changing its column auto-scales it to the new column. */
258
+ updateSeries(chartId: string, seriesId: string, patch: Partial<PlotSeries>): Promise<void>;
259
+ removeSeries(chartId: string, seriesId: string): void;
260
+ /** New order as series ids (unknown ids are ignored, missing ones appended). */
261
+ reorderSeries(chartId: string, seriesIds: string[]): void;
262
+ /** Fit a series' Y scale to its column's data range. */
263
+ autoScaleSeries(chartId: string, seriesId: string): Promise<void>;
264
+ };
265
+ view: {
266
+ /** Linked X zoom of a worksheet (default: active); null = full range. */
267
+ setXRange(range: [number, number] | null, worksheetId?: string): void;
268
+ resetZoom(worksheetId?: string): void;
269
+ setLiveWindow(seconds: number | null): void;
270
+ };
271
+ mathChannels: {
272
+ /** Create a channel (`name` and `formula` at least). Returns it. */
273
+ create(input: Partial<MathChannel> & {
274
+ name: string;
275
+ formula: string;
276
+ }): MathChannel;
277
+ update(id: string, patch: MathChannelPatch): void;
278
+ remove(id: string): void;
279
+ /** Import channels from a JSON export. Returns what was imported and per-entry errors. */
280
+ importFile(file: File): Promise<{
281
+ channels: MathChannel[];
282
+ errors: string[];
283
+ }>;
284
+ /** Add several channels at once (e.g. parsed from a file by your own UI). */
285
+ importMany(channels: MathChannel[]): Promise<MathChannel[]>;
286
+ exportBlob: () => Blob;
287
+ /** Evaluate a channel against the plotted data (for editors). */
288
+ preview(channel: MathChannel, worksheetId?: string): EvalResult | null;
289
+ /** Host-owned store: push the authoritative list after it changes (an open workbook's channels stay). */
290
+ sync(channels: MathChannel[]): void;
291
+ };
292
+ live: {
293
+ /** A stream the page feeds itself (no relay, no crypto). */
294
+ openLocal(name: string, options?: LiveStreamOptions): LocalLiveStream;
295
+ /** Join an end-to-end encrypted relay stream with its password, the API key
296
+ * that publishes it, its key (CryptoKey, e.g. from `unwrapStreamKey`), or a
297
+ * StreamKeyProvider (streams of end-to-end encrypted projects). */
298
+ connect(stream: LiveStreamDescriptor, secret: string | CryptoKey | StreamKeyProvider, options?: LiveStreamOptions): Promise<void>;
299
+ /** Record every sample the connection receives from now on (whole, not just
300
+ * the live window the charts keep). */
301
+ startRecording(id: string): void;
302
+ /** Stop recording. Returns the recording as CSV parts (`new Blob(csv)` to
303
+ * save it) with its row count, stream name and start time; null if none ran. */
304
+ stopRecording(id: string): {
305
+ csv: string[];
306
+ rows: number;
307
+ name: string;
308
+ startedAt: number;
309
+ } | null;
310
+ /** Samples currently buffered for a connection (cheap; poll it for counters). */
311
+ rowCount(id: string): number;
312
+ disconnect(id: string): void;
313
+ };
314
+ io: {
315
+ /** True when there are charts that a workspace import would replace. */
316
+ hasWork: () => boolean;
317
+ exportWorksheet(worksheetId?: string): Exported;
318
+ exportChart(chartId: string): Exported;
319
+ /** What a file contains: 'workspace' | 'worksheet' | 'chart' | 'encrypted' | 'error'. */
320
+ parseFile: (file: File) => Promise<ParsedFile>;
321
+ /** Envelope v2 of every worksheet as JSON (plain: embedders' own storage). Limit: workspaces-io. */
322
+ exportWorkspace(): Exported;
323
+ /** A `.kymo` file: v3 under a scope key the host holds (AES-256-GCM; `password`: an optional PBKDF2-SHA256 600k
324
+ * layer inside), or, given only a password, v2 (password-only, for embedders without scope keys). Limit:
325
+ * workspaces-io. */
326
+ exportWorkspaceEncrypted(options: WorkbookFileOptions | string): Promise<Exported>;
327
+ /** Replace every worksheet with a v1, v2 or `.kymo` file (datasets are kept). A v3 file needs its scope's `key`
328
+ * (parseFile says which scope) and, when it has one inside, the `password`. */
329
+ importWorkspace(src: File | Blob | string | object, options?: {
330
+ password?: string;
331
+ key?: Uint8Array;
332
+ }): Promise<{
333
+ worksheets: number;
334
+ errors: string[];
335
+ }>;
336
+ /** The body a cloud workbook stores (not gated by workspaces-io: the host gates its cloud). */
337
+ toWorkbook: () => WorkbookBody;
338
+ /** Open a cloud workbook's body (v1 or v2) over whatever data is loaded. */
339
+ openWorkbook(body: unknown): {
340
+ worksheets: number;
341
+ errors: string[];
342
+ };
343
+ /** Append an exported worksheet and activate it. */
344
+ importWorksheet(src: File | Blob | string | object): Promise<string>;
345
+ /** Add an exported chart to a worksheet (default: active). */
346
+ importChart(src: File | Blob | string | object, worksheetId?: string): Promise<string>;
347
+ /** Everything needed to rebuild this state later in the same page. */
348
+ snapshot(): Snapshot;
349
+ /** Replace datasets, worksheets and selection at once (e.g. a prepared demo). */
350
+ restore(snap: Snapshot): void;
351
+ /** Empty workspace: no datasets, one blank worksheet. Live connections stay. */
352
+ reset(): void;
353
+ };
354
+ /** Undo/redo of the analysis: worksheets, charts, settings, annotations and the math channels the workbook carries.
355
+ * Not data loading, live streams, selection, zoom, switching worksheets, own (library) math channels (saved to the
356
+ * library at once) or laps settings (they follow the data). During a live stream an undo may plot against the
357
+ * step's X key until the next connection change. Cleared by io.openWorkbook/importWorkspace/restore/reset.
358
+ * `getState().history` says whether there is a step to undo or redo. */
359
+ history: {
360
+ undo(): void;
361
+ redo(): void;
362
+ clear(): void;
363
+ };
364
+ data: {
365
+ /**
366
+ * The plotted datasets and live streams merged into one table, with every
367
+ * math channel materialised (against the worksheet's X key). null when
368
+ * nothing is plotted. For tables, previews and custom renderers.
369
+ */
370
+ merged(worksheetId?: string): Dataset | null;
371
+ /** Like merged() without the math channels: no row cloning, stable until the data changes. */
372
+ plotted(): Dataset | null;
373
+ };
374
+ dispose(): void;
375
+ };
376
+ export type KymoscopeEngine = ReturnType<typeof createEngine>;
377
+ /** Internal access for the chart runtime (not part of the public API). */
378
+ export interface EngineInternals {
379
+ store: ReturnType<typeof createStore<InternalState>>;
380
+ derived: () => {
381
+ laps: ReturnType<ReturnType<typeof createDerivations>['lapsByDataset']>;
382
+ selectedDatasets: LoadedDataset[];
383
+ selectedLive: LiveConnection[];
384
+ staticDataset: Dataset | null;
385
+ merged: Dataset | null;
386
+ channels: MathChannel[];
387
+ numericColumns: string[];
388
+ allColumns: string[];
389
+ columnUnits: Record<string, string>;
390
+ };
391
+ live: ReturnType<typeof createLiveController>;
392
+ }
393
+ export declare function getInternals(engine: KymoscopeEngine): EngineInternals;
394
+ export { DATASET_PALETTE };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared derivations over the engine's domain state. Each selector recomputes only
3
+ * when its inputs change identity (live frames never change these inputs: the
4
+ * live snapshot is rebuilt only on structural changes).
5
+ */
6
+ import type { LoadedDataset } from '../types/loadedDataset';
7
+ import type { LiveConnection } from '../lib/live/types';
8
+ import type { MathChannel } from '../lib/mathChannels/types';
9
+ import type { Dataset } from '../lib/parseDataset';
10
+ export interface LapGroup {
11
+ laps: LoadedDataset[];
12
+ values: (number | null)[];
13
+ unit: string;
14
+ isTime: boolean;
15
+ }
16
+ export declare function createDerivations(): {
17
+ lapsByDataset: (datasets: LoadedDataset[]) => Record<string, LapGroup>;
18
+ selectedDatasets: (datasets: LoadedDataset[], laps: Record<string, LapGroup>, selection: string[]) => LoadedDataset[];
19
+ selectedLive: (live: LiveConnection[], selection: string[]) => LiveConnection[];
20
+ staticDataset: (selected: LoadedDataset[]) => Dataset | null;
21
+ mergedDataset: (stat: Dataset | null, live: LiveConnection[]) => Dataset | null;
22
+ mergedParts: (selected: LoadedDataset[], live: LiveConnection[]) => Dataset[];
23
+ numericColumns: (merged: Dataset | null, selected: LoadedDataset[], channels: MathChannel[], parts?: Dataset[] | undefined) => string[];
24
+ allColumns: (merged: Dataset | null, selected: LoadedDataset[], channels: MathChannel[]) => string[];
25
+ columnUnits: (merged: Dataset | null, channels: MathChannel[]) => Record<string, string>;
26
+ };
27
+ export type Derivations = ReturnType<typeof createDerivations>;
@@ -0,0 +1,6 @@
1
+ /** Minimal typed event emitter. A throwing listener never breaks the others. */
2
+ export declare function createEmitter<E extends Record<string, unknown>>(): {
3
+ on<K extends keyof E>(event: K, cb: (payload: E[K]) => void): () => void;
4
+ emit<K extends keyof E>(event: K, payload: E[K]): void;
5
+ clear(): void;
6
+ };
@@ -0,0 +1,7 @@
1
+ export type KymoscopeErrorCode = 'column_length_mismatch' | 'no_columns' | 'bad_workspace' | 'bad_file' | 'import_vetoed' | 'limit_reached' | 'not_found' | 'wrong_password' | 'password_required' | 'scope_key_required' | 'relay_unavailable' | 'disposed' | 'invalid_argument' | 'export_failed' | 'popup_blocked' | 'cancelled';
2
+ /** Every failure the SDK reports. `detail` carries structured context (e.g. `{reason}` for limits). */
3
+ export declare class KymoscopeError extends Error {
4
+ readonly code: KymoscopeErrorCode;
5
+ readonly detail?: unknown;
6
+ constructor(code: KymoscopeErrorCode, detail?: unknown);
7
+ }
@@ -0,0 +1,29 @@
1
+ /** Undo/redo of the analysis: stacks of analysis snapshots (spec 2026-10-20-undo-start-samples-design.md §A).
2
+ * `observe` is called after every effective change with the current snapshot; one that is not `equals` to the current
3
+ * records a step, and a burst of changes within `mergeMs` of the previous recorded one stays one step. In memory only. */
4
+ export interface HistoryOptions<T> {
5
+ limit: number;
6
+ mergeMs: number;
7
+ now: () => number;
8
+ equals?: (a: T, b: T) => boolean;
9
+ }
10
+ export declare class AnalysisHistory<T> {
11
+ private past;
12
+ private future;
13
+ private current;
14
+ private lastAt;
15
+ private readonly o;
16
+ private readonly equals;
17
+ constructor(o: HistoryOptions<T>);
18
+ get canUndo(): boolean;
19
+ get canRedo(): boolean;
20
+ /** The analysis after a change. True when it recorded (or extended) a step. */
21
+ observe(snap: T): boolean;
22
+ /** The analysis changed without a user edit (loaded data adapted it, a step was applied): the new current, no step. */
23
+ rebase(snap: T): void;
24
+ /** The snapshot to apply, or null. */
25
+ undo(): T | null;
26
+ redo(): T | null;
27
+ /** Forget both stacks; `baseline` is the analysis now on screen. */
28
+ clear(baseline: T | null): void;
29
+ }
@@ -0,0 +1,9 @@
1
+ import type { ChartKind } from './types';
2
+ export interface ChartKindInfo {
3
+ kind: ChartKind;
4
+ label: string;
5
+ }
6
+ /** Every chart kind the SDK renders, in menu order. */
7
+ export declare const CHART_KINDS: ChartKindInfo[];
8
+ /** Human label of a chart kind (e.g. 'Multi-band'). */
9
+ export declare function chartLabel(kind: ChartKind): string;
@@ -0,0 +1,27 @@
1
+ import { type LiveRelayConfig } from '../lib/live/stream';
2
+ import type { MathChannel } from '../lib/mathChannels/types';
3
+ import type { LiveConnection, LiveStreamDescriptor, LiveStreamOptions, LocalLiveStream, StreamKeyProvider } from '../lib/live/types';
4
+ export interface LiveController {
5
+ connections(): LiveConnection[];
6
+ /** `secret`: the stream password, the API key that publishes it, the stream key
7
+ * itself (e.g. unwrapped from a device wrap), or a key provider (encrypted projects). */
8
+ connect(stream: LiveStreamDescriptor, secret: string | CryptoKey | StreamKeyProvider, options?: LiveStreamOptions): Promise<void>;
9
+ disconnect(id: string): void;
10
+ /** Start recording every sample `id` receives (no-op if already recording). */
11
+ startRecording(id: string): void;
12
+ /** Stop recording; the recording as CSV parts, or null if none was running. */
13
+ stopRecording(id: string): {
14
+ csv: string[];
15
+ rows: number;
16
+ name: string;
17
+ startedAt: number;
18
+ } | null;
19
+ openLocal(name: string, options?: LiveStreamOptions): LocalLiveStream;
20
+ setRelay(relay: LiveRelayConfig | undefined): void;
21
+ /** Math channels evaluated on every stream (as computed buffer columns). */
22
+ setMathChannels(channels: MathChannel[]): void;
23
+ dispose(): void;
24
+ }
25
+ export declare function createLiveController(initialRelay: LiveRelayConfig | undefined, onChange: (connections: LiveConnection[]) => void,
26
+ /** The host's defaults for new streams (read when one opens). */
27
+ defaults?: () => LiveStreamOptions): LiveController;
@@ -0,0 +1,8 @@
1
+ /** Immutable-snapshot store: `set` replaces the state; listeners run only on a real change. */
2
+ export declare function createStore<S>(initial: S): {
3
+ get: () => S;
4
+ set(updater: (prev: S) => S): void;
5
+ subscribe(listener: () => void): () => void;
6
+ };
7
+ /** Caches the last result; recomputes only when an argument changes identity. */
8
+ export declare function memoizeLast<A extends unknown[], R>(fn: (...args: A) => R): (...args: A) => R;