@fjader/svelte 0.2.4 → 0.2.5

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 (139) hide show
  1. package/README.md +1 -1
  2. package/dist/animation/EntranceGate.d.ts +52 -0
  3. package/dist/animation/EntranceGate.js +93 -0
  4. package/dist/animation/TransitionClock.d.ts +61 -30
  5. package/dist/animation/TransitionClock.js +67 -29
  6. package/dist/animation/transition-log.d.ts +41 -0
  7. package/dist/animation/transition-log.js +57 -0
  8. package/dist/components/axes/AxisBottom.svelte +4 -2
  9. package/dist/components/axes/AxisCenter.svelte +4 -2
  10. package/dist/components/axes/AxisLeft.svelte +4 -2
  11. package/dist/components/axes/BandAxisTicks.svelte +3 -2
  12. package/dist/components/charts/ChartCell.svelte +32 -5
  13. package/dist/components/charts/ChartCell.svelte.d.ts +9 -0
  14. package/dist/components/charts/ChartContainer.svelte +45 -3
  15. package/dist/components/charts/ChartContainer.svelte.d.ts +7 -1
  16. package/dist/components/charts/ChartHover.svelte.d.ts +14 -0
  17. package/dist/components/charts/ChartHover.svelte.js +22 -0
  18. package/dist/components/charts/ChartVisibility.svelte.d.ts +43 -0
  19. package/dist/components/charts/ChartVisibility.svelte.js +67 -0
  20. package/dist/components/guides/Grid.svelte +12 -1
  21. package/dist/components/marks/AreaMarkX.svelte +16 -4
  22. package/dist/components/marks/AreaMarkY.svelte +16 -4
  23. package/dist/components/marks/BarMarkX.svelte +7 -3
  24. package/dist/components/marks/BarMarkY.svelte +7 -3
  25. package/dist/components/marks/DotsMark.svelte +6 -3
  26. package/dist/components/marks/LineMark.svelte +11 -8
  27. package/dist/components/marks/ParallelCoordinatesMark.svelte +7 -2
  28. package/dist/components/marks/ParallelCoordinatesMark.svelte.d.ts +2 -0
  29. package/dist/components/marks/RectMark.svelte +6 -3
  30. package/dist/components/marks/RectXMark.svelte +7 -3
  31. package/dist/components/marks/RectYMark.svelte +7 -3
  32. package/dist/components/marks/RuleMarkX.svelte +30 -26
  33. package/dist/components/marks/RuleMarkY.svelte +30 -26
  34. package/dist/components/marks/TextMark.svelte +44 -5
  35. package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.d.ts +16 -0
  36. package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.js +398 -54
  37. package/dist/components/marks/lifecycle/domain-queries.d.ts +11 -2
  38. package/dist/components/marks/lifecycle/domain-queries.js +33 -6
  39. package/dist/components/marks/lifecycle/domain-registration.d.ts +6 -1
  40. package/dist/components/marks/lifecycle/domain-registration.js +15 -14
  41. package/dist/components/marks/lifecycle/other-rows.svelte.d.ts +9 -1
  42. package/dist/components/marks/lifecycle/other-rows.svelte.js +2 -2
  43. package/dist/components/marks/lifecycle/query-builder.d.ts +27 -2
  44. package/dist/components/marks/lifecycle/query-builder.js +23 -20
  45. package/dist/components/marks/utils/MarkColor.svelte.d.ts +3 -1
  46. package/dist/components/marks/utils/MarkColor.svelte.js +33 -3
  47. package/dist/components/marks/utils/MarkSeries.svelte.d.ts +14 -1
  48. package/dist/components/marks/utils/MarkSeries.svelte.js +25 -1
  49. package/dist/components/marks/utils/MarkSize.svelte.js +4 -0
  50. package/dist/components/marks/utils/reactive-config.d.ts +1 -1
  51. package/dist/components/marks/utils/reactive-config.js +3 -0
  52. package/dist/components/rendering/Areas.svelte +57 -19
  53. package/dist/components/rendering/Areas.svelte.d.ts +12 -0
  54. package/dist/components/rendering/BaseGPURenderer.svelte.d.ts +1 -1
  55. package/dist/components/rendering/Dots.svelte +23 -3
  56. package/dist/components/rendering/Dots.svelte.d.ts +6 -0
  57. package/dist/components/rendering/GridLine.svelte +13 -1
  58. package/dist/components/rendering/GridLine.svelte.d.ts +5 -0
  59. package/dist/components/rendering/Lines.svelte +48 -13
  60. package/dist/components/rendering/Lines.svelte.d.ts +12 -0
  61. package/dist/components/rendering/ParallelLines.svelte +5 -5
  62. package/dist/components/rendering/Rects.svelte +51 -11
  63. package/dist/components/rendering/Rects.svelte.d.ts +16 -2
  64. package/dist/components/rendering/RuleX.svelte +1 -1
  65. package/dist/components/rendering/RuleY.svelte +1 -1
  66. package/dist/components/rendering/WebGPUCanvas.svelte +4 -2
  67. package/dist/components/rendering/activity-reads.d.ts +41 -24
  68. package/dist/components/rendering/activity-reads.js +54 -31
  69. package/dist/components/rendering/uniform-sizes.d.ts +1 -1
  70. package/dist/components/rendering/uniform-sizes.js +1 -1
  71. package/dist/config/ChartConfig.svelte.d.ts +7 -0
  72. package/dist/config/ChartConfig.svelte.js +17 -0
  73. package/dist/gpu/GPUCoordinator.svelte.d.ts +31 -1
  74. package/dist/gpu/GPUCoordinator.svelte.js +164 -7
  75. package/dist/gpu/MorphedChannels.d.ts +11 -2
  76. package/dist/gpu/MorphedChannels.js +33 -11
  77. package/dist/gpu/ScaleTemplateSystem.d.ts +20 -2
  78. package/dist/gpu/ScaleTemplateSystem.js +58 -2
  79. package/dist/index.d.ts +17 -4
  80. package/dist/index.js +19 -4
  81. package/dist/interactors/TooltipManager.svelte.js +14 -5
  82. package/dist/query-engine/CrossfilterSelection.svelte.d.ts +4 -2
  83. package/dist/query-engine/CrossfilterSelection.svelte.js +2 -0
  84. package/dist/query-engine/FieldActivity.svelte.d.ts +2 -3
  85. package/dist/query-engine/FieldActivity.svelte.js +1 -2
  86. package/dist/query-engine/MarkHandle.svelte.d.ts +28 -1
  87. package/dist/query-engine/MarkHandle.svelte.js +33 -1
  88. package/dist/query-engine/QueryEngine.d.ts +225 -5
  89. package/dist/query-engine/QueryEngine.js +671 -41
  90. package/dist/query-engine/categorical-identity.d.ts +34 -8
  91. package/dist/query-engine/categorical-identity.js +138 -73
  92. package/dist/query-engine/debug/query-log.d.ts +39 -0
  93. package/dist/query-engine/debug/query-log.js +90 -0
  94. package/dist/query-engine/duckdb.d.ts +55 -8
  95. package/dist/query-engine/duckdb.js +82 -30
  96. package/dist/query-engine/index.d.ts +7 -4
  97. package/dist/query-engine/index.js +3 -3
  98. package/dist/query-engine/preagg/BuildQueue.d.ts +33 -13
  99. package/dist/query-engine/preagg/BuildQueue.js +51 -15
  100. package/dist/query-engine/preagg/Preagg.d.ts +46 -3
  101. package/dist/query-engine/preagg/Preagg.js +159 -31
  102. package/dist/query-engine/preagg/plan.d.ts +28 -1
  103. package/dist/query-engine/preagg/plan.js +128 -8
  104. package/dist/query-engine/sliced-lane.d.ts +108 -0
  105. package/dist/query-engine/sliced-lane.js +299 -0
  106. package/dist/query-engine/source-routing.d.ts +106 -0
  107. package/dist/query-engine/source-routing.js +189 -0
  108. package/dist/scales/BandScaleState.svelte.d.ts +9 -1
  109. package/dist/scales/BandScaleState.svelte.js +16 -5
  110. package/dist/scales/ChartScales.svelte.d.ts +3 -0
  111. package/dist/scales/ChartScales.svelte.js +26 -8
  112. package/dist/scales/ColorScaleState.svelte.d.ts +18 -2
  113. package/dist/scales/ColorScaleState.svelte.js +138 -26
  114. package/dist/scales/LinearScaleState.svelte.d.ts +7 -10
  115. package/dist/scales/LinearScaleState.svelte.js +21 -39
  116. package/dist/scales/ScaleGeneration.svelte.d.ts +48 -0
  117. package/dist/scales/ScaleGeneration.svelte.js +42 -0
  118. package/dist/scales/ScaleRegistry.svelte.js +3 -1
  119. package/dist/scales/SlotTransition.d.ts +15 -1
  120. package/dist/scales/SlotTransition.js +33 -4
  121. package/dist/scales/TimeScaleState.svelte.d.ts +7 -3
  122. package/dist/scales/TimeScaleState.svelte.js +7 -7
  123. package/dist/scales/color-utils.d.ts +0 -3
  124. package/dist/scales/color-utils.js +0 -3
  125. package/dist/scales/entered.d.ts +8 -0
  126. package/dist/scales/entered.js +10 -0
  127. package/dist/spec/LazyQueryEngine.js +2 -19
  128. package/dist/spec/MarkRegistry.d.ts +3 -1
  129. package/dist/spec/MarkRegistry.js +13 -1
  130. package/dist/spec/PlotFromSpec.svelte +1 -0
  131. package/dist/spec/export.js +11 -2
  132. package/dist/spec/types.d.ts +41 -13
  133. package/dist/sql/bin.d.ts +25 -0
  134. package/dist/sql/bin.js +108 -5
  135. package/dist/transforms/TransformCompiler.d.ts +10 -0
  136. package/dist/transforms/TransformCompiler.js +26 -0
  137. package/dist/transforms/index.js +144 -24
  138. package/dist/types.d.ts +136 -2
  139. package/package.json +2 -2
