@kanzo-tech/graph 0.18.0 → 0.20.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 (76) hide show
  1. package/README.md +25 -7
  2. package/dist/core/detail.d.ts +3 -1
  3. package/dist/core/detail.d.ts.map +1 -1
  4. package/dist/core/detail.js +55 -30
  5. package/dist/core/detail.js.map +1 -1
  6. package/dist/core/error.d.ts +16 -0
  7. package/dist/core/error.d.ts.map +1 -0
  8. package/dist/core/error.js +14 -0
  9. package/dist/core/error.js.map +1 -0
  10. package/dist/core/filter.d.ts +0 -13
  11. package/dist/core/filter.d.ts.map +1 -1
  12. package/dist/core/filter.js +47 -55
  13. package/dist/core/filter.js.map +1 -1
  14. package/dist/core/load.d.ts +5 -0
  15. package/dist/core/load.d.ts.map +1 -1
  16. package/dist/core/load.js +93 -78
  17. package/dist/core/load.js.map +1 -1
  18. package/dist/core/state.d.ts +21 -6
  19. package/dist/core/state.d.ts.map +1 -1
  20. package/dist/core/store.d.ts.map +1 -1
  21. package/dist/core/store.js +107 -101
  22. package/dist/core/store.js.map +1 -1
  23. package/dist/core/types.d.ts +4 -4
  24. package/dist/core/types.d.ts.map +1 -1
  25. package/dist/index.d.ts +8 -3
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +27 -19
  28. package/dist/index.js.map +1 -1
  29. package/dist/parts/graph-canvas.js +45 -45
  30. package/dist/parts/graph-canvas.js.map +1 -1
  31. package/dist/parts/graph-counts.d.ts +15 -0
  32. package/dist/parts/graph-counts.d.ts.map +1 -0
  33. package/dist/parts/graph-counts.js +41 -0
  34. package/dist/parts/graph-counts.js.map +1 -0
  35. package/dist/parts/graph-inspector.d.ts.map +1 -1
  36. package/dist/parts/graph-inspector.js +48 -48
  37. package/dist/parts/graph-inspector.js.map +1 -1
  38. package/dist/parts/graph-legend.d.ts +3 -2
  39. package/dist/parts/graph-legend.d.ts.map +1 -1
  40. package/dist/parts/graph-legend.js +30 -48
  41. package/dist/parts/graph-legend.js.map +1 -1
  42. package/dist/parts/graph-looks.d.ts +16 -0
  43. package/dist/parts/graph-looks.d.ts.map +1 -0
  44. package/dist/parts/graph-looks.js +138 -0
  45. package/dist/parts/graph-looks.js.map +1 -0
  46. package/dist/parts/graph-search.d.ts +17 -0
  47. package/dist/parts/graph-search.d.ts.map +1 -0
  48. package/dist/parts/graph-search.js +89 -0
  49. package/dist/parts/graph-search.js.map +1 -0
  50. package/dist/react/use-graph-prefs.d.ts +6 -2
  51. package/dist/react/use-graph-prefs.d.ts.map +1 -1
  52. package/dist/react/use-graph-prefs.js +7 -7
  53. package/dist/react/use-graph-prefs.js.map +1 -1
  54. package/dist/react/use-graph.js +61 -55
  55. package/dist/react/use-graph.js.map +1 -1
  56. package/dist/render/camera.d.ts +26 -0
  57. package/dist/render/camera.d.ts.map +1 -0
  58. package/dist/render/camera.js +40 -0
  59. package/dist/render/camera.js.map +1 -0
  60. package/dist/render/graph-looks.d.ts +10 -0
  61. package/dist/render/graph-looks.d.ts.map +1 -1
  62. package/dist/render/graph-looks.js +37 -20
  63. package/dist/render/graph-looks.js.map +1 -1
  64. package/dist/render/placement.d.ts +23 -0
  65. package/dist/render/placement.d.ts.map +1 -0
  66. package/dist/render/placement.js +23 -0
  67. package/dist/render/placement.js.map +1 -0
  68. package/dist/render/renderer.d.ts +1 -0
  69. package/dist/render/renderer.d.ts.map +1 -1
  70. package/dist/render/renderer.js +115 -96
  71. package/dist/render/renderer.js.map +1 -1
  72. package/dist/render/webgl.d.ts +5 -4
  73. package/dist/render/webgl.d.ts.map +1 -1
  74. package/dist/render/webgl.js +14 -11
  75. package/dist/render/webgl.js.map +1 -1
  76. package/package.json +10 -9
package/README.md CHANGED
@@ -19,7 +19,7 @@ DuckDB-WASM engine and its crossfilter, cosmos.gl draws, and the parts are built
19
19
  ```tsx
20
20
  import { open } from "@fossil-lang/corpus";
21
21
  import { engine } from "@kanzo-tech/mosaic";
22
- import { GraphCanvas, GraphInspector, GraphLegend, GraphRoot, GraphToolbar } from "@kanzo-tech/graph";
22
+ import { GraphCanvas, GraphCounts, GraphInspector, GraphLegend, GraphRoot, GraphToolbar } from "@kanzo-tech/graph";
23
23
 
24
24
  const corpus = engine().then((e) => open(url, { engine: e }));
25
25
 
@@ -28,6 +28,7 @@ const corpus = engine().then((e) => open(url, { engine: e }));
28
28
  <GraphToolbar />
29
29
  <GraphLegend />
30
30
  </GraphCanvas>
31
+ <GraphCounts />
31
32
  <GraphInspector />
32
33
  </GraphRoot>;
33
34
  ```
