@kanzo-tech/graph 0.2.0 → 0.3.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.
@@ -1,15 +1,17 @@
1
1
  "use client";
2
- import { numbers as k, clausePoints as $t, fillColumn as M, column as pt } from "@kanzo-tech/mosaic";
3
- import { SliceRead as W } from "./slice-client.js";
4
- import { typeOf as xt, denseOf as tt, vertexId as At } from "./resident.js";
5
- function et(t, e) {
2
+ import { open as at, PAYLOAD_COORDINATES as k, PAYLOAD_IDENTITY as it, PAYLOAD_ADDRESS as ot } from "@fossil-lang/corpus";
3
+ import { numbers as C, clausePoints as rt, fillColumn as _, column as ct } from "@kanzo-tech/mosaic";
4
+ import { SliceRead as v } from "./slice-client.js";
5
+ import { typeOf as lt, denseOf as J, vertexId as ut } from "./resident.js";
6
+ const X = 0;
7
+ function Et(t, e) {
6
8
  return {
7
- points: new W(t, e),
8
- links: new W(t, e),
9
- meta: new W(t)
9
+ points: new v(t, e),
10
+ links: new v(t, e),
11
+ meta: new v(t)
10
12
  };
11
13
  }
12
- function nt(t) {
14
+ function dt(t) {
13
15
  let e = Promise.resolve();
14
16
  return (n) => {
15
17
  const s = e.then(() => t.ask(() => n));
@@ -17,97 +19,49 @@ function nt(t) {
17
19
  }), s;
18
20
  };
19
21
  }
20
- function Q(t) {
22
+ function Y(t) {
21
23
  if (t == null) return "";
22
24
  const n = (Array.isArray(t) ? t : [t]).filter((s) => s != null).map((s) => String(s));
23
25
  return n.length > 0 ? n.map((s) => `(${s})`).join(" AND ") : "";
24
26
  }
25
- function st(t, e) {
27
+ function Z(t, e) {
26
28
  return t ? e ? `(${t}) AND (${e})` : t : e || "TRUE";
27
29
  }
28
- function wt(t) {
29
- const { coordinator: e, edges: n, filterBy: s, nodes: r, typeIndex: i } = t, c = {
30
- id: t.idField ?? "id",
31
- subject: t.subjectField,
32
- x: t.xField ?? "x",
33
- y: t.yField ?? "y",
34
- source: t.sourceField ?? "source",
35
- target: t.targetField ?? "target"
36
- }, l = et(e, s), E = nt(l.meta), y = it(l);
37
- return {
38
- ...y.api,
39
- publish(p) {
40
- ot(l, s, c.id, p);
41
- },
42
- async total() {
43
- const p = await E(`SELECT count(*) AS n FROM ${r}`);
44
- return Number(k(p, "n")[0] ?? 0);
45
- },
46
- /**
47
- * Four aggregates, so the canvas can frame what is actually there.
48
- *
49
- * One scan of two columns, once — against a first paint that otherwise opens on the renderer's
50
- * default box and finds the corpus occupying a corner of it.
51
- */
52
- async extent() {
53
- const p = await E(
54
- `SELECT min(${c.x}) AS x0, max(${c.x}) AS x1,
55
- min(${c.y}) AS y0, max(${c.y}) AS y1
56
- FROM ${r}`
57
- ), x = (f) => Number(k(p, f)[0] ?? 0);
58
- return { xMin: x("x0"), yMin: x("y0"), xMax: x("x1"), yMax: x("y1") };
59
- },
60
- /**
61
- * Regions only, and it says so by **not having** `explore`.
62
- *
63
- * This source is two relations and a spatial predicate — it has no adjacency index, so a
64
- * neighbourhood query would mean recursive joins over the whole edge table, which is the
65
- * unbounded pattern wearing a bounded interface. It used to say that with a predicate and a
66
- * throw; now the absence is the statement, and asking is a compile error.
67
- */
68
- async slice(p) {
69
- const { fill: x, limit: f, minLinkPixels: N, perPixel: $, pinned: O, r: g, view: F } = p, A = { ...c, category: x, size: g };
70
- return y.run(
71
- rt(r, n, A, F, f, i, O, $, N, void 0)
72
- );
73
- }
74
- };
75
- }
76
- function St(t, e) {
30
+ function mt(t, e) {
77
31
  const s = [
78
32
  [t.x, e.xMin, ">="],
79
33
  [t.x, e.xMax, "<="],
80
34
  [t.y, e.yMin, ">="],
81
35
  [t.y, e.yMax, "<="]
82
- ].filter(([, r]) => Number.isFinite(r)).map(([r, i, c]) => `${r} ${c} ${i}`);
36
+ ].filter(([, o]) => Number.isFinite(o)).map(([o, r, l]) => `${o} ${l} ${r}`);
83
37
  return s.length > 0 ? s.join(" AND ") : "TRUE";
84
38
  }
85
- const ht = (t) => `greatest(1, CAST(ceil(matched / ${t}.0) AS BIGINT))`;
86
- function q(t, e, n, s, r = !0) {
87
- const i = e.size ? `, ${e.size} AS size` : "", c = e.subject ? `, ${e.subject} AS subject` : "", l = e.category ? "(dense_rank() OVER (ORDER BY cat) - 1)::INTEGER AS category" : "0::INTEGER AS category";
39
+ const At = (t) => `greatest(1, CAST(ceil(matched / ${t}.0) AS BIGINT))`;
40
+ function q(t, e, n, s, o = !0) {
41
+ const r = e.size ? `, ${e.size} AS size` : "", l = e.subject ? `, ${e.subject} AS subject` : "", m = e.category ? "(dense_rank() OVER (ORDER BY cat) - 1)::INTEGER AS category" : "0::INTEGER AS category";
88
42
  return `WITH pool AS (
89
- SELECT ${e.id} AS id, ${e.x} AS x, ${e.y} AS y${i}${c}${e.category ? `, ${e.category} AS cat` : ""},
43
+ SELECT ${e.id} AS id, ${e.x} AS x, ${e.y} AS y${r}${l}${e.category ? `, ${e.category} AS cat` : ""},
90
44
  count(*) OVER () AS matched
91
45
  FROM ${t}
92
46
  WHERE ${n}
93
47
  ), vis AS (
94
- SELECT id, x, y${e.size ? ", size" : ""}${e.subject ? ", subject" : ""}${r ? ", matched" : ""},
95
- ${l},
48
+ SELECT id, x, y${e.size ? ", size" : ""}${e.subject ? ", subject" : ""}${o ? ", matched" : ""},
49
+ ${m},
96
50
  (row_number() OVER (ORDER BY id) - 1)::INTEGER AS local
97
51
  FROM pool
98
- WHERE id % ${ht(s)} = 0
52
+ WHERE id % ${At(s)} = 0
99
53
  LIMIT ${s}
100
54
  )`;
101
55
  }
102
- function at(t, e, n, s) {
56
+ function yt(t, e, n, s) {
103
57
  if (n === void 0 || !Number.isFinite(n) || n <= 0) return "";
104
- const r = s * n;
105
- return `(${t}.x - ${e}.x) * (${t}.x - ${e}.x) + (${t}.y - ${e}.y) * (${t}.y - ${e}.y) >= ${r * r}`;
58
+ const o = s * n;
59
+ return `(${t}.x - ${e}.x) * (${t}.x - ${e}.x) + (${t}.y - ${e}.y) * (${t}.y - ${e}.y) >= ${o * o}`;
106
60
  }
107
- function ft(t, e, n, s, r, i, c) {
108
- const l = st(`NOT (${s})`, r), E = at("a", "b", i, c);
61
+ function pt(t, e, n, s, o, r, l) {
62
+ const m = Z(`NOT (${s})`, o), E = yt("a", "b", r, l);
109
63
  return `, out AS (
110
- SELECT ${n.id} AS id, ${n.x} AS x, ${n.y} AS y FROM ${t} WHERE ${l}
64
+ SELECT ${n.id} AS id, ${n.x} AS x, ${n.y} AS y FROM ${t} WHERE ${m}
111
65
  ), reach AS (
112
66
  SELECT id, x, y FROM vis UNION ALL SELECT id, x, y FROM out
113
67
  ), span AS (
@@ -126,8 +80,8 @@ function ft(t, e, n, s, r, i, c) {
126
80
  UNION SELECT dst_id FROM span WHERE dst IS NULL)
127
81
  )`;
128
82
  }
129
- function rt(t, e, n, s, r, i, c, l, E, y) {
130
- const p = St(n, s), x = (c ?? []).filter((u) => xt(u) === i).map(tt), f = x.length > 0 ? ` OR ${n.id} IN (${x.join(",")})` : "", N = `(${p})${f}`, $ = (u) => st(N, Q(u)), O = n.size ? ", size" : "", g = n.subject ? ", subject" : "", F = at("s", "t", l, E), A = (u) => y ? ft(y, e, n, N, Q(u), l, E) : "";
83
+ function $t(t, e, n, s, o, r, l, m) {
84
+ const E = mt(n, s), h = (r ?? []).filter((u) => lt(u) === X).map(J), b = h.length > 0 ? ` OR ${n.id} IN (${h.join(",")})` : "", R = `(${E})${b}`, y = (u) => Z(R, Y(u)), A = n.size ? ", size" : "", x = n.subject ? ", subject" : "", f = (u) => pt(t, e, n, R, Y(u), l, m);
131
85
  return {
132
86
  /**
133
87
  * The marks, then the anchors, in one answer — because they are one buffer.
@@ -136,30 +90,26 @@ function rt(t, e, n, s, r, i, c, l, E, y) {
136
90
  * and continues past it over the anchors, so ordering by it puts every mark before every anchor
137
91
  * and makes `marks` a prefix length. `matched` is then still read off row zero.
138
92
  */
139
- points: (u) => `${q(t, n, $(u), r)}${A(u)}
140
- SELECT local, id, x, y, category, matched, TRUE AS mark${O}${g} FROM vis${y ? `
93
+ points: (u) => `${q(t, n, y(u), o)}${f(u)}
94
+ SELECT local, id, x, y, category, matched, TRUE AS mark${A}${x} FROM vis
141
95
  UNION ALL
142
- SELECT local, id, x, y, 0::INTEGER, NULL::BIGINT, FALSE AS mark${n.size ? ", CAST(NULL AS DOUBLE)" : ""}${n.subject ? ", CAST(NULL AS VARCHAR)" : ""} FROM anchor` : ""}
96
+ SELECT local, id, x, y, 0::INTEGER, NULL::BIGINT, FALSE AS mark${n.size ? ", CAST(NULL AS DOUBLE)" : ""}${n.subject ? ", CAST(NULL AS VARCHAR)" : ""} FROM anchor
143
97
  ORDER BY local`,
144
98
  /**
145
- * One end drawn and both ends positioned — or, where nothing is held, both ends drawn.
99
+ * One end drawn and both ends positioned.
146
100
  *
147
- * The second form is what a source over an ordinary relation gets, and it is the old query plus
148
- * the length predicate. It still drops an edge that leaves the window, and the reason is now
149
- * exact rather than a limitation of the reader: no bytes beyond the rectangle were fetched, so
150
- * there is no position to draw the far end at. A corpus fetches tiles and therefore has some.
101
+ * There was a second form here — both ends drawn, edges leaving the window dropped — for a source
102
+ * that fetched no bytes beyond the rectangle and therefore had no position to put a far end at.
103
+ * It went with that source, and `longEnough` with it — the length predicate lives in `anchorCte`
104
+ * now, on the same span, which is where it has to sit once the join runs over the held rows
105
+ * rather than over the visible ones.
151
106
  */
152
- links: (u) => y ? `${q(t, n, $(u), r, !1)}${A(u)}
107
+ links: (u) => `${q(t, n, y(u), o, !1)}${f(u)}
153
108
  SELECT coalesce(sp.src, sa.local) AS src, coalesce(sp.dst, da.local) AS dst
154
109
  FROM span sp
155
110
  LEFT JOIN anchor sa ON sa.id = sp.src_id
156
- LEFT JOIN anchor da ON da.id = sp.dst_id` : `${q(t, n, $(u), r, !1)}
157
- SELECT s.local AS src, t.local AS dst
158
- FROM ${e} e
159
- JOIN vis s ON e.${n.source} = s.id
160
- JOIN vis t ON e.${n.target} = t.id${F ? `
161
- WHERE ${F}` : ""}`,
162
- assemble: (u, D) => ({
111
+ LEFT JOIN anchor da ON da.id = sp.dst_id`,
112
+ assemble: (u, p) => ({
163
113
  /**
164
114
  * How many matched, separately from how many came back.
165
115
  *
@@ -168,17 +118,17 @@ function rt(t, e, n, s, r, i, c, l, E, y) {
168
118
  * has been about. Read off the first row rather than asked for: `matched` is constant down the
169
119
  * column, and an empty answer has no row and no matches, which agree.
170
120
  */
171
- n: Number(k(u, "matched")[0] ?? 0),
172
- ...Nt(u, D, i, n.size ? "size" : void 0, n.subject !== void 0)
121
+ n: Number(C(u, "matched")[0] ?? 0),
122
+ ...Rt(u, p, n.size ? "size" : void 0, n.subject !== void 0)
173
123
  })
174
124
  };
175
125
  }
176
- function it(t) {
126
+ function St(t) {
177
127
  let e = null, n = null;
178
- const s = /* @__PURE__ */ new Map(), r = (i) => (c) => {
179
- if (!e || !n || (s.set(i, c), s.size < 2)) return;
180
- const l = s.get("points"), E = s.get("links");
181
- s.clear(), n(e.assemble(l, E));
128
+ const s = /* @__PURE__ */ new Map(), o = (r) => (l) => {
129
+ if (!e || !n || (s.set(r, l), s.size < 2)) return;
130
+ const m = s.get("points"), E = s.get("links");
131
+ s.clear(), n(e.assemble(m, E));
182
132
  };
183
133
  return {
184
134
  api: {
@@ -189,202 +139,182 @@ function it(t) {
189
139
  * has *already* re-run both reads with the new predicate by the time we hear about it. Asking
190
140
  * again would run the same two queries a second time to learn what is in hand.
191
141
  */
192
- watch(i) {
193
- return n = i, t.points.onAnswer = r("points"), t.links.onAnswer = r("links"), () => {
142
+ watch(r) {
143
+ return n = r, t.points.onAnswer = o("points"), t.links.onAnswer = o("links"), () => {
194
144
  n = null, s.clear(), e = null, t.points.release(), t.links.release(), t.meta.release();
195
145
  };
196
146
  }
197
147
  },
198
- async run(i) {
199
- e = i, s.clear();
200
- const [c, l] = await Promise.all([
201
- t.points.ask(i.points),
202
- t.links.ask(i.links)
148
+ async run(r) {
149
+ e = r, s.clear();
150
+ const [l, m] = await Promise.all([
151
+ t.points.ask(r.points),
152
+ t.links.ask(r.links)
203
153
  ]);
204
- return i.assemble(c, l);
154
+ return r.assemble(l, m);
205
155
  }
206
156
  };
207
157
  }
208
- function ot(t, e, n, s) {
158
+ function Nt(t, e, n, s) {
209
159
  e && e.update(
210
- $t([n], s == null ? void 0 : s.map((r) => [tt(r)]), {
160
+ rt([n], s == null ? void 0 : s.map((o) => [J(o)]), {
211
161
  source: t.points,
212
162
  clients: /* @__PURE__ */ new Set([t.points, t.links])
213
163
  })
214
164
  );
215
165
  }
216
- function Nt(t, e, n, s, r = !1) {
217
- const i = X(t, "x"), c = new Float32Array(i * 2), l = M(t, "x", c, 0, 2);
218
- M(t, "y", c, 1, 2);
219
- const E = new Int32Array(i);
220
- M(t, "mark", E);
221
- let y = 0;
222
- for (; y < l && E[y] !== 0; ) y++;
223
- const p = new Float64Array(l);
224
- M(t, "id", p);
225
- const x = new BigUint64Array(l);
226
- for (let A = 0; A < l; A++) x[A] = At(n, p[A]);
227
- const f = new Uint16Array(l);
228
- M(t, "category", f);
229
- const N = r ? pt(t, "subject") : void 0;
230
- let $;
231
- s && ($ = new Float32Array(l), M(t, s, $));
232
- const O = X(e, "src"), g = new Float32Array(O * 2), F = M(e, "src", g, 0, 2);
233
- return M(e, "dst", g, 1, 2), {
234
- marks: y,
235
- vertices: x,
236
- subjects: N,
237
- positions: c.subarray(0, l * 2),
238
- links: g.subarray(0, F * 2),
239
- categories: f,
240
- sizes: $
166
+ function Rt(t, e, n, s = !1) {
167
+ const o = V(t, "x"), r = new Float32Array(o * 2), l = _(t, "x", r, 0, 2);
168
+ _(t, "y", r, 1, 2);
169
+ const m = new Int32Array(o);
170
+ _(t, "mark", m);
171
+ let E = 0;
172
+ for (; E < l && m[E] !== 0; ) E++;
173
+ const h = new Float64Array(l);
174
+ _(t, "id", h);
175
+ const b = new BigUint64Array(l);
176
+ for (let p = 0; p < l; p++) b[p] = ut(X, h[p]);
177
+ const R = new Uint16Array(l);
178
+ _(t, "category", R);
179
+ const y = s ? ct(t, "subject") : void 0;
180
+ let A;
181
+ n && (A = new Float32Array(l), _(t, n, A));
182
+ const x = V(e, "src"), f = new Float32Array(x * 2), u = _(e, "src", f, 0, 2);
183
+ return _(e, "dst", f, 1, 2), {
184
+ marks: E,
185
+ vertices: b,
186
+ subjects: y,
187
+ positions: r.subarray(0, l * 2),
188
+ links: f.subarray(0, u * 2),
189
+ categories: R,
190
+ sizes: A
241
191
  };
242
192
  }
243
- function X(t, e) {
244
- var r;
193
+ function V(t, e) {
194
+ var o;
245
195
  const n = t == null ? void 0 : t.numRows;
246
196
  if (typeof n == "number") return n;
247
- const s = (r = t == null ? void 0 : t.getChild) == null ? void 0 : r.call(t, e);
197
+ const s = (o = t == null ? void 0 : t.getChild) == null ? void 0 : o.call(t, e);
248
198
  return s ? s.length : Array.from(t).length;
249
199
  }
250
- function I(t, e) {
251
- const n = t.split(`
252
- `).find((s) => s.startsWith(`${e}:`));
253
- return n == null ? void 0 : n.slice(e.length + 1).trim().replace(/^['"]|['"]$/g, "");
254
- }
255
- function Z(t, e) {
256
- const n = t.split(`
257
- `), s = n.findIndex((i) => i.startsWith(`${e}:`));
258
- if (s < 0) return [];
259
- const r = [];
260
- for (const i of n.slice(s + 1)) {
261
- if (!i.startsWith("- ")) break;
262
- r.push(i.slice(2).trim());
263
- }
264
- return r;
265
- }
266
- async function P(t, e) {
267
- const n = await fetch(`${t}/${e}`);
268
- if (!n.ok) throw new Error(`corpus: ${e} is not readable (${n.status})`);
269
- return n.text();
200
+ async function ft(t) {
201
+ const e = await fetch(t);
202
+ if (!e.ok) throw new Error(`corpus: ${t} is not readable (${e.status})`);
203
+ return e.text();
270
204
  }
271
- async function Tt(t) {
272
- const { coordinator: e, dest: n, filterBy: s, subjects: r = !1, vertexType: i } = t, c = et(e, s), l = nt(c.meta), E = it(c), y = await P(n, "graph.graph.yml"), p = Z(y, "vertices"), x = Z(y, "edges"), f = await Promise.all(p.map((a) => P(n, a))), N = i === void 0 ? f[0] : f.find((a) => I(a, "type") === i);
273
- if (!N)
274
- throw new Error(
275
- `corpus: no vertex type ${i ?? "(none declared)"} in ${n}/graph.graph.yml`
276
- );
277
- const $ = I(N, "type"), O = I(N, "prefix"), g = Number(I(N, "chunk_size"));
278
- if (!$ || !O || !Number.isFinite(g) || g <= 0)
279
- throw new Error(`corpus: ${n} declares no usable type, prefix and chunk_size`);
280
- const A = (await Promise.all(x.map((a) => P(n, a)))).find((a) => I(a, "src_type") === $), u = A ? I(A, "prefix") : void 0;
281
- let D = null, G;
282
- const j = (a) => `'${n}/${O}chunk${a}.parquet'`, ct = (a) => `'${n}/${u}by_source/tile${a}.parquet'`, lt = async () => {
283
- var o;
284
- const a = (o = e.databaseConnector) == null ? void 0 : o.call(e);
205
+ async function gt(t) {
206
+ var z;
207
+ const { coordinator: e, dest: n, filterBy: s, readText: o = ft, subjects: r = !1, vertexType: l, wasmUrl: m } = t, E = Et(e, s), h = dt(E.meta), b = St(E), R = await at(n, { readText: o, wasmUrl: m }), y = R.vertexType(l), A = R.incident(y.type).find((a) => a.srcType === y.type), x = (A == null ? void 0 : A.adjacency("src")) ?? null;
208
+ let f = null;
209
+ const u = async () => {
210
+ var i;
211
+ const a = (i = e.databaseConnector) == null ? void 0 : i.call(e);
285
212
  if (!(a != null && a.getDuckDB)) return null;
286
213
  try {
287
214
  return await a.getDuckDB();
288
215
  } catch {
289
216
  return null;
290
217
  }
291
- }, U = /* @__PURE__ */ new Map();
292
- let z = 0;
293
- const ut = 64 * 1024 * 1024, dt = 256 * 1024;
294
- async function V(a, o) {
295
- const d = a === "vertex" ? j : ct, C = await lt();
296
- if (!C) return o.map(d);
297
- const v = await Promise.all(
298
- o.map(async (m) => {
299
- const S = `corpus_${$}_${a}_${m}.parquet`;
300
- if (U.has(S)) return S;
301
- const b = d(m).slice(1, -1), w = await fetch(b, { method: "HEAD" }), T = Number(w.headers.get("content-length"));
302
- if (!w.ok || !Number.isFinite(T) || T > dt) return d(m);
303
- const _ = await fetch(b);
304
- if (!_.ok) return d(m);
305
- const R = new Uint8Array(await _.arrayBuffer());
306
- return await C.registerFileBuffer(S, R), U.set(S, R.byteLength), z += R.byteLength, S;
218
+ }, p = /* @__PURE__ */ new Map(), I = /* @__PURE__ */ new Map();
219
+ let F = 0;
220
+ const K = 64 * 1024 * 1024, Q = 256 * 1024;
221
+ async function j(a) {
222
+ if (a.length === 0) return [];
223
+ const i = await u();
224
+ if (!i) return a.map((c) => `'${c}'`);
225
+ const d = await Promise.all(
226
+ a.map(async (c) => {
227
+ const $ = I.get(c);
228
+ if ($ !== void 0 && ($.startsWith("'") || p.has($)))
229
+ return $;
230
+ const L = `corpus_${c.replace(/[^A-Za-z0-9]+/g, "_")}`, T = await fetch(c, { method: "HEAD" }), S = Number(T.headers.get("content-length"));
231
+ if (!T.ok || !Number.isFinite(S) || S > Q) {
232
+ const g = `'${c}'`;
233
+ return I.set(c, g), g;
234
+ }
235
+ const O = await fetch(c);
236
+ if (!O.ok) return `'${c}'`;
237
+ const N = new Uint8Array(await O.arrayBuffer());
238
+ return await i.registerFileBuffer(L, N), p.set(L, N.byteLength), I.set(c, L), F += N.byteLength, L;
307
239
  })
308
240
  );
309
- for (; z > ut && U.size > 0; ) {
310
- const [m, S] = U.entries().next().value;
311
- if (v.includes(m)) break;
312
- U.delete(m), z -= S, await C.dropFile(m);
241
+ for (; F > K && p.size > 0; ) {
242
+ const [c, $] = p.entries().next().value;
243
+ if (d.includes(c)) break;
244
+ p.delete(c), F -= $, await i.dropFile(c);
313
245
  }
314
- return v.map((m) => m.startsWith("'") ? m : `'${m}'`);
246
+ return d.map((c) => c.startsWith("'") ? c : `'${c}'`);
315
247
  }
316
- function H() {
317
- return D ?? (D = (async () => {
318
- const a = async (h) => (await fetch(`${n}/${O}chunk${h}.parquet`, { method: "HEAD" })).ok;
319
- if (!await a(0)) throw new Error(`corpus: ${n}/${O} holds no chunk0`);
320
- let o = 0, d = 1;
321
- for (; await a(d); )
322
- o = d, d *= 2;
323
- for (; d - o > 1; ) {
324
- const h = Math.floor((o + d) / 2);
325
- await a(h) ? o = h : d = h;
326
- }
327
- const C = o + 1, v = await l(
328
- `SELECT num_rows AS n FROM parquet_file_metadata('${j(o).slice(1, -1)}')`
329
- );
330
- G = o * g + Number(k(v, "n")[0] ?? 0);
331
- const m = Array.from({ length: C }, (h, L) => j(L)).join(", "), S = await l(
332
- `SELECT CAST(regexp_extract(file_name, 'chunk(\\d+)', 1) AS INTEGER) AS tile,
333
- min(CASE WHEN path_in_schema = 'x' THEN coalesce(stats_min_value, stats_min)::DOUBLE END) AS x0,
334
- max(CASE WHEN path_in_schema = 'x' THEN coalesce(stats_max_value, stats_max)::DOUBLE END) AS x1,
335
- min(CASE WHEN path_in_schema = 'y' THEN coalesce(stats_min_value, stats_min)::DOUBLE END) AS y0,
336
- max(CASE WHEN path_in_schema = 'y' THEN coalesce(stats_max_value, stats_max)::DOUBLE END) AS y1
337
- FROM parquet_metadata([${m}])
338
- WHERE path_in_schema IN ('x', 'y') GROUP BY 1 ORDER BY 1`
339
- ), b = k(S, "tile"), [w, T, _, R] = ["x0", "x1", "y0", "y1"].map((h) => k(S, h));
340
- return b.map((h, L) => ({
341
- tile: h,
342
- x0: w == null ? void 0 : w[L],
343
- x1: T == null ? void 0 : T[L],
344
- y0: _ == null ? void 0 : _[L],
345
- y1: R == null ? void 0 : R[L]
248
+ const H = y.files(), M = (a) => a.map((i) => `'${i.replace(/'/g, "''")}'`).join(", ");
249
+ function B() {
250
+ return f ?? (f = (async () => {
251
+ const a = M(H), [i, d] = k, c = R.container === "rowgroups" ? "row_group_id" : `list_position([${a}], file_name) - 1`, $ = await h(
252
+ `SELECT ${c} AS tile,
253
+ min(CASE WHEN path_in_schema = '${i}' THEN coalesce(stats_min_value, stats_min)::DOUBLE END) AS x0,
254
+ max(CASE WHEN path_in_schema = '${i}' THEN coalesce(stats_max_value, stats_max)::DOUBLE END) AS x1,
255
+ min(CASE WHEN path_in_schema = '${d}' THEN coalesce(stats_min_value, stats_min)::DOUBLE END) AS y0,
256
+ max(CASE WHEN path_in_schema = '${d}' THEN coalesce(stats_max_value, stats_max)::DOUBLE END) AS y1
257
+ FROM parquet_metadata([${a}])
258
+ WHERE path_in_schema IN ('${i}', '${d}') GROUP BY 1 ORDER BY 1`
259
+ ), L = C($, "tile"), [T, S, O, N] = ["x0", "x1", "y0", "y1"].map((g) => C($, g));
260
+ return L.map((g, w) => ({
261
+ tile: g,
262
+ x0: T == null ? void 0 : T[w],
263
+ x1: S == null ? void 0 : S[w],
264
+ y0: O == null ? void 0 : O[w],
265
+ y1: N == null ? void 0 : N[w]
346
266
  }));
347
- })()), D;
267
+ })()), f;
348
268
  }
349
- function Et(a, o) {
350
- return a.filter((d) => d.x1 >= o.xMin && d.x0 <= o.xMax && d.y1 >= o.yMin && d.y0 <= o.yMax).map((d) => d.tile);
269
+ function tt(a, i) {
270
+ return a.filter((d) => d.x1 >= i.xMin && d.x0 <= i.xMax && d.y1 >= i.yMin && d.y0 <= i.yMax).map((d) => d.tile);
351
271
  }
352
- const J = {
353
- id: "dense_id",
354
- subject: r ? "subject" : void 0,
355
- x: "x",
356
- y: "y",
357
- source: "src_dense",
358
- target: "dst_dense"
359
- }, Y = `corpus_${$}`, B = u ? `corpus_${$}_edges` : void 0, mt = async () => (await H()).map((o) => j(o.tile)).join(", ");
272
+ const W = {
273
+ id: ot[0],
274
+ subject: r ? it[0] : void 0,
275
+ x: k[0],
276
+ y: k[1],
277
+ source: (x == null ? void 0 : x.column) ?? "src_dense",
278
+ target: ((z = A == null ? void 0 : A.adjacency("dst")) == null ? void 0 : z.column) ?? "dst_dense"
279
+ }, D = `corpus_${y.type}`, U = x ? `corpus_${y.type}_edges` : void 0;
360
280
  return await e.exec(
361
- `CREATE OR REPLACE VIEW ${Y} AS SELECT * FROM read_parquet([${await mt()}])`
362
- ), B && await e.exec(
363
- `CREATE OR REPLACE VIEW ${B} AS
364
- SELECT * FROM read_parquet('${n}/${u}by_source.parquet')`
281
+ `CREATE OR REPLACE VIEW ${D} AS SELECT * FROM read_parquet([${M(H)}])`
282
+ ), U && A && await e.exec(
283
+ `CREATE OR REPLACE VIEW ${U} AS
284
+ SELECT * FROM read_parquet([${M(A.projectionFiles(1, "src"))}])`
365
285
  ), { source: {
366
- ...E.api,
286
+ ...b.api,
367
287
  publish(a) {
368
- ot(c, s, J.id, a);
288
+ Nt(E, s, W.id, a);
369
289
  },
290
+ /**
291
+ * How many vertices there are — **read, not probed.**
292
+ *
293
+ * The manifest declares `vertex_count`. What stood here was a note saying no manifest carries
294
+ * it, and a doubling-then-bisecting `HEAD` search for the last chunk plus a
295
+ * `parquet_file_metadata` read of its row count — about a dozen requests and a query, per
296
+ * corpus, for a number already in hand.
297
+ */
370
298
  async total() {
371
- return await H(), G ?? 0;
299
+ if (y.count !== null) return Number(y.count);
300
+ const a = await h(`SELECT count(*) AS n FROM ${D}`);
301
+ return Number(C(a, "n")[0] ?? 0);
372
302
  },
373
303
  async extent() {
374
- const a = await H();
304
+ const a = await B();
375
305
  return {
376
- xMin: Math.min(...a.map((o) => o.x0)),
377
- yMin: Math.min(...a.map((o) => o.y0)),
378
- xMax: Math.max(...a.map((o) => o.x1)),
379
- yMax: Math.max(...a.map((o) => o.y1))
306
+ xMin: Math.min(...a.map((i) => i.x0)),
307
+ yMin: Math.min(...a.map((i) => i.y0)),
308
+ xMax: Math.max(...a.map((i) => i.x1)),
309
+ yMax: Math.max(...a.map((i) => i.y1))
380
310
  };
381
311
  },
382
- // Regions only, said the same way `duckBoundedSource` says it: no `explore`. A neighbourhood
383
- // needs adjacency this source does not index, and fossil's `expand` is what answers it —
384
- // `ExploringSource` is the shape waiting for whoever writes that one.
312
+ // Regions only, and it says so by being the only method there is. A neighbourhood needs
313
+ // adjacency this source does not index; `ExploringSource` declared the second question here and
314
+ // was deleted with it — `index.test.ts` carries why, and fossil's `expand` is what answers it.
385
315
  async slice(a) {
386
- const { fill: o, limit: d, minLinkPixels: C, perPixel: v, pinned: m, r: S, signal: b, view: w } = a, T = { ...J, category: o, size: S }, _ = await H(), R = Et(_, w);
387
- if (R.length === 0)
316
+ const { fill: i, limit: d, minLinkPixels: c, perPixel: $, pinned: L, r: T, signal: S, view: O } = a, N = { ...W, category: i, size: T }, g = await B(), w = tt(g, O);
317
+ if (w.length === 0)
388
318
  return {
389
319
  n: 0,
390
320
  marks: 0,
@@ -393,20 +323,21 @@ async function Tt(t) {
393
323
  links: new Float32Array(0),
394
324
  categories: new Uint16Array(0)
395
325
  };
396
- const [h, L] = await Promise.all([
397
- V("vertex", R),
398
- V("edge", u ? R : [])
326
+ const G = R.tilesFor({
327
+ type: y.type,
328
+ tiles: w,
329
+ directions: ["src"]
330
+ }), [et, P] = await Promise.all([
331
+ j(G.vertexUrls),
332
+ j(G.edgeUrls)
399
333
  ]);
400
- if (b != null && b.aborted) throw b.reason;
401
- const K = `read_parquet([${h.join(", ")}])`, yt = `read_parquet([${L.join(", ")}])`;
402
- return E.run(
403
- rt(K, yt, T, w, d, 0, m, v, C, K)
404
- );
334
+ if (S != null && S.aborted) throw S.reason;
335
+ const nt = `read_parquet([${et.join(", ")}])`, st = P.length > 0 ? `read_parquet([${P.join(", ")}])` : `(SELECT NULL::BIGINT AS ${N.source}, NULL::BIGINT AS ${N.target} WHERE FALSE)`;
336
+ return b.run($t(nt, st, N, O, d, L, $, c));
405
337
  }
406
- }, nodes: Y, edges: B };
338
+ }, nodes: D, edges: U };
407
339
  }
408
340
  export {
409
- wt as duckBoundedSource,
410
- Tt as openCorpus
341
+ gt as openCorpus
411
342
  };
412
343
  //# sourceMappingURL=duck-source.js.map