@kanzo-tech/graph 0.2.0 → 0.4.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,118 +1,72 @@
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 le, PAYLOAD_COORDINATES as v, PAYLOAD_IDENTITY as ue, PAYLOAD_ADDRESS as de } from "@fossil-lang/corpus";
3
+ import { numbers as I, clausePoints as Ee, fillColumn as _, column as pe } from "@kanzo-tech/mosaic";
4
+ import { SliceRead as k } from "./slice-client.js";
5
+ import { typeOf as ye, denseOf as X, vertexId as me } from "./resident.js";
6
+ const Z = 0;
7
+ function Ae(e, t) {
6
8
  return {
7
- points: new W(t, e),
8
- links: new W(t, e),
9
- meta: new W(t)
9
+ points: new k(e, t),
10
+ links: new k(e, t),
11
+ meta: new k(e)
10
12
  };
11
13
  }
12
- function nt(t) {
13
- let e = Promise.resolve();
14
+ function $e(e) {
15
+ let t = Promise.resolve();
14
16
  return (n) => {
15
- const s = e.then(() => t.ask(() => n));
16
- return e = s.catch(() => {
17
+ const s = t.then(() => e.ask(() => n));
18
+ return t = s.catch(() => {
17
19
  }), s;
18
20
  };
19
21
  }
20
- function Q(t) {
21
- if (t == null) return "";
22
- const n = (Array.isArray(t) ? t : [t]).filter((s) => s != null).map((s) => String(s));
22
+ function q(e) {
23
+ if (e == null) return "";
24
+ const n = (Array.isArray(e) ? e : [e]).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) {
26
- return t ? e ? `(${t}) AND (${e})` : t : e || "TRUE";
27
+ function K(e, t) {
28
+ return e ? t ? `(${e}) AND (${t})` : e : t || "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 Se(e, t) {
77
31
  const s = [
78
- [t.x, e.xMin, ">="],
79
- [t.x, e.xMax, "<="],
80
- [t.y, e.yMin, ">="],
81
- [t.y, e.yMax, "<="]
82
- ].filter(([, r]) => Number.isFinite(r)).map(([r, i, c]) => `${r} ${c} ${i}`);
32
+ [e.x, t.xMin, ">="],
33
+ [e.x, t.xMax, "<="],
34
+ [e.y, t.yMin, ">="],
35
+ [e.y, t.yMax, "<="]
36
+ ].filter(([, a]) => Number.isFinite(a)).map(([a, i, c]) => `${a} ${c} ${i}`);
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 fe = (e) => `greatest(1, CAST(ceil(matched / ${e}.0) AS BIGINT))`;
40
+ function V(e, t, n, s, a = !0) {
41
+ const i = t.size ? `, ${t.size} AS size` : "", c = t.subject ? `, ${t.subject} AS subject` : "", p = t.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 ${t.id} AS id, ${t.x} AS x, ${t.y} AS y${i}${c}${t.category ? `, ${t.category} AS cat` : ""},
90
44
  count(*) OVER () AS matched
91
- FROM ${t}
45
+ FROM ${e}
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${t.size ? ", size" : ""}${t.subject ? ", subject" : ""}${a ? ", matched" : ""},
49
+ ${p},
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 % ${fe(s)} = 0
99
53
  LIMIT ${s}
100
54
  )`;
101
55
  }
102
- function at(t, e, n, s) {
56
+ function Te(e, t, 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 a = s * n;
59
+ return `(${e}.x - ${t}.x) * (${e}.x - ${t}.x) + (${e}.y - ${t}.y) * (${e}.y - ${t}.y) >= ${a * a}`;
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 Ne(e, t, n, s, a, i, c) {
62
+ const p = K(`NOT (${s})`, a), E = Te("a", "b", i, c);
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 ${e} WHERE ${p}
111
65
  ), reach AS (
112
66
  SELECT id, x, y FROM vis UNION ALL SELECT id, x, y FROM out
113
67
  ), span AS (
114
68
  SELECT sv.local AS src, tv.local AS dst, a.id AS src_id, b.id AS dst_id
115
- FROM ${e} e
69
+ FROM ${t} e
116
70
  JOIN reach a ON e.${n.source} = a.id
117
71
  JOIN reach b ON e.${n.target} = b.id
118
72
  LEFT JOIN vis sv ON sv.id = a.id
@@ -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 he(e, t, n, s, a, i, c, p) {
84
+ const E = Se(n, s), h = (i ?? []).filter((u) => ye(u) === Z).map(X), b = h.length > 0 ? ` OR ${n.id} IN (${h.join(",")})` : "", S = `(${E})${b}`, y = (u) => K(S, q(u)), f = n.size ? ", size" : "", w = n.subject ? ", subject" : "", T = (u) => Ne(e, t, n, S, q(u), c, p);
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) => `${V(e, n, y(u), a)}${T(u)}
94
+ SELECT local, id, x, y, category, matched, TRUE AS mark${f}${w} 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) => `${V(e, n, y(u), a, !1)}${T(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, R) => ({
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(I(u, "matched")[0] ?? 0),
122
+ ...xe(u, R, n.size ? "size" : void 0, n.subject !== void 0)
173
123
  })
174
124
  };
175
125
  }
176
- function it(t) {
177
- 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));
126
+ function Re(e) {
127
+ let t = null, n = null;
128
+ const s = /* @__PURE__ */ new Map(), a = (i) => (c) => {
129
+ if (!t || !n || (s.set(i, c), s.size < 2)) return;
130
+ const p = s.get("points"), E = s.get("links");
131
+ s.clear(), n(t.assemble(p, E));
182
132
  };
183
133
  return {
184
134
  api: {
@@ -190,223 +140,241 @@ function it(t) {
190
140
  * again would run the same two queries a second time to learn what is in hand.
191
141
  */
192
142
  watch(i) {
193
- return n = i, t.points.onAnswer = r("points"), t.links.onAnswer = r("links"), () => {
194
- n = null, s.clear(), e = null, t.points.release(), t.links.release(), t.meta.release();
143
+ return n = i, e.points.onAnswer = a("points"), e.links.onAnswer = a("links"), () => {
144
+ n = null, s.clear(), t = null, e.points.release(), e.links.release(), e.meta.release();
195
145
  };
196
146
  }
197
147
  },
198
148
  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)
149
+ t = i, s.clear();
150
+ const [c, p] = await Promise.all([
151
+ e.points.ask(i.points),
152
+ e.links.ask(i.links)
203
153
  ]);
204
- return i.assemble(c, l);
154
+ return i.assemble(c, p);
205
155
  }
206
156
  };
207
157
  }
208
- function ot(t, e, n, s) {
209
- e && e.update(
210
- $t([n], s == null ? void 0 : s.map((r) => [tt(r)]), {
211
- source: t.points,
212
- clients: /* @__PURE__ */ new Set([t.points, t.links])
158
+ function Oe(e, t, n, s) {
159
+ t && t.update(
160
+ Ee([n], s == null ? void 0 : s.map((a) => [X(a)]), {
161
+ source: e.points,
162
+ clients: /* @__PURE__ */ new Set([e.points, e.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 xe(e, t, n, s = !1) {
167
+ const a = J(e, "x"), i = new Float32Array(a * 2), c = _(e, "x", i, 0, 2);
168
+ _(e, "y", i, 1, 2);
169
+ const p = new Int32Array(a);
170
+ _(e, "mark", p);
171
+ let E = 0;
172
+ for (; E < c && p[E] !== 0; ) E++;
173
+ const h = new Float64Array(c);
174
+ _(e, "id", h);
175
+ const b = new BigUint64Array(c);
176
+ for (let R = 0; R < c; R++) b[R] = me(Z, h[R]);
177
+ const S = new Uint16Array(c);
178
+ _(e, "category", S);
179
+ const y = s ? pe(e, "subject") : void 0;
180
+ let f;
181
+ n && (f = new Float32Array(c), _(e, n, f));
182
+ const w = J(t, "src"), T = new Float32Array(w * 2), u = _(t, "src", T, 0, 2);
183
+ return _(t, "dst", T, 1, 2), {
184
+ marks: E,
185
+ vertices: b,
186
+ subjects: y,
187
+ positions: i.subarray(0, c * 2),
188
+ links: T.subarray(0, u * 2),
189
+ categories: S,
190
+ sizes: f
241
191
  };
242
192
  }
243
- function X(t, e) {
244
- var r;
245
- const n = t == null ? void 0 : t.numRows;
193
+ function J(e, t) {
194
+ var a;
195
+ const n = e == null ? void 0 : e.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);
248
- return s ? s.length : Array.from(t).length;
249
- }
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, "");
197
+ const s = (a = e == null ? void 0 : e.getChild) == null ? void 0 : a.call(e, t);
198
+ return s ? s.length : Array.from(e).length;
254
199
  }
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());
200
+ function Le(e, t) {
201
+ const n = [], s = [];
202
+ for (const a of e) {
203
+ const i = { edgeType: a.edgeType, srcType: a.srcType, dstType: a.dstType };
204
+ if (a.srcType !== t || a.dstType !== t) {
205
+ s.push({ ...i, reason: "other-space" });
206
+ continue;
207
+ }
208
+ const c = a.adjacency("src");
209
+ if (c === null) {
210
+ s.push({ ...i, reason: "not-declared" });
211
+ continue;
212
+ }
213
+ n.push({
214
+ ...i,
215
+ address: a,
216
+ adjacency: c,
217
+ view: `corpus_${a.srcType}_${a.edgeType}_${a.dstType}`
218
+ });
263
219
  }
264
- return r;
220
+ return { drawn: n, undrawn: s };
265
221
  }
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();
222
+ async function ge(e) {
223
+ const t = await fetch(e);
224
+ if (!t.ok) throw new Error(`corpus: ${e} is not readable (${t.status})`);
225
+ return t.text();
270
226
  }
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);
285
- if (!(a != null && a.getDuckDB)) return null;
227
+ async function Fe(e) {
228
+ var z, G, P;
229
+ const { coordinator: t, dest: n, filterBy: s, readText: a = ge, subjects: i = !1, vertexType: c, wasmUrl: p } = e, E = Ae(t, s), h = $e(E.meta), b = Re(E), S = await le(n, { readText: a, wasmUrl: p }), y = S.vertexType(c), { drawn: f, undrawn: w } = Le(S.incident(y.type), y.type), T = f.map((o) => o.adjacency);
230
+ let u = null;
231
+ const R = async () => {
232
+ var r;
233
+ const o = (r = t.databaseConnector) == null ? void 0 : r.call(t);
234
+ if (!(o != null && o.getDuckDB)) return null;
286
235
  try {
287
- return await a.getDuckDB();
236
+ return await o.getDuckDB();
288
237
  } catch {
289
238
  return null;
290
239
  }
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;
240
+ }, C = /* @__PURE__ */ new Map(), F = /* @__PURE__ */ new Map();
241
+ let M = 0;
242
+ const Q = 64 * 1024 * 1024, ee = 256 * 1024;
243
+ async function j(o) {
244
+ if (o.length === 0) return [];
245
+ const r = await R();
246
+ if (!r) return o.map((l) => `'${l}'`);
247
+ const d = await Promise.all(
248
+ o.map(async (l) => {
249
+ const m = F.get(l);
250
+ if (m !== void 0 && (m.startsWith("'") || C.has(m)))
251
+ return m;
252
+ const O = `corpus_${l.replace(/[^A-Za-z0-9]+/g, "_")}`, x = await fetch(l, { method: "HEAD" }), A = Number(x.headers.get("content-length"));
253
+ if (!x.ok || !Number.isFinite(A) || A > ee) {
254
+ const L = `'${l}'`;
255
+ return F.set(l, L), L;
256
+ }
257
+ const N = await fetch(l);
258
+ if (!N.ok) return `'${l}'`;
259
+ const $ = new Uint8Array(await N.arrayBuffer());
260
+ return await r.registerFileBuffer(O, $), C.set(O, $.byteLength), F.set(l, O), M += $.byteLength, O;
307
261
  })
308
262
  );
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);
263
+ for (; M > Q && C.size > 0; ) {
264
+ const [l, m] = C.entries().next().value;
265
+ if (d.includes(l)) break;
266
+ C.delete(l), M -= m, await r.dropFile(l);
313
267
  }
314
- return v.map((m) => m.startsWith("'") ? m : `'${m}'`);
268
+ return d.map((l) => l.startsWith("'") ? l : `'${l}'`);
315
269
  }
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]
270
+ const H = y.files(), D = (o) => o.map((r) => `'${r.replace(/'/g, "''")}'`).join(", ");
271
+ function B() {
272
+ return u ?? (u = (async () => {
273
+ const o = D(H), [r, d] = v, l = S.container === "rowgroups" ? "row_group_id" : `list_position([${o}], file_name) - 1`, m = await h(
274
+ `SELECT ${l} AS tile,
275
+ min(CASE WHEN path_in_schema = '${r}' THEN coalesce(stats_min_value, stats_min)::DOUBLE END) AS x0,
276
+ max(CASE WHEN path_in_schema = '${r}' THEN coalesce(stats_max_value, stats_max)::DOUBLE END) AS x1,
277
+ min(CASE WHEN path_in_schema = '${d}' THEN coalesce(stats_min_value, stats_min)::DOUBLE END) AS y0,
278
+ max(CASE WHEN path_in_schema = '${d}' THEN coalesce(stats_max_value, stats_max)::DOUBLE END) AS y1
279
+ FROM parquet_metadata([${o}])
280
+ WHERE path_in_schema IN ('${r}', '${d}') GROUP BY 1 ORDER BY 1`
281
+ ), O = I(m, "tile"), [x, A, N, $] = ["x0", "x1", "y0", "y1"].map((L) => I(m, L));
282
+ return O.map((L, g) => ({
283
+ tile: L,
284
+ x0: x == null ? void 0 : x[g],
285
+ x1: A == null ? void 0 : A[g],
286
+ y0: N == null ? void 0 : N[g],
287
+ y1: $ == null ? void 0 : $[g]
346
288
  }));
347
- })()), D;
289
+ })()), u;
348
290
  }
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);
291
+ function te(o, r) {
292
+ return o.filter((d) => d.x1 >= r.xMin && d.x0 <= r.xMax && d.y1 >= r.yMin && d.y0 <= r.yMax).map((d) => d.tile);
351
293
  }
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(", ");
360
- 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')`
365
- ), { source: {
366
- ...E.api,
367
- publish(a) {
368
- ot(c, s, J.id, a);
369
- },
370
- async total() {
371
- return await H(), G ?? 0;
372
- },
373
- async extent() {
374
- const a = await H();
375
- 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))
380
- };
381
- },
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.
385
- 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)
294
+ const W = {
295
+ id: de[0],
296
+ subject: i ? ue[0] : void 0,
297
+ x: v[0],
298
+ y: v[1],
299
+ source: ((z = T[0]) == null ? void 0 : z.column) ?? "src_dense",
300
+ target: ((P = (G = f[0]) == null ? void 0 : G.address.adjacency("dst")) == null ? void 0 : P.column) ?? "dst_dense"
301
+ }, U = `corpus_${y.type}`;
302
+ await t.exec(
303
+ `CREATE OR REPLACE VIEW ${U} AS SELECT * FROM read_parquet([${D(H)}])`
304
+ );
305
+ for (const o of f)
306
+ await t.exec(
307
+ `CREATE OR REPLACE VIEW ${o.view} AS
308
+ SELECT * FROM read_parquet([${D(o.address.projectionFiles(1, "src"))}])`
309
+ );
310
+ return {
311
+ source: {
312
+ ...b.api,
313
+ publish(o) {
314
+ Oe(E, s, W.id, o);
315
+ },
316
+ /**
317
+ * How many vertices there are — **read, not probed.**
318
+ *
319
+ * The manifest declares `vertex_count`. What stood here was a note saying no manifest carries
320
+ * it, and a doubling-then-bisecting `HEAD` search for the last chunk plus a
321
+ * `parquet_file_metadata` read of its row count — about a dozen requests and a query, per
322
+ * corpus, for a number already in hand.
323
+ */
324
+ async total() {
325
+ if (y.count !== null) return Number(y.count);
326
+ const o = await h(`SELECT count(*) AS n FROM ${U}`);
327
+ return Number(I(o, "n")[0] ?? 0);
328
+ },
329
+ async extent() {
330
+ const o = await B();
388
331
  return {
389
- n: 0,
390
- marks: 0,
391
- vertices: new BigUint64Array(0),
392
- positions: new Float32Array(0),
393
- links: new Float32Array(0),
394
- categories: new Uint16Array(0)
332
+ xMin: Math.min(...o.map((r) => r.x0)),
333
+ yMin: Math.min(...o.map((r) => r.y0)),
334
+ xMax: Math.max(...o.map((r) => r.x1)),
335
+ yMax: Math.max(...o.map((r) => r.y1))
395
336
  };
396
- const [h, L] = await Promise.all([
397
- V("vertex", R),
398
- V("edge", u ? R : [])
399
- ]);
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
- );
405
- }
406
- }, nodes: Y, edges: B };
337
+ },
338
+ // Regions only, and it says so by being the only method there is. A neighbourhood needs
339
+ // adjacency this source does not index; `ExploringSource` declared the second question here and
340
+ // was deleted with it — `index.test.ts` carries why, and fossil's `expand` is what answers it.
341
+ async slice(o) {
342
+ const { fill: r, limit: d, minLinkPixels: l, perPixel: m, pinned: O, r: x, signal: A, view: N } = o, $ = { ...W, category: r, size: x }, L = await B(), g = te(L, N);
343
+ if (g.length === 0)
344
+ return {
345
+ n: 0,
346
+ marks: 0,
347
+ vertices: new BigUint64Array(0),
348
+ positions: new Float32Array(0),
349
+ links: new Float32Array(0),
350
+ categories: new Uint16Array(0)
351
+ };
352
+ const ne = S.tilesFor({
353
+ type: y.type,
354
+ tiles: g,
355
+ directions: ["src"]
356
+ }), se = [
357
+ ...new Set(T.flatMap((ie) => g.map((ce) => ie.tileUrl(ce))))
358
+ ], [ae, Y] = await Promise.all([
359
+ j(ne.vertexUrls),
360
+ j(se)
361
+ ]);
362
+ if (A != null && A.aborted) throw A.reason;
363
+ const oe = `read_parquet([${ae.join(", ")}])`, re = Y.length > 0 ? `read_parquet([${Y.join(", ")}])` : `(SELECT NULL::BIGINT AS ${$.source}, NULL::BIGINT AS ${$.target} WHERE FALSE)`;
364
+ return b.run(he(oe, re, $, N, d, O, m, l));
365
+ }
366
+ },
367
+ nodes: U,
368
+ edges: f.map(({ edgeType: o, srcType: r, dstType: d, view: l }) => ({
369
+ edgeType: o,
370
+ srcType: r,
371
+ dstType: d,
372
+ view: l
373
+ })),
374
+ undrawn: w
375
+ };
407
376
  }
408
377
  export {
409
- wt as duckBoundedSource,
410
- Tt as openCorpus
378
+ Fe as openCorpus
411
379
  };
412
380
  //# sourceMappingURL=duck-source.js.map