@kanzo-tech/graph 0.10.0 → 0.11.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 (204) hide show
  1. package/README.md +60 -95
  2. package/dist/core/categories.d.ts +12 -0
  3. package/dist/core/categories.d.ts.map +1 -0
  4. package/dist/core/categories.js +14 -0
  5. package/dist/core/categories.js.map +1 -0
  6. package/dist/core/channels.d.ts +34 -0
  7. package/dist/core/channels.d.ts.map +1 -0
  8. package/dist/core/channels.js +19 -0
  9. package/dist/core/channels.js.map +1 -0
  10. package/dist/core/detail.d.ts +18 -0
  11. package/dist/core/detail.d.ts.map +1 -0
  12. package/dist/core/detail.js +20 -0
  13. package/dist/core/detail.js.map +1 -0
  14. package/dist/core/filter.d.ts +34 -0
  15. package/dist/core/filter.d.ts.map +1 -0
  16. package/dist/core/filter.js +99 -0
  17. package/dist/core/filter.js.map +1 -0
  18. package/dist/core/refine.d.ts +14 -0
  19. package/dist/core/refine.d.ts.map +1 -0
  20. package/dist/core/refine.js +20 -0
  21. package/dist/core/refine.js.map +1 -0
  22. package/dist/{resident.d.ts → core/resident.d.ts} +7 -7
  23. package/dist/core/resident.d.ts.map +1 -0
  24. package/dist/core/resident.js +44 -0
  25. package/dist/core/resident.js.map +1 -0
  26. package/dist/core/scheduler.d.ts +42 -0
  27. package/dist/core/scheduler.d.ts.map +1 -0
  28. package/dist/core/scheduler.js +77 -0
  29. package/dist/core/scheduler.js.map +1 -0
  30. package/dist/core/state.d.ts +132 -0
  31. package/dist/core/state.d.ts.map +1 -0
  32. package/dist/core/store.d.ts +6 -0
  33. package/dist/core/store.d.ts.map +1 -0
  34. package/dist/core/store.js +235 -0
  35. package/dist/core/store.js.map +1 -0
  36. package/dist/core/tile-matrix.d.ts +37 -0
  37. package/dist/core/tile-matrix.d.ts.map +1 -0
  38. package/dist/core/tile-matrix.js +49 -0
  39. package/dist/core/tile-matrix.js.map +1 -0
  40. package/dist/core/tile.d.ts +54 -0
  41. package/dist/core/tile.d.ts.map +1 -0
  42. package/dist/core/tile.js +76 -0
  43. package/dist/core/tile.js.map +1 -0
  44. package/dist/core/tileset.d.ts +59 -0
  45. package/dist/core/tileset.d.ts.map +1 -0
  46. package/dist/core/tileset.js +177 -0
  47. package/dist/core/tileset.js.map +1 -0
  48. package/dist/{types.d.ts → core/types.d.ts} +9 -0
  49. package/dist/core/types.d.ts.map +1 -0
  50. package/dist/index.d.ts +37 -70
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +32 -34
  53. package/dist/index.js.map +1 -1
  54. package/dist/{use-graph-selection.d.ts → parts/gesture.d.ts} +4 -4
  55. package/dist/parts/gesture.d.ts.map +1 -0
  56. package/dist/{use-graph-selection.js → parts/gesture.js} +17 -17
  57. package/dist/parts/gesture.js.map +1 -0
  58. package/dist/parts/graph-canvas.d.ts +17 -0
  59. package/dist/parts/graph-canvas.d.ts.map +1 -0
  60. package/dist/parts/graph-canvas.js +174 -0
  61. package/dist/parts/graph-canvas.js.map +1 -0
  62. package/dist/parts/graph-inspector.d.ts +18 -0
  63. package/dist/parts/graph-inspector.d.ts.map +1 -0
  64. package/dist/parts/graph-inspector.js +84 -0
  65. package/dist/parts/graph-inspector.js.map +1 -0
  66. package/dist/parts/graph-legend.d.ts +12 -0
  67. package/dist/parts/graph-legend.d.ts.map +1 -0
  68. package/dist/parts/graph-legend.js +61 -0
  69. package/dist/parts/graph-legend.js.map +1 -0
  70. package/dist/parts/graph-toolbar.d.ts +15 -0
  71. package/dist/parts/graph-toolbar.d.ts.map +1 -0
  72. package/dist/parts/graph-toolbar.js +94 -0
  73. package/dist/parts/graph-toolbar.js.map +1 -0
  74. package/dist/parts/overlays.d.ts +33 -0
  75. package/dist/parts/overlays.d.ts.map +1 -0
  76. package/dist/{use-graph-overlays.js → parts/overlays.js} +8 -8
  77. package/dist/parts/overlays.js.map +1 -0
  78. package/dist/{shape-glyph.d.ts → parts/shape-glyph.d.ts} +1 -1
  79. package/dist/parts/shape-glyph.d.ts.map +1 -0
  80. package/dist/{shape-glyph.js → parts/shape-glyph.js} +1 -1
  81. package/dist/parts/shape-glyph.js.map +1 -0
  82. package/dist/react/graph-root.d.ts +15 -0
  83. package/dist/react/graph-root.d.ts.map +1 -0
  84. package/dist/react/graph-root.js +23 -0
  85. package/dist/react/graph-root.js.map +1 -0
  86. package/dist/{use-graph-prefs.d.ts → react/use-graph-prefs.d.ts} +2 -2
  87. package/dist/react/use-graph-prefs.d.ts.map +1 -0
  88. package/dist/{use-graph-prefs.js → react/use-graph-prefs.js} +3 -3
  89. package/dist/react/use-graph-prefs.js.map +1 -0
  90. package/dist/react/use-graph-state.d.ts +11 -0
  91. package/dist/react/use-graph-state.d.ts.map +1 -0
  92. package/dist/react/use-graph-state.js +16 -0
  93. package/dist/react/use-graph-state.js.map +1 -0
  94. package/dist/react/use-graph.d.ts +38 -0
  95. package/dist/react/use-graph.d.ts.map +1 -0
  96. package/dist/react/use-graph.js +87 -0
  97. package/dist/react/use-graph.js.map +1 -0
  98. package/dist/{adaptive.d.ts → render/adaptive.d.ts} +1 -1
  99. package/dist/render/adaptive.d.ts.map +1 -0
  100. package/dist/render/adaptive.js.map +1 -0
  101. package/dist/render/compose.d.ts +51 -0
  102. package/dist/render/compose.d.ts.map +1 -0
  103. package/dist/render/compose.js +107 -0
  104. package/dist/render/compose.js.map +1 -0
  105. package/dist/render/css-color.d.ts.map +1 -0
  106. package/dist/render/css-color.js.map +1 -0
  107. package/dist/render/encode.d.ts +42 -0
  108. package/dist/render/encode.d.ts.map +1 -0
  109. package/dist/render/encode.js +68 -0
  110. package/dist/render/encode.js.map +1 -0
  111. package/dist/render/graph-looks.d.ts.map +1 -0
  112. package/dist/render/graph-looks.js.map +1 -0
  113. package/dist/render/graph-model.d.ts +44 -0
  114. package/dist/render/graph-model.d.ts.map +1 -0
  115. package/dist/render/graph-model.js +80 -0
  116. package/dist/render/graph-model.js.map +1 -0
  117. package/dist/{graph-sim.d.ts → render/graph-sim.d.ts} +1 -1
  118. package/dist/render/graph-sim.d.ts.map +1 -0
  119. package/dist/render/graph-sim.js.map +1 -0
  120. package/dist/{obligations.d.ts → render/obligations.d.ts} +1 -1
  121. package/dist/render/obligations.d.ts.map +1 -0
  122. package/dist/render/renderer.d.ts +38 -0
  123. package/dist/render/renderer.d.ts.map +1 -0
  124. package/dist/render/renderer.js +235 -0
  125. package/dist/render/renderer.js.map +1 -0
  126. package/dist/render/webgl.d.ts +14 -0
  127. package/dist/render/webgl.d.ts.map +1 -0
  128. package/dist/render/webgl.js +19 -0
  129. package/dist/render/webgl.js.map +1 -0
  130. package/dist/render/when-ready.d.ts.map +1 -0
  131. package/dist/render/when-ready.js.map +1 -0
  132. package/package.json +11 -21
  133. package/dist/adaptive.d.ts.map +0 -1
  134. package/dist/adaptive.js.map +0 -1
  135. package/dist/bounded.d.ts +0 -364
  136. package/dist/bounded.d.ts.map +0 -1
  137. package/dist/bounded.js +0 -18
  138. package/dist/bounded.js.map +0 -1
  139. package/dist/cluster-ring.d.ts +0 -25
  140. package/dist/cluster-ring.d.ts.map +0 -1
  141. package/dist/cluster-ring.js +0 -16
  142. package/dist/cluster-ring.js.map +0 -1
  143. package/dist/css-color.d.ts.map +0 -1
  144. package/dist/css-color.js.map +0 -1
  145. package/dist/duck-source.d.ts +0 -198
  146. package/dist/duck-source.d.ts.map +0 -1
  147. package/dist/duck-source.js +0 -320
  148. package/dist/duck-source.js.map +0 -1
  149. package/dist/graph-canvas.d.ts +0 -50
  150. package/dist/graph-canvas.d.ts.map +0 -1
  151. package/dist/graph-canvas.js +0 -35
  152. package/dist/graph-canvas.js.map +0 -1
  153. package/dist/graph-looks.d.ts.map +0 -1
  154. package/dist/graph-looks.js.map +0 -1
  155. package/dist/graph-model.d.ts +0 -130
  156. package/dist/graph-model.d.ts.map +0 -1
  157. package/dist/graph-model.js +0 -104
  158. package/dist/graph-model.js.map +0 -1
  159. package/dist/graph-sim.d.ts.map +0 -1
  160. package/dist/graph-sim.js.map +0 -1
  161. package/dist/obligations.d.ts.map +0 -1
  162. package/dist/resident.d.ts.map +0 -1
  163. package/dist/resident.js +0 -44
  164. package/dist/resident.js.map +0 -1
  165. package/dist/shape-glyph.d.ts.map +0 -1
  166. package/dist/shape-glyph.js.map +0 -1
  167. package/dist/slice-client.d.ts +0 -78
  168. package/dist/slice-client.d.ts.map +0 -1
  169. package/dist/slice-client.js +0 -98
  170. package/dist/slice-client.js.map +0 -1
  171. package/dist/types.d.ts.map +0 -1
  172. package/dist/use-graph-look.d.ts +0 -28
  173. package/dist/use-graph-look.d.ts.map +0 -1
  174. package/dist/use-graph-look.js +0 -28
  175. package/dist/use-graph-look.js.map +0 -1
  176. package/dist/use-graph-overlays.d.ts +0 -44
  177. package/dist/use-graph-overlays.d.ts.map +0 -1
  178. package/dist/use-graph-overlays.js.map +0 -1
  179. package/dist/use-graph-prefs.d.ts.map +0 -1
  180. package/dist/use-graph-prefs.js.map +0 -1
  181. package/dist/use-graph-selection.d.ts.map +0 -1
  182. package/dist/use-graph-selection.js.map +0 -1
  183. package/dist/use-graph.d.ts +0 -203
  184. package/dist/use-graph.d.ts.map +0 -1
  185. package/dist/use-graph.js +0 -102
  186. package/dist/use-graph.js.map +0 -1
  187. package/dist/use-query-loop.d.ts +0 -88
  188. package/dist/use-query-loop.d.ts.map +0 -1
  189. package/dist/use-query-loop.js +0 -148
  190. package/dist/use-query-loop.js.map +0 -1
  191. package/dist/use-renderer.d.ts +0 -86
  192. package/dist/use-renderer.d.ts.map +0 -1
  193. package/dist/use-renderer.js +0 -188
  194. package/dist/use-renderer.js.map +0 -1
  195. package/dist/when-ready.d.ts.map +0 -1
  196. package/dist/when-ready.js.map +0 -1
  197. /package/dist/{adaptive.js → render/adaptive.js} +0 -0
  198. /package/dist/{css-color.d.ts → render/css-color.d.ts} +0 -0
  199. /package/dist/{css-color.js → render/css-color.js} +0 -0
  200. /package/dist/{graph-looks.d.ts → render/graph-looks.d.ts} +0 -0
  201. /package/dist/{graph-looks.js → render/graph-looks.js} +0 -0
  202. /package/dist/{graph-sim.js → render/graph-sim.js} +0 -0
  203. /package/dist/{when-ready.d.ts → render/when-ready.d.ts} +0 -0
  204. /package/dist/{when-ready.js → render/when-ready.js} +0 -0