@@ -49,8 +50,11 @@ does not survive is hidden; a clause that cannot be translated reaches `onFailur
49
50
  dropped. A lasso or a click publishes the reader's pick back into it, exempting the graph itself.
50
51
 
51
52
  **The layout is off at load.** The corpus's positions are drawn as they are; `GraphToolbar` runs and
52
- stops a force layout from where the points are, and dragging a node pins it either way. At 200,000
53
- vertices the viewer's p95 frame interval is 9.0 ms against 9.3 ms for cosmos.gl alone.
53
+ stops a force layout from where the points are. Dragging a node pins it only while a layout runs,
54
+ and releasing the pins reheats it. While a layout runs the camera follows the points until the reader
55
+ zooms or pans. `status` is the data's life and `motion` the layout's — two axes, because each moves
56
+ without the other. At 200,000 vertices the viewer's p95 frame interval is 9.0 ms against 9.3 ms for
57
+ cosmos.gl alone.
54
58
 
55
59
  The reference composition is the docs' workspace showcase: one `GraphRoot` around a shell with the
56
60
  graph, a second main view and a dock of panels.
@@ -62,13 +66,27 @@ graph, a second main view and a dock of panels.
62
66
  never changes; `useGraphState(selector)` reads a slice of the state.
63
67
  - `GraphCanvas` — the element, the grid, the vignette, the labels, the hover card and the marquee
64
68
  and lasso gesture. Children are chrome positioned over it.
65
- - `GraphLegend`, `GraphToolbar`, `GraphInspector` — the categorical scale with its tally, the
66
- commands drawn, and the focused vertex's row with a render prop for a product's own fields. No
67
- part takes a callback: what a click means is `onSelect`, `onFocus` and `onFailure` on the root.
69
+ - `GraphToolbar` — the selection tools, the selection, zoom and fit, and the layout's transport.
70
+ - `GraphLegend` — one row per category: glyph, name and how many are drawn.
71
+ - `GraphCounts` — what is drawn of the whole: "1.2K of 5K nodes drawn · 8K edges".
72
+ - `GraphSearch` — every drawn vertex's text, read once and filtered in the browser; picking reveals.
73
+ - `GraphInspector` — the focused vertex's row, with a render prop for a product's own fields.
74
+ - `GraphLooks` — Nebula, Atlas and Ink as presets over `GRAPH_SECTION`'s axes, with the axes under
75
+ Customize. Needs `GRAPH_SECTION` on the theme provider, and no root.
76
+ - `ShapeGlyph` — the glyph the canvas draws for a category, in the DOM.
77
+
78
+ No part takes a callback: what a click means is `onSelect`, `onFocus` and `onFailure` on the root.
79
+ `onFailure` is required and receives every failure as thrown — fossil's coded errors, and the graph's
80
+ own `GraphError` (`graph/no-webgl`, `graph/context-lost`, `graph/nothing-to-draw`,
81
+ `graph/untranslatable-filter`).
82
+
68
83
  - `lookFrom`, `simFrom`, `useGraphPrefs` — the form and the forces from a preferences panel's
69
- answers; `scaleOf` — what colour and shape a category wears.
84
+ answers, and the look `preset` they are; `scaleOf` — what colour and shape a category wears.
70
85
  - `VertexId` — a vertex is its `dense_id`, a `number`.
71
86
 
87
+ The full guide — the parts, layout and camera, and the workspace recipe — is at
88
+ [kanzo-tech.github.io/ui/docs/graph](https://kanzo-tech.github.io/ui/docs/graph).
89
+
72
90
  ## Why a package, and not part of `@kanzo-tech/ui`
73
91
 
74
92
  The [first admission rule](https://kanzo-tech.github.io/ui/docs/philosophy#admission) is
@@ -20,5 +20,7 @@ export declare function readVertex(corpus: Corpus, geometry: Geometry, vertex: V
20
20
  * **A label's text, for the few vertices that carry one** — `title` where the vertex's table has the
21
21
  * column, and the table's declared `identity` where it does not. One scan per table they fall in.
22
22
  */
23
- export declare function readTitles(corpus: Corpus, geometry: Geometry, vertices: readonly VertexId[], title: string | undefined, signal?: AbortSignal): Promise<Map<VertexId, string>>;
23
+ export declare function readTitles(corpus: Corpus, geometry: Geometry, vertices: readonly VertexId[], title: string | undefined, signal: AbortSignal): Promise<Map<VertexId, string>>;
24
+ /** **Every drawn vertex's text, by id** — what a search filters. One scan per drawn table, two columns. */
25
+ export declare function readEveryTitle(corpus: Corpus, geometry: Geometry, title: string | undefined, signal: AbortSignal): Promise<string[]>;
24
26
  //# sourceMappingURL=detail.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"detail.d.ts","sourceRoot":"","sources":["../../src/core/detail.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,wGAAwG;AACxG,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CAChF;AAKD;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,QAAQ,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAa9B;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,SAAS,QAAQ,EAAE,EAC7B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAmBhC"}
1
+ {"version":3,"file":"detail.d.ts","sourceRoot":"","sources":["../../src/core/detail.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,wGAAwG;AACxG,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CAChF;AAKD;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,QAAQ,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAa9B;AAMD;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,SAAS,QAAQ,EAAE,EAC7B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAqBhC;AAED,2GAA2G;AAC3G,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,MAAM,EAAE,CAAC,CAmBnB"}
@@ -1,38 +1,63 @@
1
- const y = (o, a) => a < o.size ? o.tables[o.table[a]] : void 0;
2
- async function k(o, a, i, c) {
3
- var u, f;
4
- const e = y(a, i);
5
- if (!e) return null;
6
- const l = /* @__PURE__ */ new Set([e.key, (u = e.position) == null ? void 0 : u.x, (f = e.position) == null ? void 0 : f.y]), r = e.properties.map((t) => t.name).filter((t) => !l.has(t)), n = o.scan({ table: e.name, filter: { column: e.key, op: "=", value: i }, select: [e.key, ...r], limit: 1 }), s = (await n.read(n.plan(), { signal: c })).find((t) => t.numRows > 0);
7
- return s ? {
8
- vertex: i,
9
- table: e.name,
10
- fields: r.map((t) => {
11
- var p;
12
- return { name: t, value: ((p = s.getChild(t)) == null ? void 0 : p.toArray()[0]) ?? null };
1
+ import { together as h } from "./load.js";
2
+ const k = (o, e) => e < o.size ? o.tables[o.table[e]] : void 0;
3
+ async function C(o, e, r, y) {
4
+ var f, c;
5
+ const s = k(e, r);
6
+ if (!s) return null;
7
+ const u = /* @__PURE__ */ new Set([s.key, (f = s.position) == null ? void 0 : f.x, (c = s.position) == null ? void 0 : c.y]), a = s.properties.map((t) => t.name).filter((t) => !u.has(t)), i = o.scan({ table: s.name, filter: { column: s.key, op: "=", value: r }, select: [s.key, ...a], limit: 1 }), n = (await i.read(i.plan(), { signal: y })).find((t) => t.numRows > 0);
8
+ return n ? {
9
+ vertex: r,
10
+ table: s.name,
11
+ fields: a.map((t) => {
12
+ var m;
13
+ return { name: t, value: ((m = n.getChild(t)) == null ? void 0 : m.toArray()[0]) ?? null };
13
14
  })
14
15
  } : null;
15
16
  }
16
- async function h(o, a, i, c, e) {
17
- const l = /* @__PURE__ */ new Map();
18
- for (const n of i) {
19
- const s = y(a, n);
20
- s && l.set(s, [...l.get(s) ?? [], n]);
17
+ const b = (o, e) => e !== void 0 && o.properties.some((r) => r.name === e) ? e : o.identity;
18
+ async function v(o, e, r, y, s) {
19
+ const u = /* @__PURE__ */ new Map();
20
+ for (const i of r) {
21
+ const n = k(e, i);
22
+ n && u.set(n, [...u.get(n) ?? [], i]);
21
23
  }
22
- const r = /* @__PURE__ */ new Map();
23
- return await Promise.all(
24
- [...l].map(async ([n, s]) => {
25
- var t, p;
26
- const u = c !== void 0 && n.properties.some((d) => d.name === c) ? c : n.identity, f = o.scan({ table: n.name, filter: { column: n.key, op: "in", values: s }, select: [n.key, u] });
27
- for (const d of await f.read(f.plan(), { signal: e })) {
28
- const w = ((t = d.getChild(n.key)) == null ? void 0 : t.toArray()) ?? [], b = ((p = d.getChild(u)) == null ? void 0 : p.toArray()) ?? [];
29
- for (let m = 0; m < d.numRows; m++) r.set(Number(w[m]), String(b[m] ?? ""));
30
- }
31
- })
32
- ), r;
24
+ const a = /* @__PURE__ */ new Map();
25
+ return await h(
26
+ s,
27
+ (i) => Promise.all(
28
+ [...u].map(async ([n, f]) => {
29
+ var m, d;
30
+ const c = b(n, y), t = o.scan({ table: n.name, filter: { column: n.key, op: "in", values: f }, select: [n.key, c] });
31
+ for (const l of await t.read(t.plan(), { signal: i })) {
32
+ const p = ((m = l.getChild(n.key)) == null ? void 0 : m.toArray()) ?? [], g = ((d = l.getChild(c)) == null ? void 0 : d.toArray()) ?? [];
33
+ for (let w = 0; w < l.numRows; w++) a.set(Number(p[w]), String(g[w] ?? ""));
34
+ }
35
+ })
36
+ )
37
+ ), a;
38
+ }
39
+ async function x(o, e, r, y) {
40
+ const s = new Array(e.size).fill("");
41
+ return await h(
42
+ y,
43
+ (u) => Promise.all(
44
+ e.tables.map(async (a) => {
45
+ var f, c;
46
+ const i = b(a, r), n = o.scan({ table: a.name, select: [a.key, i] });
47
+ for (const t of await n.read(n.plan(), { signal: u })) {
48
+ const m = ((f = t.getChild(a.key)) == null ? void 0 : f.toArray()) ?? [], d = ((c = t.getChild(i)) == null ? void 0 : c.toArray()) ?? [];
49
+ for (let l = 0; l < t.numRows; l++) {
50
+ const p = Number(m[l]);
51
+ p < e.size && (s[p] = String(d[l] ?? ""));
52
+ }
53
+ }
54
+ })
55
+ )
56
+ ), s;
33
57
  }
34
58
  export {
35
- h as readTitles,
36
- k as readVertex
59
+ x as readEveryTitle,
60
+ v as readTitles,
61
+ C as readVertex
37
62
  };
38
63
  //# sourceMappingURL=detail.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"detail.js","sources":["../../src/core/detail.ts"],"sourcesContent":["import type { Corpus, VertexTable } from \"@fossil-lang/corpus\";\nimport type { Geometry } from \"./load\";\nimport type { VertexId } from \"./types\";\n\n/** One vertex's row, as its table's properties order it — every column but the key and the position. */\nexport interface VertexDetail {\n readonly vertex: VertexId;\n /** The vertex table it belongs to. */\n readonly table: string;\n readonly fields: readonly { readonly name: string; readonly value: unknown }[];\n}\n\nconst tableOf = (geometry: Geometry, vertex: VertexId): VertexTable | undefined =>\n vertex < geometry.size ? geometry.tables[geometry.table[vertex] as number] : undefined;\n\n/**\n * **Detail is fetched, not carried.** The loaded graph holds positions and channels; the rest of a\n * row is asked for when a reader focuses the vertex — a scan of its table, filtered to its key.\n */\nexport async function readVertex(\n corpus: Corpus,\n geometry: Geometry,\n vertex: VertexId,\n signal?: AbortSignal,\n): Promise<VertexDetail | null> {\n const table = tableOf(geometry, vertex);\n if (!table) return null;\n const hidden = new Set([table.key, table.position?.x, table.position?.y]);\n const names = table.properties.map((p) => p.name).filter((name) => !hidden.has(name));\n const scan = corpus.scan({ table: table.name, filter: { column: table.key, op: \"=\", value: vertex }, select: [table.key, ...names], limit: 1 });\n const rows = (await scan.read(scan.plan(), { signal })).find((batch) => batch.numRows > 0);\n if (!rows) return null;\n return {\n vertex,\n table: table.name,\n fields: names.map((name) => ({ name, value: rows.getChild(name)?.toArray()[0] ?? null })),\n };\n}\n\n/**\n * **A label's text, for the few vertices that carry one** — `title` where the vertex's table has the\n * column, and the table's declared `identity` where it does not. One scan per table they fall in.\n */\nexport async function readTitles(\n corpus: Corpus,\n geometry: Geometry,\n vertices: readonly VertexId[],\n title: string | undefined,\n signal?: AbortSignal,\n): Promise<Map<VertexId, string>> {\n const byTable = new Map<VertexTable, VertexId[]>();\n for (const vertex of vertices) {\n const table = tableOf(geometry, vertex);\n if (table) byTable.set(table, [...(byTable.get(table) ?? []), vertex]);\n }\n const found = new Map<VertexId, string>();\n await Promise.all(\n [...byTable].map(async ([table, ids]) => {\n const column = title !== undefined && table.properties.some((p) => p.name === title) ? title : table.identity;\n const scan = corpus.scan({ table: table.name, filter: { column: table.key, op: \"in\", values: ids }, select: [table.key, column] });\n for (const batch of await scan.read(scan.plan(), { signal })) {\n const keys = batch.getChild(table.key)?.toArray() ?? [];\n const texts = batch.getChild(column)?.toArray() ?? [];\n for (let i = 0; i < batch.numRows; i++) found.set(Number(keys[i]), String(texts[i] ?? \"\"));\n }\n }),\n );\n return found;\n}\n"],"names":["tableOf","geometry","vertex","readVertex","corpus","signal","_a","_b","table","hidden","names","p","name","scan","rows","batch","readTitles","vertices","title","byTable","found","ids","column","keys","texts","i"],"mappings":"AAYA,MAAMA,IAAU,CAACC,GAAoBC,MACnCA,IAASD,EAAS,OAAOA,EAAS,OAAOA,EAAS,MAAMC,CAAM,CAAW,IAAI;AAM/E,eAAsBC,EACpBC,GACAH,GACAC,GACAG,GAC8B;AAZhC,MAAAC,GAAAC;AAaE,QAAMC,IAAQR,EAAQC,GAAUC,CAAM;AACtC,MAAI,CAACM,EAAO,QAAO;AACnB,QAAMC,IAAS,oBAAI,IAAI,CAACD,EAAM,MAAKF,IAAAE,EAAM,aAAN,gBAAAF,EAAgB,IAAGC,IAAAC,EAAM,aAAN,gBAAAD,EAAgB,CAAC,CAAC,GAClEG,IAAQF,EAAM,WAAW,IAAI,CAACG,MAAMA,EAAE,IAAI,EAAE,OAAO,CAACC,MAAS,CAACH,EAAO,IAAIG,CAAI,CAAC,GAC9EC,IAAOT,EAAO,KAAK,EAAE,OAAOI,EAAM,MAAM,QAAQ,EAAE,QAAQA,EAAM,KAAK,IAAI,KAAK,OAAON,EAAA,GAAU,QAAQ,CAACM,EAAM,KAAK,GAAGE,CAAK,GAAG,OAAO,EAAA,CAAG,GACxII,KAAQ,MAAMD,EAAK,KAAKA,EAAK,QAAQ,EAAE,QAAAR,EAAA,CAAQ,GAAG,KAAK,CAACU,MAAUA,EAAM,UAAU,CAAC;AACzF,SAAKD,IACE;AAAA,IACL,QAAAZ;AAAA,IACA,OAAOM,EAAM;AAAA,IACb,QAAQE,EAAM,IAAI,CAACE,MAAA;AAvBvB,UAAAN;AAuBiC,eAAE,MAAAM,GAAM,SAAON,IAAAQ,EAAK,SAASF,CAAI,MAAlB,gBAAAN,EAAqB,UAAU,OAAM;KAAO;AAAA,EAAA,IAJxE;AAMpB;AAMA,eAAsBU,EACpBZ,GACAH,GACAgB,GACAC,GACAb,GACgC;AAChC,QAAMc,wBAAc,IAAA;AACpB,aAAWjB,KAAUe,GAAU;AAC7B,UAAMT,IAAQR,EAAQC,GAAUC,CAAM;AACtC,IAAIM,KAAOW,EAAQ,IAAIX,GAAO,CAAC,GAAIW,EAAQ,IAAIX,CAAK,KAAK,CAAA,GAAKN,CAAM,CAAC;AAAA,EACvE;AACA,QAAMkB,wBAAY,IAAA;AAClB,eAAM,QAAQ;AAAA,IACZ,CAAC,GAAGD,CAAO,EAAE,IAAI,OAAO,CAACX,GAAOa,CAAG,MAAM;AA7C7C,UAAAf,GAAAC;AA8CM,YAAMe,IAASJ,MAAU,UAAaV,EAAM,WAAW,KAAK,CAACG,MAAMA,EAAE,SAASO,CAAK,IAAIA,IAAQV,EAAM,UAC/FK,IAAOT,EAAO,KAAK,EAAE,OAAOI,EAAM,MAAM,QAAQ,EAAE,QAAQA,EAAM,KAAK,IAAI,MAAM,QAAQa,EAAA,GAAO,QAAQ,CAACb,EAAM,KAAKc,CAAM,GAAG;AACjI,iBAAWP,KAAS,MAAMF,EAAK,KAAKA,EAAK,QAAQ,EAAE,QAAAR,EAAA,CAAQ,GAAG;AAC5D,cAAMkB,MAAOjB,IAAAS,EAAM,SAASP,EAAM,GAAG,MAAxB,gBAAAF,EAA2B,cAAa,CAAA,GAC/CkB,MAAQjB,IAAAQ,EAAM,SAASO,CAAM,MAArB,gBAAAf,EAAwB,cAAa,CAAA;AACnD,iBAASkB,IAAI,GAAGA,IAAIV,EAAM,SAASU,OAAW,IAAI,OAAOF,EAAKE,CAAC,CAAC,GAAG,OAAOD,EAAMC,CAAC,KAAK,EAAE,CAAC;AAAA,MAC3F;AAAA,IACF,CAAC;AAAA,EAAA,GAEIL;AACT;"}
1
+ {"version":3,"file":"detail.js","sources":["../../src/core/detail.ts"],"sourcesContent":["import type { Corpus, VertexTable } from \"@fossil-lang/corpus\";\nimport { together, type Geometry } from \"./load\";\nimport type { VertexId } from \"./types\";\n\n/** One vertex's row, as its table's properties order it — every column but the key and the position. */\nexport interface VertexDetail {\n readonly vertex: VertexId;\n /** The vertex table it belongs to. */\n readonly table: string;\n readonly fields: readonly { readonly name: string; readonly value: unknown }[];\n}\n\nconst tableOf = (geometry: Geometry, vertex: VertexId): VertexTable | undefined =>\n vertex < geometry.size ? geometry.tables[geometry.table[vertex] as number] : undefined;\n\n/**\n * **Detail is fetched, not carried.** The loaded graph holds positions and channels; the rest of a\n * row is asked for when a reader focuses the vertex — a scan of its table, filtered to its key.\n */\nexport async function readVertex(\n corpus: Corpus,\n geometry: Geometry,\n vertex: VertexId,\n signal?: AbortSignal,\n): Promise<VertexDetail | null> {\n const table = tableOf(geometry, vertex);\n if (!table) return null;\n const hidden = new Set([table.key, table.position?.x, table.position?.y]);\n const names = table.properties.map((p) => p.name).filter((name) => !hidden.has(name));\n const scan = corpus.scan({ table: table.name, filter: { column: table.key, op: \"=\", value: vertex }, select: [table.key, ...names], limit: 1 });\n const rows = (await scan.read(scan.plan(), { signal })).find((batch) => batch.numRows > 0);\n if (!rows) return null;\n return {\n vertex,\n table: table.name,\n fields: names.map((name) => ({ name, value: rows.getChild(name)?.toArray()[0] ?? null })),\n };\n}\n\n/** The column a vertex's text is read from: `title` where its table has it, else the table's `identity`. */\nconst titleColumn = (table: VertexTable, title: string | undefined): string =>\n title !== undefined && table.properties.some((p) => p.name === title) ? title : table.identity;\n\n/**\n * **A label's text, for the few vertices that carry one** — `title` where the vertex's table has the\n * column, and the table's declared `identity` where it does not. One scan per table they fall in.\n */\nexport async function readTitles(\n corpus: Corpus,\n geometry: Geometry,\n vertices: readonly VertexId[],\n title: string | undefined,\n signal: AbortSignal,\n): Promise<Map<VertexId, string>> {\n const byTable = new Map<VertexTable, VertexId[]>();\n for (const vertex of vertices) {\n const table = tableOf(geometry, vertex);\n if (table) byTable.set(table, [...(byTable.get(table) ?? []), vertex]);\n }\n const found = new Map<VertexId, string>();\n await together(signal, (signal) =>\n Promise.all(\n [...byTable].map(async ([table, ids]) => {\n const column = titleColumn(table, title);\n const scan = corpus.scan({ table: table.name, filter: { column: table.key, op: \"in\", values: ids }, select: [table.key, column] });\n for (const batch of await scan.read(scan.plan(), { signal })) {\n const keys = batch.getChild(table.key)?.toArray() ?? [];\n const texts = batch.getChild(column)?.toArray() ?? [];\n for (let i = 0; i < batch.numRows; i++) found.set(Number(keys[i]), String(texts[i] ?? \"\"));\n }\n }),\n ),\n );\n return found;\n}\n\n/** **Every drawn vertex's text, by id** — what a search filters. One scan per drawn table, two columns. */\nexport async function readEveryTitle(\n corpus: Corpus,\n geometry: Geometry,\n title: string | undefined,\n signal: AbortSignal,\n): Promise<string[]> {\n const titles = new Array<string>(geometry.size).fill(\"\");\n await together(signal, (signal) =>\n Promise.all(\n geometry.tables.map(async (table) => {\n const column = titleColumn(table, title);\n const scan = corpus.scan({ table: table.name, select: [table.key, column] });\n for (const batch of await scan.read(scan.plan(), { signal })) {\n const keys = batch.getChild(table.key)?.toArray() ?? [];\n const texts = batch.getChild(column)?.toArray() ?? [];\n for (let i = 0; i < batch.numRows; i++) {\n const id = Number(keys[i]);\n if (id < geometry.size) titles[id] = String(texts[i] ?? \"\");\n }\n }\n }),\n ),\n );\n return titles;\n}\n"],"names":["tableOf","geometry","vertex","readVertex","corpus","signal","table","hidden","_a","_b","names","p","name","scan","rows","batch","titleColumn","title","readTitles","vertices","byTable","found","together","ids","column","keys","texts","i","readEveryTitle","titles","id"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The failures the graph names itself: no GPU device to draw with, or the one it had was taken back;
3
+ * a corpus with no positioned vertex type to draw; a crossfilter clause it cannot ask the corpus. A
4
+ * corpus fossil will not read never gets here: `open` refuses it, coded. `data.after` is set when a deadline fired. Everything else reaches `onFailure` as thrown.
5
+ */
6
+ export declare class GraphError extends Error {
7
+ readonly code: "graph/no-webgl" | "graph/context-lost" | "graph/nothing-to-draw" | "graph/untranslatable-filter";
8
+ readonly data: {
9
+ readonly after?: number;
10
+ };
11
+ readonly name = "GraphError";
12
+ constructor(code: "graph/no-webgl" | "graph/context-lost" | "graph/nothing-to-draw" | "graph/untranslatable-filter", message: string, data?: {
13
+ readonly after?: number;
14
+ }, options?: ErrorOptions);
15
+ }
16
+ //# sourceMappingURL=error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/core/error.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,UAAW,SAAQ,KAAK;IAGjC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,GAAG,uBAAuB,GAAG,6BAA6B;IAEhH,QAAQ,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAJ5C,SAAkB,IAAI,gBAAgB;gBAE3B,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,GAAG,uBAAuB,GAAG,6BAA6B,EAChH,OAAO,EAAE,MAAM,EACN,IAAI,GAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,EAC/C,OAAO,CAAC,EAAE,YAAY;CAIzB"}
@@ -0,0 +1,14 @@
1
+ var h = Object.defineProperty;
2
+ var p = (o, r, s) => r in o ? h(o, r, { enumerable: !0, configurable: !0, writable: !0, value: s }) : o[r] = s;
3
+ var t = (o, r, s) => p(o, typeof r != "symbol" ? r + "" : r, s);
4
+ class n extends Error {
5
+ constructor(s, a, e = {}, c) {
6
+ super(a, c);
7
+ t(this, "name", "GraphError");
8
+ this.code = s, this.data = e;
9
+ }
10
+ }
11
+ export {
12
+ n as GraphError
13
+ };
14
+ //# sourceMappingURL=error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.js","sources":["../../src/core/error.ts"],"sourcesContent":["/**\n * The failures the graph names itself: no GPU device to draw with, or the one it had was taken back;\n * a corpus with no positioned vertex type to draw; a crossfilter clause it cannot ask the corpus. A\n * corpus fossil will not read never gets here: `open` refuses it, coded. `data.after` is set when a deadline fired. Everything else reaches `onFailure` as thrown.\n */\nexport class GraphError extends Error {\n override readonly name = \"GraphError\";\n constructor(\n readonly code: \"graph/no-webgl\" | \"graph/context-lost\" | \"graph/nothing-to-draw\" | \"graph/untranslatable-filter\",\n message: string,\n readonly data: { readonly after?: number } = {},\n options?: ErrorOptions,\n ) {\n super(message, options);\n }\n}\n"],"names":["GraphError","code","message","data","options","__publicField"],"mappings":";;;AAKO,MAAMA,UAAmB,MAAM;AAAA,EAEpC,YACWC,GACTC,GACSC,IAAoC,CAAA,GAC7CC,GACA;AACA,UAAMF,GAASE,CAAO;AAPN,IAAAC,EAAA,cAAO;AAEd,SAAA,OAAAJ,GAEA,KAAA,OAAAE;AAAA,EAIX;AACF;"}
@@ -1,18 +1,5 @@
1
1
  import { Filter } from '@fossil-lang/corpus';
2
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
3
  /** One mosaic-sql expression node → one `Filter`. */
17
4
  export declare function translate(node: unknown): Filter;
18
5
  /** Every column a filter names, once each. */
@@ -1 +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,8CAA8C;AAC9C,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAMlD;AAED;;;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,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GAC5B,IAAI,CAON"}
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;AAiEhF,qDAAqD;AACrD,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAyC/C;AAID,8CAA8C;AAC9C,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAMlD;AAED;;;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,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GAC5B,IAAI,CAON"}
@@ -1,14 +1,6 @@
1
- var m = Object.defineProperty;
2
- var O = (n, t, r) => t in n ? m(n, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : n[t] = r;
3
- var f = (n, t, r) => O(n, typeof t != "symbol" ? t + "" : t, r);
4
- import { MosaicClient as h, clausePoints as y } from "@kanzo-tech/mosaic";
5
- class A extends Error {
6
- constructor() {
7
- super(...arguments);
8
- f(this, "name", "UntranslatableFilter");
9
- }
10
- }
11
- const N = {
1
+ import { MosaicClient as f, clausePoints as m } from "@kanzo-tech/mosaic";
2
+ import { GraphError as h } from "./error.js";
3
+ const O = {
12
4
  "=": "=",
13
5
  "==": "=",
14
6
  "IS NOT DISTINCT FROM": "=",
@@ -18,86 +10,86 @@ const N = {
18
10
  "<=": "<=",
19
11
  ">": ">",
20
12
  ">=": ">="
21
- }, g = { "=": "=", "!=": "!=", "<": ">", "<=": ">=", ">": "<", ">=": "<=" }, o = (n) => {
22
- throw new A(
23
- `the graph cannot express this crossfilter clause as a filter on the corpus: ${String(n)}`
13
+ }, y = { "=": "=", "!=": "!=", "<": ">", "<=": ">=", ">": "<", ">=": "<=" }, o = (r) => {
14
+ throw new h(
15
+ "graph/untranslatable-filter",
16
+ `the graph cannot express this crossfilter clause as a filter on the corpus: ${String(r)}`
24
17
  );
25
18
  };
26
- function s(n) {
27
- const t = n;
19
+ function a(r) {
20
+ const t = r;
28
21
  return (t == null ? void 0 : t.type) === "COLUMN_REF" && typeof t.name == "string" ? t.name : null;
29
22
  }
30
- function u(n) {
31
- const t = n, r = (t == null ? void 0 : t.type) === "LITERAL" || (t == null ? void 0 : t.type) === "PARAM" ? t.value : o(n);
32
- if (typeof r == "number" || typeof r == "bigint" || typeof r == "string" || typeof r == "boolean")
33
- return r;
34
- if (r instanceof Date) {
35
- const e = r.toISOString();
23
+ function s(r) {
24
+ const t = r, n = (t == null ? void 0 : t.type) === "LITERAL" || (t == null ? void 0 : t.type) === "PARAM" ? t.value : o(r);
25
+ if (typeof n == "number" || typeof n == "bigint" || typeof n == "string" || typeof n == "boolean")
26
+ return n;
27
+ if (n instanceof Date) {
28
+ const e = n.toISOString();
36
29
  return e.endsWith("T00:00:00.000Z") ? e.slice(0, 10) : e.replace("T", " ").replace("Z", "");
37
30
  }
38
- return o(n);
31
+ return o(r);
39
32
  }
40
- function v(n) {
41
- const t = n;
42
- return (Array.isArray(n) ? n : Array.isArray(t == null ? void 0 : t.values) ? t.values : o(n)).map(u);
33
+ function A(r) {
34
+ const t = r;
35
+ return (Array.isArray(r) ? r : Array.isArray(t == null ? void 0 : t.values) ? t.values : o(r)).map(s);
43
36
  }
44
- function c(n) {
45
- const t = n;
46
- if (Array.isArray(n)) return i(n.map(c));
37
+ function c(r) {
38
+ const t = r;
39
+ if (Array.isArray(r)) return i(r.map(c));
47
40
  switch (t == null ? void 0 : t.type) {
48
41
  case "LOGICAL_OPERATOR": {
49
- const r = (t.clauses ?? []).map(c);
50
- return t.op === "OR" ? { or: r } : i(r);
42
+ const n = (t.clauses ?? []).map(c);
43
+ return t.op === "OR" ? { or: n } : i(n);
51
44
  }
52
45
  case "UNARY":
53
- return t.op === "NOT" ? { not: c(t.expr) } : o(n);
46
+ return t.op === "NOT" ? { not: c(t.expr) } : o(r);
54
47
  case "UNARY_POSTFIX": {
55
- const r = s(t.expr) ?? o(n);
56
- return t.op === "IS NULL" ? { column: r, op: "is null" } : t.op === "IS NOT NULL" ? { column: r, op: "not null" } : o(n);
48
+ const n = a(t.expr) ?? o(r);
49
+ return t.op === "IS NULL" ? { column: n, op: "is null" } : t.op === "IS NOT NULL" ? { column: n, op: "not null" } : o(r);
57
50
  }
58
51
  case "BINARY": {
59
- const r = N[String(t.op).toUpperCase()] ?? o(n), e = s(t.left);
60
- return e !== null ? { column: e, op: r, value: u(t.right) } : { column: s(t.right) ?? o(n), op: g[r], value: u(t.left) };
52
+ const n = O[String(t.op).toUpperCase()] ?? o(r), e = a(t.left);
53
+ return e !== null ? { column: e, op: n, value: s(t.right) } : { column: a(t.right) ?? o(r), op: y[n], value: s(t.left) };
61
54
  }
62
55
  case "BETWEEN":
63
56
  case "NOT_BETWEEN": {
64
- const r = s(t.expr) ?? o(n), [e, a] = t.extent ?? o(n), p = i([
65
- { column: r, op: ">=", value: u(e) },
66
- { column: r, op: "<=", value: u(a) }
57
+ const n = a(t.expr) ?? o(r), [e, u] = t.extent ?? o(r), p = i([
58
+ { column: n, op: ">=", value: s(e) },
59
+ { column: n, op: "<=", value: s(u) }
67
60
  ]);
68
61
  return t.type === "BETWEEN" ? p : { not: p };
69
62
  }
70
63
  case "IN":
71
- return { column: s(t.expr) ?? o(n), op: "in", values: v(t.values) };
64
+ return { column: a(t.expr) ?? o(r), op: "in", values: A(t.values) };
72
65
  default:
73
- return o(n);
66
+ return o(r);
74
67
  }
75
68
  }
76
- const i = (n) => n.length === 1 ? n[0] : { and: n };
77
- function l(n) {
78
- return "column" in n ? [n.column] : "and" in n ? [...new Set(n.and.flatMap(l))] : "or" in n ? [...new Set(n.or.flatMap(l))] : "not" in n ? l(n.not) : [];
69
+ const i = (r) => r.length === 1 ? r[0] : { and: r };
70
+ function l(r) {
71
+ return "column" in r ? [r.column] : "and" in r ? [...new Set(r.and.flatMap(l))] : "or" in r ? [...new Set(r.or.flatMap(l))] : "not" in r ? l(r.not) : [];
79
72
  }
80
- function S(n, t) {
81
- const r = n.predicate(t), e = (Array.isArray(r) ? r : [r]).filter((a) => a != null);
73
+ function v(r, t) {
74
+ const n = r.predicate(t), e = (Array.isArray(n) ? n : [n]).filter((u) => u != null);
82
75
  return e.length === 0 ? void 0 : c(e);
83
76
  }
84
- function T() {
85
- return new h();
77
+ function E() {
78
+ return new f();
86
79
  }
87
- function I(n, t, r, e) {
88
- n.update(
89
- y([r], e && e.length > 0 ? e.map((a) => [a]) : void 0, {
80
+ function R(r, t, n, e) {
81
+ r.update(
82
+ m([n], e && e.length > 0 ? e.map((u) => [u]) : void 0, {
90
83
  source: t,
91
84
  clients: /* @__PURE__ */ new Set([t])
92
85
  })
93
86
  );
94
87
  }
95
88
  export {
96
- A as UntranslatableFilter,
97
89
  l as columnsOf,
98
- S as filterFor,
99
- T as graphClient,
100
- I as publish,
90
+ v as filterFor,
91
+ E as graphClient,
92
+ R as publish,
101
93
  c as translate
102
94
  };
103
95
  //# sourceMappingURL=filter.js.map
@@ -1 +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/** Every column a filter names, once each. */\nexport function columnsOf(filter: Filter): string[] {\n if (\"column\" in filter) return [filter.column];\n if (\"and\" in filter) return [...new Set(filter.and.flatMap(columnsOf))];\n if (\"or\" in filter) return [...new Set(filter.or.flatMap(columnsOf))];\n if (\"not\" in filter) return columnsOf(filter.not);\n return [];\n}\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 ids: readonly number[] | null,\n): void {\n selection.update(\n clausePoints([column], ids && ids.length > 0 ? ids.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","columnsOf","filter","filterFor","selection","self","predicate","p","graphClient","MosaicClient","publish","ids","clausePoints","d"],"mappings":"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"}
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\";\nimport { GraphError } from \"./error\";\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\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\n/** A predicate this cannot express as a `Filter` — reported, never dropped. */\nconst fail = (what: unknown): never => {\n throw new GraphError(\n \"graph/untranslatable-filter\",\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/** Every column a filter names, once each. */\nexport function columnsOf(filter: Filter): string[] {\n if (\"column\" in filter) return [filter.column];\n if (\"and\" in filter) return [...new Set(filter.and.flatMap(columnsOf))];\n if (\"or\" in filter) return [...new Set(filter.or.flatMap(columnsOf))];\n if (\"not\" in filter) return columnsOf(filter.not);\n return [];\n}\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 ids: readonly number[] | null,\n): void {\n selection.update(\n clausePoints([column], ids && ids.length > 0 ? ids.map((d) => [d]) : undefined, {\n source: self,\n clients: new Set([self]),\n }),\n );\n}\n"],"names":["COMPARISONS","MIRRORED","fail","what","GraphError","columnOf","node","n","literalOf","value","iso","valuesOf","translate","and","clauses","column","op","left","lo","hi","within","columnsOf","filter","filterFor","selection","self","predicate","p","graphClient","MosaicClient","publish","ids","clausePoints","d"],"mappings":"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"}
@@ -35,6 +35,11 @@ export interface Encoding {
35
35
  }
36
36
  /** The vertex tables fossil lays out or the program places; a table without a position is not drawn. */
37
37
  export declare const drawnTables: (corpus: Corpus) => VertexTable[];
38
+ /**
39
+ * Reads in parallel that end together: the first to fail aborts the rest, so no read outlives the
40
+ * answer and no second failure goes unheard behind the first.
41
+ */
42
+ export declare function together<T>(signal: AbortSignal, reads: (signal: AbortSignal) => Promise<T>): Promise<T>;
38
43
  /** One scan per drawn table and one per relation between them, read whole, in parallel. */
39
44
  export declare function loadGraph(corpus: Corpus, binding: Binding, seed: readonly unknown[], signal: AbortSignal): Promise<{
40
45
  geometry: Geometry;
@@ -1 +1 @@
1
- {"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../src/core/load.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,KAAK,EAAS,MAAM,EAAa,MAAM,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGzF;;;;GAIG;AAEH,gFAAgF;AAChF,MAAM,WAAW,QAAQ;IACvB,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;IACxC,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,6CAA6C;IAC7C,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,sEAAsE;IACtE,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAC5G;AAED,yFAAyF;AACzF,MAAM,WAAW,QAAQ;IACvB,kCAAkC;IAClC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,kEAAkE;IAClE,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;CACrC;AAED,wGAAwG;AACxG,eAAO,MAAM,WAAW,GAAI,QAAQ,MAAM,KAAG,WAAW,EAAqE,CAAC;AAgF9H,2FAA2F;AAC3F,wBAAsB,SAAS,CAC7B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,SAAS,OAAO,EAAE,EACxB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC,CAgCrD;AAED,uGAAuG;AACvG,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,SAAS,OAAO,EAAE,EACxB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,QAAQ,CAAC,CAUnB;AAED,yFAAyF;AACzF,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;CACjC;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,WAAW,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBjI;AAED,qDAAqD;AACrD,wBAAgB,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,GAAG,UAAU,CAKjE"}
1
+ {"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../src/core/load.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,KAAK,EAAS,MAAM,EAAa,MAAM,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGzF;;;;GAIG;AAEH,gFAAgF;AAChF,MAAM,WAAW,QAAQ;IACvB,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;IACxC,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,6CAA6C;IAC7C,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,sEAAsE;IACtE,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAC5G;AAED,yFAAyF;AACzF,MAAM,WAAW,QAAQ;IACvB,kCAAkC;IAClC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,kEAAkE;IAClE,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;CACrC;AAED,wGAAwG;AACxG,eAAO,MAAM,WAAW,GAAI,QAAQ,MAAM,KAAG,WAAW,EAAqE,CAAC;AAI9H;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAMvG;AA8ED,2FAA2F;AAC3F,wBAAsB,SAAS,CAC7B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,SAAS,OAAO,EAAE,EACxB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC,CAkCrD;AAED,uGAAuG;AACvG,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,SAAS,OAAO,EAAE,EACxB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,QAAQ,CAAC,CAUnB;AAED,yFAAyF;AACzF,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;CACjC;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,WAAW,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBjI;AAED,qDAAqD;AACrD,wBAAgB,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,GAAG,UAAU,CAKjE"}