@@ -1,7 +1,6 @@
1
1
  import type { ClockLoop } from '../animation';
2
2
  import { ActivityTable } from '../scales/ActivityTable';
3
3
  import type { FieldIdentity } from './categorical-identity';
4
- /** What a FieldActivity needs from the page around it. */
5
4
  /** One mark's Other rows for one bucket: what a click on it selects. */
6
5
  export interface OtherEntry {
7
6
  /** The mark's filter group, or null when it is filtered by the selection (`'filter'`). */
@@ -17,6 +16,7 @@ export interface OtherClick {
17
16
  action: 'replace' | 'add' | 'remove';
18
17
  values: unknown[];
19
18
  }
19
+ /** What a FieldActivity needs from the page around it. */
20
20
  export interface FieldActivityHost {
21
21
  /** Drives the fades; null in unit tests and before a coordinator exists, and fades land at once. */
22
22
  readonly loop: ClockLoop | null;
@@ -49,8 +49,7 @@ export declare const DETACHED_HOST: FieldActivityHost;
49
49
  * existing ids keep their fades while new ones start in their state.
50
50
  * `reconcile` runs from the packer and from `set`, never from a reader, so
51
51
  * nothing is resized or retargeted while a template renders.
52
- */
53
- /**
52
+ *
54
53
  * THE FREE SLOT. Each group's table holds one slot past the field's ids: 1 while
55
54
  * the group selects nothing on the field, 0 while it selects anything, faded on
56
55
  * the same clock as every id. The shader reads it as the last slot of the
@@ -44,8 +44,7 @@ class GroupFade {
44
44
  * existing ids keep their fades while new ones start in their state.
45
45
  * `reconcile` runs from the packer and from `set`, never from a reader, so
46
46
  * nothing is resized or retargeted while a template renders.
47
- */
48
- /**
47
+ *
49
48
  * THE FREE SLOT. Each group's table holds one slot past the field's ids: 1 while
50
49
  * the group selects nothing on the field, 0 while it selects anything, faded on
51
50
  * the same clock as every id. The shader reads it as the last slot of the
@@ -15,15 +15,42 @@ export interface QueryParams {
15
15
  sort?: NormalizedSortConfig;
16
16
  limit?: number;
17
17
  }
18
+ /**
19
+ * Why a result was queried. `catchUp`: the mark came back into view stale (see
20
+ * `active`), so the result brings it up to date with changes it missed, which
21
+ * nobody just made; it lands without animating. `update`: anything else.
22
+ */
23
+ export type ResultCause = 'update' | 'catchUp';
18
24
  export declare class MarkHandle {
19
25
  #private;
20
26
  arrowTable: Table<import("@uwdata/flechette").TypeMap>;
21
27
  dataVersion: number;
28
+ /**
29
+ * The cause of the result `dataVersion` counts, set with it. Plain, not
30
+ * $state: read by whatever reacts to `dataVersion`, never on its own.
31
+ */
32
+ resultCause: ResultCause;
22
33
  error: Error;
23
34
  get params(): QueryParams;
24
35
  /** Called by mark lifecycle when sort/limit change. */
25
36
  updateParams(params: QueryParams): void;
26
37
  _onParamsChange: (() => void) | null;
38
+ /**
39
+ * Whether the mark is on screen (or about to be). The engine does not
40
+ * re-query an inactive mark that has its first result: a filter or params
41
+ * change only marks it stale, and it queries once, with whatever is current,
42
+ * when it becomes active again. Its first query always runs, since the
43
+ * first result can feed domains other charts share. Not reactive: the
44
+ * engine reads it when it would send.
45
+ */
46
+ get active(): boolean;
47
+ /**
48
+ * Returns whether this call sent a catch-up query: the mark came back
49
+ * stale. A return with no query means the data it holds is current, so
50
+ * the caller can act on its return now rather than after a result.
51
+ */
52
+ setActive(active: boolean): boolean;
53
+ _onActivate: (() => boolean) | null;
27
54
  /**
28
55
  * Engine-internal: called to deliver query results.
29
56
  *
@@ -33,7 +60,7 @@ export declare class MarkHandle {
33
60
  * The error is cleared either way, so a success after a failure still
34
61
  * clears it.
35
62
  */
36
- _setResult(table: Table): void;
63
+ _setResult(table: Table, cause?: ResultCause): void;
37
64
  /** Engine-internal: called on query error. */
38
65
  _setError(error: Error): void;
39
66
  /** Clean up registration. Set by engine during registerMark. */
@@ -8,6 +8,11 @@
8
8
  export class MarkHandle {
9
9
  arrowTable = $state(null);
10
10
  dataVersion = $state(0);
11
+ /**
12
+ * The cause of the result `dataVersion` counts, set with it. Plain, not
13
+ * $state: read by whatever reacts to `dataVersion`, never on its own.
14
+ */
15
+ resultCause = 'update';
11
16
  error = $state(null);
12
17
  #params = $state({ sort: undefined, limit: undefined });
13
18
  get params() {
@@ -21,6 +26,32 @@ export class MarkHandle {
21
26
  // Engine sets this callback during registration.
22
27
  // Conventional underscore — engine needs external access.
23
28
  _onParamsChange = null;
29
+ /**
30
+ * Whether the mark is on screen (or about to be). The engine does not
31
+ * re-query an inactive mark that has its first result: a filter or params
32
+ * change only marks it stale, and it queries once, with whatever is current,
33
+ * when it becomes active again. Its first query always runs, since the
34
+ * first result can feed domains other charts share. Not reactive: the
35
+ * engine reads it when it would send.
36
+ */
37
+ get active() {
38
+ return this.#active;
39
+ }
40
+ /**
41
+ * Returns whether this call sent a catch-up query: the mark came back
42
+ * stale. A return with no query means the data it holds is current, so
43
+ * the caller can act on its return now rather than after a result.
44
+ */
45
+ setActive(active) {
46
+ if (active === this.#active)
47
+ return false;
48
+ this.#active = active;
49
+ return active ? (this._onActivate?.() ?? false) : false;
50
+ }
51
+ #active = true;
52
+ // Engine sets this during registration: query a mark that went stale, and
53
+ // say whether it did.
54
+ _onActivate = null;
24
55
  /**
25
56
  * Engine-internal: called to deliver query results.
26
57
  *
@@ -30,10 +61,11 @@ export class MarkHandle {
30
61
  * The error is cleared either way, so a success after a failure still
31
62
  * clears it.
32
63
  */
33
- _setResult(table) {
64
+ _setResult(table, cause = 'update') {
34
65
  this.error = null;
35
66
  if (table === this.arrowTable)
36
67
  return;
68
+ this.resultCause = cause;
37
69
  this.arrowTable = table;
38
70
  this.dataVersion++;
39
71
  }
@@ -20,9 +20,11 @@
20
20
  import { type Table } from '@uwdata/flechette';
21
21
  export type { BrushActivation } from './sufficient-statistics';
22
22
  import { type QueryParts } from '../components/marks/lifecycle/query-builder';
23
+ import { type BuildEvent } from './preagg/Preagg';
23
24
  import type { ActivationSource } from './preagg/plan';
24
25
  export type { ActiveDim, ActivationSource } from './preagg/plan';
25
26
  import { MarkHandle } from './MarkHandle.svelte';
27
+ import { type LaneHold, type SlicedLane } from './sliced-lane';
26
28
  import { type DimValue, type FieldIdentity, type IdentityField, type SyntheticId } from './categorical-identity';
27
29
  import type { FilterGroup } from './FilterGroup.svelte';
28
30
  import { type Clause, type MarkRoute } from './CrossfilterSelection.svelte';
@@ -33,13 +35,141 @@ export interface QueryOpts {
33
35
  callerId?: string;
34
36
  /** AbortSignal for external cancellation. */
35
37
  signal?: AbortSignal;
36
- /** Enable query result caching. Default true. Set false for queries against
37
- * volatile temp tables whose contents change without the SQL changing. */
38
+ /**
39
+ * Result caching, and sharing an identical query already running.
40
+ *
41
+ * - Mark queries (with a `callerId`): on by default; `false` for queries
42
+ * against tables whose contents change without the SQL changing.
43
+ * - Ad-hoc queries (no `callerId`): off unless `true`, so an app re-reading
44
+ * a table it writes to sees its writes. fjader's own setup queries pass
45
+ * `true`: they read base tables, which change only on a data reload, after
46
+ * which `clearQueryCache()` is the contract.
47
+ *
48
+ * The key includes the dims' version (see #cacheKey).
49
+ */
38
50
  cache?: boolean;
51
+ /**
52
+ * Who is waiting for it; see StatementClass. Default 'interaction'.
53
+ *
54
+ * `'background'` runs on another connection when the engine has one (a
55
+ * sliced lane on DuckDB-WASM), so a TEMP table created with `exec` on the
56
+ * main connection is invisible there and the query fails: pass
57
+ * `connectionLocal: true` for statements on connection-local state.
58
+ */
59
+ class?: StatementClass;
60
+ /** Reads or writes connection-local state; see StatementPlacement. */
61
+ connectionLocal?: boolean;
62
+ /**
63
+ * The table this read reads (base table, view or preagg table). An
64
+ * interaction read with one is routed by its source's cost (see
65
+ * SourceRouting): known-fast on the main connection, unknown or slow sliced
66
+ * on that source's lane. Without one it stays on the main connection.
67
+ */
68
+ source?: string;
69
+ /** A mark's data read: its cost updates the source's record. */
70
+ data?: boolean;
71
+ /**
72
+ * This read is known to be slow (a heavy query on whatever table, as a
73
+ * mark with `holdsEntrance={false}` says). The engine reads it where a
74
+ * slow read cannot hold up others (on DuckDB-WASM, sliced on its source's
75
+ * lane) and does not learn the source's cost from it: the source's other
76
+ * reads may be quick. A backend with one connection can ignore it.
77
+ */
78
+ slow?: boolean;
79
+ }
80
+ /**
81
+ * Where a statement may run, beside who waits for it.
82
+ *
83
+ * `connectionLocal`: it reads or writes state that belongs to one connection
84
+ * (a TEMP table, a session setting), so it stays on the main connection
85
+ * whatever its class. fjader's tooltip tables are TEMP: their writes, drops,
86
+ * hover lookups and the fits that read them pass it. Each table is written
87
+ * through one connection only, so statements on it keep their order. A
88
+ * backend with one connection, or truly shared state, can ignore it.
89
+ */
90
+ export interface StatementPlacement {
91
+ class: StatementClass;
92
+ connectionLocal?: boolean;
93
+ /** See QueryOpts.source. */
94
+ source?: string;
95
+ /** See QueryOpts.data. */
96
+ data?: boolean;
97
+ /**
98
+ * When the read was asked for (query() called): for a mark's
99
+ * execute-latest read, the state it reflects. Recorded, for data age.
100
+ */
101
+ requestedAt?: number;
102
+ /** The caller (a mark's id), for the log. */
103
+ callerId?: string;
104
+ /** See QueryOpts.slow. */
105
+ slow?: boolean;
106
+ }
107
+ /**
108
+ * Who is waiting for a statement. Callers state a class, never a connection:
109
+ * which connection (or later, which backend) serves it is the engine's
110
+ * business, decided in one place, `#send`.
111
+ *
112
+ * - `'interaction'`: someone is looking at the result or about to: mark data,
113
+ * hover lookups, a mark's setup (domains, dims). The default.
114
+ * - `'background'`: nobody waits for it directly: preagg builds, tooltip-table
115
+ * writes, cleanup. On DuckDB-WASM it runs on a sliced lane (another
116
+ * connection, see SlicedLane) unless `connectionLocal`, so a slow build no
117
+ * longer holds up interaction. Without a lane, preagg builds yield to
118
+ * interaction statements, not to each other's background work.
119
+ */
120
+ export type StatementClass = 'interaction' | 'background';
121
+ /**
122
+ * For `observeIdentityRequests`: a mark asking for categorical identity
123
+ * columns, or a table's drain starting (when its dims actually begin to build,
124
+ * which is what a chart's first read waits on).
125
+ */
126
+ export interface IdentityRequest {
127
+ /** 'request': a mark's ensureColumns call; 'drain': one pass of a table's drain started. */
128
+ kind: 'request' | 'drain';
129
+ table: string;
130
+ /** For a request, the fields asked for; for a drain pass, the fields it took. */
131
+ fields: string[];
132
+ /** performance.now() when asked. */
133
+ time: number;
134
+ }
135
+ /** One statement as it reached the connection, for `observeStatements`. */
136
+ export interface StatementRecord {
137
+ sql: string;
138
+ /** How it was sent: a query returning rows, a statement, a DESCRIBE, an Arrow insert. */
139
+ kind: 'query' | 'exec' | 'describe' | 'insert' | 'batch';
140
+ /** For a batch (execBatch): how many statements it ran as one message. */
141
+ count?: number;
142
+ class: StatementClass;
143
+ /** Which connection ran it: the main one, or a sliced lane. */
144
+ connection: 'main' | 'lane';
145
+ /** The source it was routed by, when it named one. */
146
+ source?: string;
147
+ /**
148
+ * When the read was asked for (StatementPlacement.requestedAt). With
149
+ * `end`, how old the state a result reflects is when it lands.
150
+ */
151
+ requested?: number;
152
+ /** Who asked (a mark's id), when it said. */
153
+ caller?: string;
154
+ /** performance.now() when handed to the connection, and when it settled. */
155
+ start: number;
156
+ end: number;
157
+ error?: unknown;
39
158
  }
40
159
  export interface QueryEngineOptions {
41
160
  /** Overrides BUILD_MAX_WAIT_MS; tests use it to make the queue deterministic. */
42
161
  buildMaxWaitMs?: number;
162
+ /**
163
+ * Debug. Send an execBatch's statements one by one, for per-statement
164
+ * timing. It changes what it measures: each statement then re-enters the
165
+ * queue behind whatever was sent meanwhile, which a batch avoids.
166
+ */
167
+ unbatch?: boolean;
168
+ /**
169
+ * Advanced. While the main connection works, all sliced lanes together get
170
+ * one slice per this many ms (see SLICE_SHARE_MS, the default).
171
+ */
172
+ sliceShareMs?: number;
43
173
  }
44
174
  export interface ColumnInfo {
45
175
  name: string;
@@ -58,11 +188,15 @@ export declare function buildSchemaMap(columns: ColumnInfo[]): Map<string, Colum
58
188
  /**
59
189
  * Minimal connection interface.
60
190
  * Supports two query paths:
61
- * - queryIPC (fast): returns raw Arrow IPC bytes, decoded by flechette (zero-copy)
191
+ * - queryIPC (fast): returns raw Arrow IPC bytes, decoded by flechette into typed arrays
62
192
  * - query (fallback): returns a Table directly (for mocks/testing)
63
193
  */
64
194
  export interface DuckDBConnection {
65
- /** Fast path: raw Arrow IPC bytes → flechette tableFromIPC → zero-copy typed arrays. */
195
+ /**
196
+ * Fast path: raw Arrow IPC bytes → flechette tableFromIPC → typed arrays. Views on
197
+ * the bytes within one record batch (~2k rows from DuckDB-WASM); past that,
198
+ * flechette concatenates each column once.
199
+ */
66
200
  queryIPC?(sql: string): Promise<Uint8Array>;
67
201
  /** Fallback query path. Used when queryIPC is not available (mocks, testing). */
68
202
  query(sql: string): Promise<Table>;
@@ -75,6 +209,17 @@ export interface DuckDBConnection {
75
209
  name: string;
76
210
  create: boolean;
77
211
  }): Promise<void>;
212
+ /**
213
+ * Open another connection on the same database whose statements run
214
+ * sliced (see SlicedLane): a slow read there no longer blocks this
215
+ * connection's queries. DuckDB-WASM via `connectionFrom`; a connection
216
+ * without it keeps everything on itself. The engine opens lanes itself and
217
+ * sends through them from `#send`, so every statement keeps its record,
218
+ * class and accounting; nothing hands a raw lane to an app.
219
+ */
220
+ openSlicedLane?(options?: {
221
+ hold?: LaneHold;
222
+ }): Promise<SlicedLane>;
78
223
  }
79
224
  export interface QueryDescriptor {
80
225
  /** The table the mark queries: its identity view when it has categorical channels. */
@@ -86,7 +231,29 @@ export interface QueryDescriptor {
86
231
  transformCompiler: TransformCompiler;
87
232
  /** What routing needs to know about the mark. Absent in tests that route nothing. */
88
233
  route?: MarkRoute;
234
+ /**
235
+ * The mark is known to be slow (`holdsEntrance={false}`): its data reads
236
+ * go sliced from the first, whatever its source's cost. The mark, not its
237
+ * source: other marks on a fast table keep reading on the main connection.
238
+ */
239
+ slow?: boolean;
89
240
  }
241
+ /**
242
+ * How a mark's data read is placed. Routed by the table it reads
243
+ * (SourceRouting): the preagg table when one answers, else the mark's BASE
244
+ * table (its identity view reads the same rows, and its range and stats reads
245
+ * name the base table: one record per source). `slow` (holdsEntrance={false})
246
+ * applies to the base query only: a preagg read is ~1 ms, and forced onto a
247
+ * lane during a brush it would cost ~20 ms of round trips in exactly the case
248
+ * preagg exists to make fast.
249
+ */
250
+ export declare function markReadOptions(markId: string, descriptor: Pick<QueryDescriptor, 'baseTable' | 'slow'>, preaggTable: string | null): QueryOpts;
251
+ /**
252
+ * DuckDB's catalog error for a table or view this connection cannot see.
253
+ * Matched by its wording, which an upgrade could change: the integration
254
+ * test missing-relation.integration.test.ts checks it against real DuckDB.
255
+ */
256
+ export declare function isMissingRelation(e: unknown): boolean;
90
257
  export declare class QueryEngine {
91
258
  #private;
92
259
  constructor(connection: DuckDBConnection | Promise<DuckDBConnection>, options?: QueryEngineOptions);
@@ -178,6 +345,29 @@ export declare class QueryEngine {
178
345
  * dropped state still invalidates every id handed out before it.
179
346
  */
180
347
  onIdentityReset(listener: () => void): () => void;
348
+ /**
349
+ * Debug: every categorical identity request (a mark's ensureColumns call),
350
+ * with its table, fields and time, before deduplication, and every drain
351
+ * start (a table's dims beginning to build, with the fields it found
352
+ * pending). Returns the unsubscribe; costs nothing while no one listens, and
353
+ * a listener that throws is logged without failing identity work.
354
+ */
355
+ observeIdentityRequests(listener: (request: IdentityRequest) => void): () => void;
356
+ /**
357
+ * Debug: every step of every preagg build (BuildEvent): planned or
358
+ * skipped (with the size guard's numbers), queued (with its hover batch),
359
+ * started, then built, failed or dropped (with the check that failed and
360
+ * the values it compared). Returns the unsubscribe; costs nothing while no
361
+ * one listens, and a listener that throws is logged without failing the
362
+ * build.
363
+ */
364
+ observeBuilds(listener: (event: BuildEvent) => void): () => void;
365
+ /**
366
+ * Listen to every statement sent to the connection, as it settles. For
367
+ * debugging and measurement; returns the unsubscribe. Costs nothing while
368
+ * no one listens.
369
+ */
370
+ observeStatements(listener: (record: StatementRecord) => void): () => void;
181
371
  /**
182
372
  * Execute a query and return the Arrow result table.
183
373
  * Accepts a SQL string or any object with toString() (e.g. mosaic-sql Query).
@@ -190,17 +380,47 @@ export declare class QueryEngine {
190
380
  query(sql: string | {
191
381
  toString(): string;
192
382
  }, opts?: QueryOpts): Promise<Table>;
383
+ /**
384
+ * Run several statements as ONE message on the main connection, returning
385
+ * the last statement's rows. Each statement commits on its own and
386
+ * execution stops at the first failure, as when sent one by one (measured
387
+ * on DuckDB-WASM, /test/query-slicing §13); the error does not say which
388
+ * statement failed.
389
+ *
390
+ * For statements on tables the main connection owns (fjader's identity
391
+ * dims, present tables, views): one connection per table. Deliberately no
392
+ * class, source or routing options: it always runs on the main connection
393
+ * as interaction (so lanes hold their slices while it runs). What it saves
394
+ * is queue entries: a dependent chain sent one by one goes to the back of
395
+ * the queue at every step.
396
+ *
397
+ * Logged as one record, kind 'batch', with the joined text and `count`.
398
+ * Without `queryIPC` (mocks, custom connections), or with
399
+ * `QueryEngineOptions.unbatch`, the statements go one by one.
400
+ */
401
+ execBatch(statements: string[]): Promise<Table>;
193
402
  /**
194
403
  * Execute DDL/DML statements (CREATE TABLE, DROP TABLE, INSERT, etc.).
195
404
  * Accepts a single SQL string or an array of statements to execute
196
405
  * sequentially.
197
406
  */
198
- exec(sql: string | string[]): Promise<void>;
407
+ exec(sql: string | string[], opts?: {
408
+ class?: StatementClass;
409
+ connectionLocal?: boolean;
410
+ signal?: AbortSignal;
411
+ }): Promise<void>;
199
412
  /**
200
413
  * Get column info for a table via DESCRIBE.
201
414
  * Results are cached — same table returns the same promise.
202
415
  */
203
416
  describe(table: string): Promise<ColumnInfo[]>;
417
+ /**
418
+ * Tell the engine how a source reads, overriding its prior: `'slow'` for a
419
+ * table that is slow to query (it reads sliced, so it never holds up
420
+ * other charts), `'fast'` for a view that is cheap (it reads on the main
421
+ * connection from its first query). A measured read still corrects it.
422
+ */
423
+ sourceCost(source: string, cost: 'fast' | 'slow'): void;
204
424
  /** Clear cached DESCRIBE results. Pass a table name to clear one, or omit for all. */
205
425
  clearDescribeCache(table?: string): void;
206
426
  /**