package/README.md CHANGED
@@ -1,116 +1,81 @@
1
1
  # @kanzo-tech/graph
2
2
 
3
- A GPU graph view over [cosmos.gl](https://cosmosgl.github.io/graph): the DuckDB relation that feeds
4
- it, the buffers that colour it from the page's own theme, and the hooks that own the renderer's
5
- lifetime.
6
-
7
- ## What it is not
8
-
9
- **There is no `<GraphCanvas>`.** That is the shape of the package rather than a gap in it. A graph
10
- canvas is a toolbar, a legend, an inspector, a hover card and a search box wired to one renderer,
11
- and every one of those answers differently per product. What is genuinely shared sits underneath —
12
- reading a relation into typed arrays, turning a look and the live theme into GPU buffers, owning the
13
- renderer across React's lifecycle, and keeping a lasso inside a Mosaic crossfilter. Those are here.
14
- The arrangement belongs at the call site; `docs/showcases/workspace/graph-canvas.tsx` is one, and it
15
- is not the only possible one.
16
-
17
- ## Why a package, and not part of `@kanzo-tech/ui`
18
-
19
- The [first admission rule](/docs/philosophy#admission) is *domain-free — nothing about RDF / SHACL / fossil / **graphs**
20
- / auth*. Graphs are excluded by name, deliberately: `ui` is the generic vocabulary every product
21
- shares. A sibling package is also the only honest home for a **required** WebGL peer — as an
22
- optional peer of `ui` it would have been a lie about what the package is.
3
+ The graph view over a [fossil](https://github.com/kanzo-tech/fossil) corpus, drawn with
4
+ [cosmos.gl](https://cosmosgl.github.io/graph). fossil is the backend and this package is the view:
5
+ the host opens the corpus, and `GraphRoot` draws it by tile — no SQL is written here.
23
6
 
24
7
  ## Install
25
8
 
26
9
  ```sh
27
- pnpm add @kanzo-tech/graph @cosmos.gl/graph
10
+ pnpm add @kanzo-tech/graph @cosmos.gl/graph @fossil-lang/corpus @kanzo-tech/mosaic @kanzo-tech/ui lucide-react @uwdata/mosaic-core @uwdata/mosaic-sql
28
11
  ```
29
12
 
30
- `@cosmos.gl/graph` is a required peer: this is a renderer, and there is nothing left of it without
31
- one. The Mosaic peers and fossil's corpus reader are **optional** — `openCorpus()` needs them and
32
- the rendering hooks do not.
33
-
34
- **So the root barrel draws nothing, and that is the shape rather than an oversight.** The reason for
35
- the split used to be *a host drawing arrays it already has should not pay for a database*, and that
36
- host no longer exists here: `memorySource` is deleted and this package sends **one** source, the
37
- corpus'. What the split protects now is a barrel that is a rendering surface — hooks, looks,
38
- identity, the buffers a slice implies — for a host whose slices arrive from somewhere else, its own
39
- `BoundedSource` included. A picture costs `@kanzo-tech/graph/duckdb`.
13
+ Every peer is required. `@fossil-lang/corpus` reads the corpus, `@kanzo-tech/mosaic` is the page's one
14
+ DuckDB-WASM engine and its crossfilter, cosmos.gl draws, and the parts are built from
15
+ `@kanzo-tech/ui` and its icons.
40
16
 
41
- ## The two halves
17
+ ## The one path
42
18
 
43
- **Data.** A **source** answers one question — *what should I draw* — and `useQueryLoop` asks it.
44
- The answer is a `Slice`: at most `limit` points as parallel typed arrays, whose size follows the
45
- question rather than the corpus. Moving the camera re-asks, and a window holding more than `limit`
46
- is **sampled** rather than truncated — one row every `ceil(matched / limit)` over the corpus'
47
- Morton-ordered `dense_id`, which spreads the marks over the window instead of drawing a corner of
48
- it. So a view of everything is still a few thousand marks, and they are still everywhere.
49
-
50
- `openCorpus` — on `@kanzo-tech/graph/duckdb`, because that is the half that needs Mosaic and
51
- fossil's reader — takes the corpus the host opened with fossil's `open` and the page's `engine()`,
52
- and hands back both halves: the source the canvas draws from, and the names fossil's verbs query
53
- the relations by, for the charts and the crossfilter.
54
-
55
- ```ts
19
+ ```tsx
56
20
  import { open } from "@fossil-lang/corpus";
57
21
  import { engine } from "@kanzo-tech/mosaic";
58
- import { openCorpus } from "@kanzo-tech/graph/duckdb";
22
+ import { GraphCanvas, GraphInspector, GraphLegend, GraphRoot, GraphToolbar } from "@kanzo-tech/graph";
23
+
24
+ const corpus = engine().then((e) => open(url, { engine: e }));
59
25
 
60
- const e = await engine();
61
- const corpus = await open("/corpus/people", { query: e.query });
62
- const { source, nodes, edges } = await openCorpus({ corpus, engine: e });
26
+ <GraphRoot corpus={corpus} fill="kind" r="degree" title="label" filterBy={crossfilter} onFailure={setFailure}>
27
+ <GraphCanvas>
28
+ <GraphToolbar />
29
+ <GraphLegend />
30
+ </GraphCanvas>
31
+ <GraphInspector />
32
+ </GraphRoot>;
63
33
  ```
64
34
 
65
- Opening is the host's: a host whose files sit behind a signature opens the corpus under a name,
66
- `` open(`jobs/${id}`, { engine: e, host }) ``, and hands over the same `corpus`. Closing it is the
67
- host's too. It takes no column names and no type index; those come off the manifest or they do not come.
68
- Under `limit` it is asked once for everything and never again, so a corpus that fits pays for
69
- nothing.
70
-
71
- **A point is addressed by index and identified by pair.** cosmos.gl numbers points by their position
72
- in the arrays it was last handed, so index 7 is whatever the current answer put seventh. A vertex is
73
- therefore `vertexId(type, dense)` — a `bigint`, and `Slice.vertices` a `BigUint64Array`, because the
74
- pair is 64 bits and a `number` holds 53. Anything that outlives one answer is held as a `VertexId`
75
- and resolved through the `Resident` that `useQueryLoop` rebuilds per answer. Do not build a second
76
- map: a copy assembled beside it is the same value one render later, with no way to notice it has
77
- fallen behind the buffers on screen.
78
-
79
- **Appearance.** `buffers(slice, look, host)` turns a look and the *live theme* into per-point
80
- colours, sizes and shapes. A `Look` carries **geometry only** — colour comes from the page's
81
- categorical scale (`categoricalColor`, on the **root** barrel of `@kanzo-tech/ui` — not
82
- `/analytics`, so reaching it costs nobody the DuckDB peer set), because a scale a graph invents is a
83
- scale that disagrees with the legend explaining it.
84
-
85
- `appearance()` is the other side of that line and it matters for performance: everything that is
86
- *one number for the whole canvas* is a cosmos.gl **uniform**, read fresh on every draw. Multiply a
87
- slider into 500,000 sizes and every tick re-uploads the array; put it in a uniform and it costs
88
- nothing.
89
-
90
- ## Scale
91
-
92
- Measured, not asserted — see the [benchmarks page](https://kanzo-tech.github.io/ui/docs/graph/benchmarks), and
93
- `/view/showcases/graph-bench` to re-run it.
94
-
95
- A live simulation is comfortable to about **50,000** points and finished by **200,000** (a step
96
- costs 62 ms there). A million points render, upload and simulate without failing, but at 440 ms a
97
- step. **Past 200,000 the honest design is positions computed once and stored as a column** — which
98
- is why a source names `xField` / `yField`, and why a simulation is off by default: the coordinates a
99
- source hands back are the index the next spatial question is asked against, and a force that moves
100
- them moves the picture out from under its own index.
101
-
102
- Bounded, over a corpus compiled once, first paint is **253 ms at a million** against the 1,225 ms it
103
- used to cost to hold two hundred thousand. What is drawn and transferred follows the window — the
104
- upload is flat at 23–30 ms and the redraw ceiling stays in the thousands of frames per second. What
105
- is *scanned* does not: the pan grows from 77 ms at a million to 256 ms at five, and the term that
106
- grows is the edge join, over one file on a DuckDB-WASM that gets a single thread. That is the next
107
- thing to fix, and it is the corpus layout, not the renderer.
35
+ **Opening is the host's.** Where the corpus is and what signs it are the host's to say — a private
36
+ corpus opens as `` open(`jobs/${id}`, { engine, host }) `` — and `GraphRoot` takes what came back,
37
+ or the promise of it, which is what lets it say it is opening. Closing it is the host's too.
38
+
39
+ **The channels are Plot's**: `fill`, `symbol`, `r`, `stroke` and `title`, where a CSS colour is a
40
+ constant and anything else is a column. A column binding is a projection: changing one builds a new
41
+ scan and reloads the tiles while keeping the picture.
42
+
43
+ **The camera is addressed.** A tileset in deck.gl's `Tileset2D` shape culls the tile matrix fossil
44
+ published, picks the finest zoom whose tiles in view hold at most `limit` rows (20,000 by default),
45
+ reads one tile at a time nearest the centre first, cancels what the camera has left, and keeps five
46
+ times what it selected. A far view is a coarser rung of fossil's cell pyramid; an edge is drawn when
47
+ both of its ends are in tiles already read.
48
+
49
+ **`filterBy` is the page's crossfilter.** Its clauses are translated into scan's filter, so the
50
+ canvas draws what survives; a clause that cannot be translated reaches `onFailure` rather than being
51
+ dropped. A lasso or a click publishes the reader's pick back into it, exempting the graph itself.
52
+
53
+ ## The pieces
54
+
55
+ - `GraphRoot` — `useGraph` plus the context. `GraphRootProvider` takes an api a host built with
56
+ `useGraph`; `useGraphContext` reads it — Ark's four, one for one. The api is the commands and
57
+ never changes; `useGraphState(selector)` reads a slice of the state.
58
+ - `GraphCanvas` — the element, the grid, the vignette, the labels, the hover card and the marquee
59
+ and lasso gesture. Children are chrome positioned over it.
60
+ - `GraphLegend`, `GraphToolbar`, `GraphInspector` — the categorical scale with its tally, the
61
+ commands drawn, and the focused vertex's row with a render prop for a product's own fields. No
62
+ part takes a callback: what a click means is `onSelect`, `onFocus` and `onFailure` on the root.
63
+ - `lookFrom`, `simFrom`, `useGraphPrefs` — the form and the forces from a preferences panel's
64
+ answers; `scaleOf` — what colour and shape a category wears; `adaptive` — simulation tuning by size.
65
+ - `vertexId`, `typeOf`, `denseOf` — a vertex is `(type_idx, dense_id)` packed into a `bigint`, and
66
+ everything that outlives one composition is held as one.
67
+
68
+ ## Why a package, and not part of `@kanzo-tech/ui`
69
+
70
+ The [first admission rule](https://kanzo-tech.github.io/ui/docs/philosophy#admission) is
71
+ *domain-free — nothing about RDF / SHACL / fossil / **graphs** / auth*. Graphs are excluded by name,
72
+ and a sibling package is the only honest home for a required WebGL peer.
108
73
 
109
74
  ## Gotchas the source will not tell you twice
110
75
 
111
76
  - **`setConfig` resets everything.** cosmos.gl 3.x resets the whole configuration to defaults and
112
77
  then applies the argument. Use `setConfigPartial`. This typechecks either way.
113
78
  - **`getConnectedLinkIndices` is not a neighbourhood.** It returns only links whose *other* endpoint
114
- is also in the argument — an induced subgraph. Use `neighboursOf(graph, index)`.
115
- - **`requestAnimationFrame` never fires in a hidden tab**, and cosmos.gl drives its simulation from
116
- rendered frames. A backgrounded graph is stopped, not slow.
79
+ is also in the argument. `getNeighboringPointIndices` is the neighbourhood.
80
+ - **`requestAnimationFrame` never fires in a hidden tab**, and the renderer draws on animation
81
+ frames. A backgrounded graph is stopped, not slow.
@@ -0,0 +1,12 @@
1
+ import { Corpus } from '@fossil-lang/corpus';
2
+ import { Binding } from './channels';
3
+ /**
4
+ * **The categorical domain, fixed before a tile arrives.** A column the manifest's `channels:`
5
+ * declares as categorical ranks by its own ordinal, `0` to `domain − 1`; otherwise the host's
6
+ * `categories` keys are the order. Only a value neither names is ranked as it is seen, after them —
7
+ * which is what a legend whose colours moved as tiles arrived was doing for every value.
8
+ */
9
+ export declare function domainOf(corpus: Corpus | null, typeIndex: number, binding: Binding, categories: Readonly<Record<string, string>> | undefined): readonly unknown[];
10
+ /** What a legend, a card or an inspector calls a category value. */
11
+ export declare const nameOf: (value: unknown, categories: Readonly<Record<string, string>> | undefined) => string;
12
+ //# sourceMappingURL=categories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"categories.d.ts","sourceRoot":"","sources":["../../src/core/categories.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C;;;;;GAKG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,GACvD,SAAS,OAAO,EAAE,CAOpB;AAED,oEAAoE;AACpE,eAAO,MAAM,MAAM,GAAI,OAAO,OAAO,EAAE,YAAY,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,KAAG,MAC7C,CAAC"}
@@ -0,0 +1,14 @@
1
+ function a(n, t, i, r) {
2
+ var o;
3
+ if (i.category === void 0) return [];
4
+ const f = (o = n == null ? void 0 : n.types.vertices[t]) == null ? void 0 : o.channels.find(
5
+ (m) => m.column === i.category && m.scale === "categorical" && m.domain !== null
6
+ );
7
+ return f != null && f.domain ? Array.from({ length: f.domain }, (m, y) => y) : r ? Object.keys(r) : [];
8
+ }
9
+ const d = (n, t) => (t == null ? void 0 : t[String(n)]) ?? String(n ?? "—");
10
+ export {
11
+ a as domainOf,
12
+ d as nameOf
13
+ };
14
+ //# sourceMappingURL=categories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"categories.js","sources":["../../src/core/categories.ts"],"sourcesContent":["import type { Corpus } from \"@fossil-lang/corpus\";\nimport type { Binding } from \"./channels\";\n\n/**\n * **The categorical domain, fixed before a tile arrives.** A column the manifest's `channels:`\n * declares as categorical ranks by its own ordinal, `0` to `domain − 1`; otherwise the host's\n * `categories` keys are the order. Only a value neither names is ranked as it is seen, after them —\n * which is what a legend whose colours moved as tiles arrived was doing for every value.\n */\nexport function domainOf(\n corpus: Corpus | null,\n typeIndex: number,\n binding: Binding,\n categories: Readonly<Record<string, string>> | undefined,\n): readonly unknown[] {\n if (binding.category === undefined) return [];\n const declared = corpus?.types.vertices[typeIndex]?.channels.find(\n (channel) => channel.column === binding.category && channel.scale === \"categorical\" && channel.domain !== null,\n );\n if (declared?.domain) return Array.from({ length: declared.domain }, (_, ordinal) => ordinal);\n return categories ? Object.keys(categories) : [];\n}\n\n/** What a legend, a card or an inspector calls a category value. */\nexport const nameOf = (value: unknown, categories: Readonly<Record<string, string>> | undefined): string =>\n categories?.[String(value)] ?? String(value ?? \"—\");\n"],"names":["domainOf","corpus","typeIndex","binding","categories","_a","declared","channel","_","ordinal","nameOf","value"],"mappings":"AASO,SAASA,EACdC,GACAC,GACAC,GACAC,GACoB;AALf,MAAAC;AAML,MAAIF,EAAQ,aAAa,OAAW,QAAO,CAAA;AAC3C,QAAMG,KAAWD,IAAAJ,KAAA,gBAAAA,EAAQ,MAAM,SAASC,OAAvB,gBAAAG,EAAmC,SAAS;AAAA,IAC3D,CAACE,MAAYA,EAAQ,WAAWJ,EAAQ,YAAYI,EAAQ,UAAU,iBAAiBA,EAAQ,WAAW;AAAA;AAE5G,SAAID,KAAA,QAAAA,EAAU,SAAe,MAAM,KAAK,EAAE,QAAQA,EAAS,OAAA,GAAU,CAACE,GAAGC,MAAYA,CAAO,IACrFL,IAAa,OAAO,KAAKA,CAAU,IAAI,CAAA;AAChD;AAGO,MAAMM,IAAS,CAACC,GAAgBP,OACrCA,KAAA,gBAAAA,EAAa,OAAOO,CAAK,OAAM,OAAOA,KAAS,GAAG;"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * What each channel is bound to — Plot's names, and Plot's rule that **a CSS colour is a constant
3
+ * and anything else is a column.** `fill="kind"` spends colour on a category; `fill="var(--foreground)"`
4
+ * paints every point one ink and leaves colour free to mean the selection.
5
+ */
6
+ export interface Channels {
7
+ /** Which column colours a point, or a CSS colour every point wears. */
8
+ fill?: string;
9
+ /**
10
+ * Which column a point's shape carries. A tile carries one categorical column, so bound beside a
11
+ * `fill` column this reads the same one; a second would need a second array everywhere.
12
+ */
13
+ symbol?: string;
14
+ /** What tints a link. Absent, a link takes the colour of the vertex it leaves. */
15
+ stroke?: string;
16
+ }
17
+ /** Narrow on purpose — `var(…)`, a hex, a colour function — so a bare word is always a column. */
18
+ export declare function isColour(value: string | undefined): value is string;
19
+ /** The columns a binding reads, which is what a scan projects. */
20
+ export interface Binding {
21
+ /** The one categorical column: `fill` when it names one, `symbol` when `fill` is a constant. */
22
+ readonly category: string | undefined;
23
+ /** What the size ramp is spent on — Plot's `r`. */
24
+ readonly size: string | undefined;
25
+ /** The text a label and the hover card show — Plot's `title`. */
26
+ readonly title: string | undefined;
27
+ }
28
+ export declare function bindingOf(options: Channels & {
29
+ r?: string;
30
+ title?: string;
31
+ }): Binding;
32
+ /** A binding is a projection: `fill`, `r` and `title` as columns become the scan's `select`. */
33
+ export declare function projectionOf(binding: Binding, fixed: readonly string[]): string[];
34
+ //# sourceMappingURL=channels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channels.d.ts","sourceRoot":"","sources":["../../src/core/channels.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,kGAAkG;AAClG,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,IAAI,MAAM,CAEnE;AAED,kEAAkE;AAClE,MAAM,WAAW,OAAO;IACtB,gGAAgG;IAChG,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,QAAQ,GAAG;IAAE,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAMrF;AAED,gGAAgG;AAChG,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAEjF"}
@@ -0,0 +1,19 @@
1
+ function l(r) {
2
+ return r !== void 0 && /^(var\(|#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(r);
3
+ }
4
+ function i(r) {
5
+ return {
6
+ category: l(r.fill) ? r.symbol : r.fill,
7
+ size: r.r,
8
+ title: r.title
9
+ };
10
+ }
11
+ function c(r, t) {
12
+ return [...new Set([...t, r.category, r.size, r.title].filter((e) => e !== void 0))];
13
+ }
14
+ export {
15
+ i as bindingOf,
16
+ l as isColour,
17
+ c as projectionOf
18
+ };
19
+ //# sourceMappingURL=channels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channels.js","sources":["../../src/core/channels.ts"],"sourcesContent":["/**\n * What each channel is bound to — Plot's names, and Plot's rule that **a CSS colour is a constant\n * and anything else is a column.** `fill=\"kind\"` spends colour on a category; `fill=\"var(--foreground)\"`\n * paints every point one ink and leaves colour free to mean the selection.\n */\nexport interface Channels {\n /** Which column colours a point, or a CSS colour every point wears. */\n fill?: string;\n /**\n * Which column a point's shape carries. A tile carries one categorical column, so bound beside a\n * `fill` column this reads the same one; a second would need a second array everywhere.\n */\n symbol?: string;\n /** What tints a link. Absent, a link takes the colour of the vertex it leaves. */\n stroke?: string;\n}\n\n/** Narrow on purpose — `var(…)`, a hex, a colour function — so a bare word is always a column. */\nexport function isColour(value: string | undefined): value is string {\n return value !== undefined && /^(var\\(|#|rgb|hsl|oklch|oklab|lab|lch|color\\()/i.test(value);\n}\n\n/** The columns a binding reads, which is what a scan projects. */\nexport interface Binding {\n /** The one categorical column: `fill` when it names one, `symbol` when `fill` is a constant. */\n readonly category: string | undefined;\n /** What the size ramp is spent on — Plot's `r`. */\n readonly size: string | undefined;\n /** The text a label and the hover card show — Plot's `title`. */\n readonly title: string | undefined;\n}\n\nexport function bindingOf(options: Channels & { r?: string; title?: string }): Binding {\n return {\n category: isColour(options.fill) ? options.symbol : options.fill,\n size: options.r,\n title: options.title,\n };\n}\n\n/** A binding is a projection: `fill`, `r` and `title` as columns become the scan's `select`. */\nexport function projectionOf(binding: Binding, fixed: readonly string[]): string[] {\n return [...new Set([...fixed, binding.category, binding.size, binding.title].filter((c) => c !== undefined))];\n}\n"],"names":["isColour","value","bindingOf","options","projectionOf","binding","fixed","c"],"mappings":"AAkBO,SAASA,EAASC,GAA4C;AACnE,SAAOA,MAAU,UAAa,kDAAkD,KAAKA,CAAK;AAC5F;AAYO,SAASC,EAAUC,GAA6D;AACrF,SAAO;AAAA,IACL,UAAUH,EAASG,EAAQ,IAAI,IAAIA,EAAQ,SAASA,EAAQ;AAAA,IAC5D,MAAMA,EAAQ;AAAA,IACd,OAAOA,EAAQ;AAAA,EAAA;AAEnB;AAGO,SAASC,EAAaC,GAAkBC,GAAoC;AACjF,SAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAGA,GAAOD,EAAQ,UAAUA,EAAQ,MAAMA,EAAQ,KAAK,EAAE,OAAO,CAACE,MAAMA,MAAM,MAAS,CAAC,CAAC;AAC9G;"}
@@ -0,0 +1,18 @@
1
+ import { Corpus, TileMatrixSet } from '@fossil-lang/corpus';
2
+ import { VertexId } from './resident';
3
+ /** One vertex's row, as its type's fields order it — every column but the address and the position. */
4
+ export interface VertexDetail {
5
+ readonly vertex: VertexId;
6
+ readonly fields: readonly {
7
+ readonly name: string;
8
+ readonly value: unknown;
9
+ }[];
10
+ }
11
+ /**
12
+ * **Detail is fetched, not carried** — ADR 0001. A tile holds what the channels project; the rest of a
13
+ * row is asked for when a reader focuses the vertex, as a scan of the one payload tile its `dense_id`
14
+ * falls in, filtered to it. `corpus.node` takes an identity, and a tile carries an address, so the
15
+ * address is what asks.
16
+ */
17
+ export declare function readVertex(corpus: Corpus, matrix: TileMatrixSet, vertex: VertexId, signal?: AbortSignal): Promise<VertexDetail | null>;
18
+ //# sourceMappingURL=detail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detail.d.ts","sourceRoot":"","sources":["../../src/core/detail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwC,KAAK,MAAM,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC5G,OAAO,EAAW,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGpD,uGAAuG;AACvG,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CAChF;AAKD;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,QAAQ,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAa9B"}
@@ -0,0 +1,20 @@
1
+ import { PAYLOAD_ADDRESS as u, PAYLOAD_COORDINATES as m } from "@fossil-lang/corpus";
2
+ import { denseOf as y } from "./resident.js";
3
+ import { tileOfDense as D } from "./tile-matrix.js";
4
+ const s = u[0], A = /* @__PURE__ */ new Set([s, ...m]);
5
+ async function S(o, t, r, p) {
6
+ const l = y(r), c = D(t, l), a = o.types.vertices.find((e) => e.type === t.type);
7
+ if (!c || !a) return null;
8
+ const f = a.fields.map((e) => e.name).filter((e) => !A.has(e)), d = o.scan({ type: t.type, filter: { column: s, op: "=", value: l }, select: [s, ...f] }), [n] = await d.read([c], { signal: p });
9
+ return !n || n.numRows === 0 ? null : {
10
+ vertex: r,
11
+ fields: f.map((e) => {
12
+ var i;
13
+ return { name: e, value: ((i = n.getChild(e)) == null ? void 0 : i.toArray()[0]) ?? null };
14
+ })
15
+ };
16
+ }
17
+ export {
18
+ S as readVertex
19
+ };
20
+ //# sourceMappingURL=detail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detail.js","sources":["../../src/core/detail.ts"],"sourcesContent":["import { PAYLOAD_ADDRESS, PAYLOAD_COORDINATES, type Corpus, type TileMatrixSet } from \"@fossil-lang/corpus\";\nimport { denseOf, type VertexId } from \"./resident\";\nimport { tileOfDense } from \"./tile-matrix\";\n\n/** One vertex's row, as its type's fields order it — every column but the address and the position. */\nexport interface VertexDetail {\n readonly vertex: VertexId;\n readonly fields: readonly { readonly name: string; readonly value: unknown }[];\n}\n\nconst KEY = PAYLOAD_ADDRESS[0] as string;\nconst HIDDEN = new Set<string>([KEY, ...PAYLOAD_COORDINATES]);\n\n/**\n * **Detail is fetched, not carried** — ADR 0001. A tile holds what the channels project; the rest of a\n * row is asked for when a reader focuses the vertex, as a scan of the one payload tile its `dense_id`\n * falls in, filtered to it. `corpus.node` takes an identity, and a tile carries an address, so the\n * address is what asks.\n */\nexport async function readVertex(\n corpus: Corpus,\n matrix: TileMatrixSet,\n vertex: VertexId,\n signal?: AbortSignal,\n): Promise<VertexDetail | null> {\n const dense = denseOf(vertex);\n const address = tileOfDense(matrix, dense);\n const type = corpus.types.vertices.find((t) => t.type === matrix.type);\n if (!address || !type) return null;\n const names = type.fields.map((field) => field.name).filter((name) => !HIDDEN.has(name));\n const scan = corpus.scan({ type: matrix.type, filter: { column: KEY, op: \"=\", value: dense }, select: [KEY, ...names] });\n const [rows] = await scan.read([address], { signal });\n if (!rows || rows.numRows === 0) return null;\n return {\n vertex,\n fields: names.map((name) => ({ name, value: rows.getChild(name)?.toArray()[0] ?? null })),\n };\n}\n"],"names":["KEY","PAYLOAD_ADDRESS","HIDDEN","PAYLOAD_COORDINATES","readVertex","corpus","matrix","vertex","signal","dense","denseOf","address","tileOfDense","type","t","names","field","name","scan","rows","_a"],"mappings":";;;AAUA,MAAMA,IAAMC,EAAgB,CAAC,GACvBC,IAAS,oBAAI,IAAY,CAACF,GAAK,GAAGG,CAAmB,CAAC;AAQ5D,eAAsBC,EACpBC,GACAC,GACAC,GACAC,GAC8B;AAC9B,QAAMC,IAAQC,EAAQH,CAAM,GACtBI,IAAUC,EAAYN,GAAQG,CAAK,GACnCI,IAAOR,EAAO,MAAM,SAAS,KAAK,CAACS,MAAMA,EAAE,SAASR,EAAO,IAAI;AACrE,MAAI,CAACK,KAAW,CAACE,EAAM,QAAO;AAC9B,QAAME,IAAQF,EAAK,OAAO,IAAI,CAACG,MAAUA,EAAM,IAAI,EAAE,OAAO,CAACC,MAAS,CAACf,EAAO,IAAIe,CAAI,CAAC,GACjFC,IAAOb,EAAO,KAAK,EAAE,MAAMC,EAAO,MAAM,QAAQ,EAAE,QAAQN,GAAK,IAAI,KAAK,OAAOS,KAAS,QAAQ,CAACT,GAAK,GAAGe,CAAK,GAAG,GACjH,CAACI,CAAI,IAAI,MAAMD,EAAK,KAAK,CAACP,CAAO,GAAG,EAAE,QAAAH,GAAQ;AACpD,SAAI,CAACW,KAAQA,EAAK,YAAY,IAAU,OACjC;AAAA,IACL,QAAAZ;AAAA,IACA,QAAQQ,EAAM,IAAI,CAACE,MAAA;;AAAU,eAAE,MAAAA,GAAM,SAAOG,IAAAD,EAAK,SAASF,CAAI,MAAlB,gBAAAG,EAAqB,UAAU,OAAM;KAAO;AAAA,EAAA;AAE5F;"}
@@ -0,0 +1,34 @@
1
+ import { Filter } from '@fossil-lang/corpus';
2
+ import { MosaicClient, Selection } from '@kanzo-tech/mosaic';
3
+ /**
4
+ * **The page's crossfilter, as scan's typed filter.** The graph listens to the `Selection` rather
5
+ * than being a client of the coordinator: a client hands the coordinator SQL to run, and the graph
6
+ * has none — it hands fossil a `Filter` and fossil binds it. So this takes the half of Mosaic's
7
+ * protocol that is about filtering: the `value` event, and `selection.predicate(client)`.
8
+ *
9
+ * The nodes are read by their `type`, never by importing mosaic-sql's classes: what arrives is
10
+ * whatever the page's copy of mosaic-sql built.
11
+ */
12
+ /** A predicate this cannot express as a `Filter` — reported, never dropped. */
13
+ export declare class UntranslatableFilter extends Error {
14
+ readonly name = "UntranslatableFilter";
15
+ }
16
+ /** One mosaic-sql expression node → one `Filter`. */
17
+ export declare function translate(node: unknown): Filter;
18
+ /**
19
+ * The page's filter for the graph, or `undefined` for none. `selection.predicate` skips the clause
20
+ * the graph published itself, which is the exemption the next function takes back.
21
+ */
22
+ export declare function filterFor(selection: Selection, self: MosaicClient): Filter | undefined;
23
+ /**
24
+ * An identity for `clients`, and nothing more: it is never connected, so the coordinator never
25
+ * queries on its behalf.
26
+ */
27
+ export declare function graphClient(): MosaicClient;
28
+ /**
29
+ * **The reader's pick, published** — naming the graph in `clients`, so the crossfilter skips it for
30
+ * the graph and applies it to every chart. A canvas that draws only what survives would otherwise
31
+ * answer a lasso of thirteen by removing the other 1,530.
32
+ */
33
+ export declare function publish(selection: Selection, self: MosaicClient, column: string, dense: readonly number[] | null): void;
34
+ //# sourceMappingURL=filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../src/core/filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAW,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAgB,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEhF;;;;;;;;GAQG;AAEH,+EAA+E;AAC/E,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,SAAkB,IAAI,0BAA0B;CACjD;AAoDD,qDAAqD;AACrD,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAyC/C;AAID;;;GAGG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAItF;AAED;;;GAGG;AACH,wBAAgB,WAAW,IAAI,YAAY,CAE1C;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,YAAY,EAClB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GAC9B,IAAI,CAON"}
@@ -0,0 +1,99 @@
1
+ var f = Object.defineProperty;
2
+ var h = (r, t, e) => t in r ? f(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e;
3
+ var p = (r, t, e) => h(r, typeof t != "symbol" ? t + "" : t, e);
4
+ import { MosaicClient as y, clausePoints as O } from "@kanzo-tech/mosaic";
5
+ class m extends Error {
6
+ constructor() {
7
+ super(...arguments);
8
+ p(this, "name", "UntranslatableFilter");
9
+ }
10
+ }
11
+ const A = {
12
+ "=": "=",
13
+ "==": "=",
14
+ "IS NOT DISTINCT FROM": "=",
15
+ "<>": "!=",
16
+ "!=": "!=",
17
+ "<": "<",
18
+ "<=": "<=",
19
+ ">": ">",
20
+ ">=": ">="
21
+ }, N = { "=": "=", "!=": "!=", "<": ">", "<=": ">=", ">": "<", ">=": "<=" }, s = (r) => {
22
+ throw new m(
23
+ `the graph cannot express this crossfilter clause as a filter on the corpus: ${String(r)}`
24
+ );
25
+ };
26
+ function o(r) {
27
+ const t = r;
28
+ return (t == null ? void 0 : t.type) === "COLUMN_REF" && typeof t.name == "string" ? t.name : null;
29
+ }
30
+ function i(r) {
31
+ const t = r, e = (t == null ? void 0 : t.type) === "LITERAL" || (t == null ? void 0 : t.type) === "PARAM" ? t.value : s(r);
32
+ if (typeof e == "number" || typeof e == "bigint" || typeof e == "string" || typeof e == "boolean")
33
+ return e;
34
+ if (e instanceof Date) {
35
+ const n = e.toISOString();
36
+ return n.endsWith("T00:00:00.000Z") ? n.slice(0, 10) : n.replace("T", " ").replace("Z", "");
37
+ }
38
+ return s(r);
39
+ }
40
+ function g(r) {
41
+ const t = r;
42
+ return (Array.isArray(r) ? r : Array.isArray(t == null ? void 0 : t.values) ? t.values : s(r)).map(i);
43
+ }
44
+ function l(r) {
45
+ const t = r;
46
+ if (Array.isArray(r)) return c(r.map(l));
47
+ switch (t == null ? void 0 : t.type) {
48
+ case "LOGICAL_OPERATOR": {
49
+ const e = (t.clauses ?? []).map(l);
50
+ return t.op === "OR" ? { or: e } : c(e);
51
+ }
52
+ case "UNARY":
53
+ return t.op === "NOT" ? { not: l(t.expr) } : s(r);
54
+ case "UNARY_POSTFIX": {
55
+ const e = o(t.expr) ?? s(r);
56
+ return t.op === "IS NULL" ? { column: e, op: "is null" } : t.op === "IS NOT NULL" ? { column: e, op: "not null" } : s(r);
57
+ }
58
+ case "BINARY": {
59
+ const e = A[String(t.op).toUpperCase()] ?? s(r), n = o(t.left);
60
+ return n !== null ? { column: n, op: e, value: i(t.right) } : { column: o(t.right) ?? s(r), op: N[e], value: i(t.left) };
61
+ }
62
+ case "BETWEEN":
63
+ case "NOT_BETWEEN": {
64
+ const e = o(t.expr) ?? s(r), [n, a] = t.extent ?? s(r), u = c([
65
+ { column: e, op: ">=", value: i(n) },
66
+ { column: e, op: "<=", value: i(a) }
67
+ ]);
68
+ return t.type === "BETWEEN" ? u : { not: u };
69
+ }
70
+ case "IN":
71
+ return { column: o(t.expr) ?? s(r), op: "in", values: g(t.values) };
72
+ default:
73
+ return s(r);
74
+ }
75
+ }
76
+ const c = (r) => r.length === 1 ? r[0] : { and: r };
77
+ function R(r, t) {
78
+ const e = r.predicate(t), n = (Array.isArray(e) ? e : [e]).filter((a) => a != null);
79
+ return n.length === 0 ? void 0 : l(n);
80
+ }
81
+ function T() {
82
+ return new y();
83
+ }
84
+ function I(r, t, e, n) {
85
+ r.update(
86
+ O([e], n && n.length > 0 ? n.map((a) => [a]) : void 0, {
87
+ source: t,
88
+ clients: /* @__PURE__ */ new Set([t])
89
+ })
90
+ );
91
+ }
92
+ export {
93
+ m as UntranslatableFilter,
94
+ R as filterFor,
95
+ T as graphClient,
96
+ I as publish,
97
+ l as translate
98
+ };
99
+ //# sourceMappingURL=filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter.js","sources":["../../src/core/filter.ts"],"sourcesContent":["import type { Filter, Literal } from \"@fossil-lang/corpus\";\nimport { MosaicClient, clausePoints, type Selection } from \"@kanzo-tech/mosaic\";\n\n/**\n * **The page's crossfilter, as scan's typed filter.** The graph listens to the `Selection` rather\n * than being a client of the coordinator: a client hands the coordinator SQL to run, and the graph\n * has none — it hands fossil a `Filter` and fossil binds it. So this takes the half of Mosaic's\n * protocol that is about filtering: the `value` event, and `selection.predicate(client)`.\n *\n * The nodes are read by their `type`, never by importing mosaic-sql's classes: what arrives is\n * whatever the page's copy of mosaic-sql built.\n */\n\n/** A predicate this cannot express as a `Filter` — reported, never dropped. */\nexport class UntranslatableFilter extends Error {\n override readonly name = \"UntranslatableFilter\";\n}\n\ninterface Node {\n readonly type?: string;\n readonly op?: string;\n readonly [field: string]: unknown;\n}\n\nconst COMPARISONS: Record<string, \"=\" | \"!=\" | \"<\" | \"<=\" | \">\" | \">=\"> = {\n \"=\": \"=\",\n \"==\": \"=\",\n \"IS NOT DISTINCT FROM\": \"=\",\n \"<>\": \"!=\",\n \"!=\": \"!=\",\n \"<\": \"<\",\n \"<=\": \"<=\",\n \">\": \">\",\n \">=\": \">=\",\n};\nconst MIRRORED = { \"=\": \"=\", \"!=\": \"!=\", \"<\": \">\", \"<=\": \">=\", \">\": \"<\", \">=\": \"<=\" } as const;\n\nconst fail = (what: unknown): never => {\n throw new UntranslatableFilter(\n `the graph cannot express this crossfilter clause as a filter on the corpus: ${String(what)}`,\n );\n};\n\nfunction columnOf(node: unknown): string | null {\n const n = node as Node | null;\n if (n?.type === \"COLUMN_REF\" && typeof n.name === \"string\") return n.name;\n return null;\n}\n\nfunction literalOf(node: unknown): Literal {\n const n = node as Node | null;\n const value = n?.type === \"LITERAL\" || n?.type === \"PARAM\" ? n.value : fail(node);\n if (typeof value === \"number\" || typeof value === \"bigint\" || typeof value === \"string\" || typeof value === \"boolean\") {\n return value;\n }\n if (value instanceof Date) {\n const iso = value.toISOString();\n return iso.endsWith(\"T00:00:00.000Z\") ? iso.slice(0, 10) : iso.replace(\"T\", \" \").replace(\"Z\", \"\");\n }\n return fail(node);\n}\n\nfunction valuesOf(node: unknown): Literal[] {\n const n = node as Node | null;\n const list = Array.isArray(node) ? node : Array.isArray(n?.values) ? n.values : fail(node);\n return (list as unknown[]).map(literalOf);\n}\n\n/** One mosaic-sql expression node → one `Filter`. */\nexport function translate(node: unknown): Filter {\n const n = node as Node | null;\n if (Array.isArray(node)) return and(node.map(translate));\n switch (n?.type) {\n case \"LOGICAL_OPERATOR\": {\n const clauses = ((n.clauses as unknown[] | undefined) ?? []).map(translate);\n return n.op === \"OR\" ? { or: clauses } : and(clauses);\n }\n case \"UNARY\":\n if (n.op === \"NOT\") return { not: translate(n.expr) };\n return fail(node);\n case \"UNARY_POSTFIX\": {\n const column = columnOf(n.expr) ?? fail(node);\n if (n.op === \"IS NULL\") return { column, op: \"is null\" };\n if (n.op === \"IS NOT NULL\") return { column, op: \"not null\" };\n return fail(node);\n }\n case \"BINARY\": {\n const op = COMPARISONS[String(n.op).toUpperCase()] ?? fail(node);\n const left = columnOf(n.left);\n if (left !== null) return { column: left, op, value: literalOf(n.right) };\n const right = columnOf(n.right) ?? fail(node);\n return { column: right, op: MIRRORED[op], value: literalOf(n.left) };\n }\n case \"BETWEEN\":\n case \"NOT_BETWEEN\": {\n const column = columnOf(n.expr) ?? fail(node);\n const [lo, hi] = (n.extent as unknown[] | undefined) ?? fail(node);\n const within: Filter = and([\n { column, op: \">=\", value: literalOf(lo) },\n { column, op: \"<=\", value: literalOf(hi) },\n ]);\n return n.type === \"BETWEEN\" ? within : { not: within };\n }\n case \"IN\": {\n const column = columnOf(n.expr) ?? fail(node);\n return { column, op: \"in\", values: valuesOf(n.values) };\n }\n default:\n return fail(node);\n }\n}\n\nconst and = (clauses: Filter[]): Filter => (clauses.length === 1 ? (clauses[0] as Filter) : { and: clauses });\n\n/**\n * The page's filter for the graph, or `undefined` for none. `selection.predicate` skips the clause\n * the graph published itself, which is the exemption the next function takes back.\n */\nexport function filterFor(selection: Selection, self: MosaicClient): Filter | undefined {\n const predicate = selection.predicate(self) as unknown;\n const clauses = (Array.isArray(predicate) ? predicate : [predicate]).filter((p) => p !== undefined && p !== null);\n return clauses.length === 0 ? undefined : translate(clauses);\n}\n\n/**\n * An identity for `clients`, and nothing more: it is never connected, so the coordinator never\n * queries on its behalf.\n */\nexport function graphClient(): MosaicClient {\n return new MosaicClient();\n}\n\n/**\n * **The reader's pick, published** — naming the graph in `clients`, so the crossfilter skips it for\n * the graph and applies it to every chart. A canvas that draws only what survives would otherwise\n * answer a lasso of thirteen by removing the other 1,530.\n */\nexport function publish(\n selection: Selection,\n self: MosaicClient,\n column: string,\n dense: readonly number[] | null,\n): void {\n selection.update(\n clausePoints([column], dense && dense.length > 0 ? dense.map((d) => [d]) : undefined, {\n source: self,\n clients: new Set([self]),\n }),\n );\n}\n"],"names":["UntranslatableFilter","__publicField","COMPARISONS","MIRRORED","fail","what","columnOf","node","n","literalOf","value","iso","valuesOf","translate","and","clauses","column","op","left","lo","hi","within","filterFor","selection","self","predicate","p","graphClient","MosaicClient","publish","dense","clausePoints","d"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { Tile } from './tile';
2
+ /**
3
+ * deck.gl's `'best-available'` refinement — `updateTileStateDefault`, the only strategy.
4
+ *
5
+ * A selected tile with content is drawn. One without is covered by its nearest ancestor holding
6
+ * content, and failing that by whatever of its descendants holds some, so a pan never shows a hole.
7
+ * A parent is therefore drawn only while a selected child is loading: at rest every selected tile
8
+ * holds content and no stand-in is needed.
9
+ *
10
+ * "Holding content" rather than deck.gl's `isLoaded`, because a tile reloading for a new question
11
+ * keeps its old picture until the new one arrives.
12
+ */
13
+ export declare function refine(tiles: Iterable<Tile>): void;
14
+ //# sourceMappingURL=refine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refine.d.ts","sourceRoot":"","sources":["../../src/core/refine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAOlD"}
@@ -0,0 +1,20 @@
1
+ function f(o) {
2
+ const n = [...o], e = /* @__PURE__ */ new Set();
3
+ for (const t of n)
4
+ t.isSelected && !c(t, e) && i(t, e);
5
+ for (const t of n) t.isVisible = e.has(t);
6
+ }
7
+ function c(o, n) {
8
+ for (let e = o; e; e = e.parent)
9
+ if (e.content)
10
+ return n.add(e), !0;
11
+ return !1;
12
+ }
13
+ function i(o, n) {
14
+ for (const e of o.children)
15
+ e.content ? n.add(e) : i(e, n);
16
+ }
17
+ export {
18
+ f as refine
19
+ };
20
+ //# sourceMappingURL=refine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refine.js","sources":["../../src/core/refine.ts"],"sourcesContent":["import type { Tile } from \"./tile\";\n\n/**\n * deck.gl's `'best-available'` refinement — `updateTileStateDefault`, the only strategy.\n *\n * A selected tile with content is drawn. One without is covered by its nearest ancestor holding\n * content, and failing that by whatever of its descendants holds some, so a pan never shows a hole.\n * A parent is therefore drawn only while a selected child is loading: at rest every selected tile\n * holds content and no stand-in is needed.\n *\n * \"Holding content\" rather than deck.gl's `isLoaded`, because a tile reloading for a new question\n * keeps its old picture until the new one arrives.\n */\nexport function refine(tiles: Iterable<Tile>): void {\n const all = [...tiles];\n const visible = new Set<Tile>();\n for (const tile of all) {\n if (tile.isSelected && !inAncestors(tile, visible)) inDescendants(tile, visible);\n }\n for (const tile of all) tile.isVisible = visible.has(tile);\n}\n\nfunction inAncestors(start: Tile, visible: Set<Tile>): boolean {\n for (let tile: Tile | null = start; tile; tile = tile.parent) {\n if (tile.content) {\n visible.add(tile);\n return true;\n }\n }\n return false;\n}\n\nfunction inDescendants(tile: Tile, visible: Set<Tile>): void {\n for (const child of tile.children) {\n if (child.content) visible.add(child);\n else inDescendants(child, visible);\n }\n}\n"],"names":["refine","tiles","all","visible","tile","inAncestors","inDescendants","start","child"],"mappings":"AAaO,SAASA,EAAOC,GAA6B;AAClD,QAAMC,IAAM,CAAC,GAAGD,CAAK,GACfE,wBAAc,IAAA;AACpB,aAAWC,KAAQF;AACjB,IAAIE,EAAK,cAAc,CAACC,EAAYD,GAAMD,CAAO,KAAGG,EAAcF,GAAMD,CAAO;AAEjF,aAAWC,KAAQF,EAAK,CAAAE,EAAK,YAAYD,EAAQ,IAAIC,CAAI;AAC3D;AAEA,SAASC,EAAYE,GAAaJ,GAA6B;AAC7D,WAASC,IAAoBG,GAAOH,GAAMA,IAAOA,EAAK;AACpD,QAAIA,EAAK;AACP,aAAAD,EAAQ,IAAIC,CAAI,GACT;AAGX,SAAO;AACT;AAEA,SAASE,EAAcF,GAAYD,GAA0B;AAC3D,aAAWK,KAASJ,EAAK;AACvB,IAAII,EAAM,UAASL,EAAQ,IAAIK,CAAK,IAC/BF,EAAcE,GAAOL,CAAO;AAErC;"}
@@ -1,4 +1,3 @@
1
- import { Slice } from './bounded';
2
1
  /**
3
2
  * Who is drawn, and where the GPU is drawing them.
4
3
  *
@@ -52,7 +51,7 @@ export declare function denseOf(vertex: VertexId): number;
52
51
  *
53
52
  * Built once per residency change and never mutated, because that is what a residency change *is*: a
54
53
  * different set of vertices, in a different order. It belongs to whoever owns residency — that is
55
- * `useQueryLoop`, which holds the answer — and is read from there by everything else, because a
54
+ * the renderer, which composes the buffers — and is read from there by everything else, because a
56
55
  * second copy is a second copy that can disagree with the buffers on screen.
57
56
  */
58
57
  export interface Resident {
@@ -68,12 +67,13 @@ export interface Resident {
68
67
  verticesAt(indices: Iterable<number>): VertexId[];
69
68
  }
70
69
  /**
71
- * The map an answer implies. `null` — before the first answer — is nobody resident, not an error.
70
+ * The map a composition implies, over its first `size` slots. A composition puts its vertices
71
+ * first — then the cells, which have no identity, then the far ends drawn only so an edge has one —
72
+ * so nothing hovers, selects or frames a slot past them. Before the first composition nobody is
73
+ * resident, which is not an error.
72
74
  *
73
75
  * Keyed by the identity itself. `Map` and `Set` compare keys by SameValueZero, which for a `bigint`
74
- * is *value* equality and not reference equality — two separately-constructed `vertexId(0, 5)`
75
- * reach the same entry. That is asserted rather than assumed in `resident.test.ts`, because the
76
- * whole file rests on it and BigInt being an object-shaped primitive makes it a fair thing to doubt.
76
+ * is *value* equality — asserted in `resident.test.ts`, because the whole file rests on it.
77
77
  */
78
- export declare function residentOf(slice: Slice | null): Resident;
78
+ export declare function residentOf(vertices?: BigUint64Array | null, size?: number): Resident;
79
79
  //# sourceMappingURL=resident.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resident.d.ts","sourceRoot":"","sources":["../../src/core/resident.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,MAAM,CAAA;CAAE,CAAC;AAYnE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,CAE9D;AAED,8FAA8F;AAC9F,wBAAgB,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAE/C;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAEhD;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,QAAQ;IACvB,iCAAiC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,OAAO,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IAC9C,0FAA0F;IAC1F,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IACxC,+EAA+E;IAC/E,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,CAAC;IAClD,+FAA+F;IAC/F,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;CACnD;AAID;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,QAAQ,GAAE,cAAc,GAAG,IAAa,EAAE,IAAI,SAAwB,GAAG,QAAQ,CA6B3G"}