@bragi-gmbh/codebus 1.6.5 → 1.6.7

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.
@@ -79,15 +79,20 @@
79
79
  * against a fixture, per the build ticket).
80
80
  * --skip-build — same effect, reads graphify-out/graph.json
81
81
  * as-is without re-running graphify.
82
- * --dry-run — do everything except the final
83
- * graph_ingest_with_token call (still reports
84
- * the head and still validates).
82
+ * --dry-run — do everything except the final
83
+ * graph_ingest_with_token call (still reports
84
+ * the head and still validates). Reports the
85
+ * post-compaction size against the 25 MB
86
+ * ingest cap (PASS/FAIL) so pre-builds know.
87
+ * --no-compact — disable artifact compaction (compaction is
88
+ * ON by default).
85
89
  */
86
90
 
87
91
  import { execFileSync } from "node:child_process";
88
92
  import { existsSync, readFileSync, realpathSync } from "node:fs";
89
93
  import { join, resolve, relative, isAbsolute } from "node:path";
90
94
  import { pathToFileURL } from "node:url";
95
+ import { createHash } from "node:crypto";
91
96
  import { createClient } from "@supabase/supabase-js";
92
97
 
93
98
  const args = process.argv.slice(2);
@@ -97,7 +102,233 @@ const opt = (name) => {
97
102
  return i >= 0 ? args[i + 1] : undefined;
98
103
  };
99
104
 
105
+ // Artifact size cap + compaction (25 MB ingest cap, 0050
106
+ // graph_ingest_artifact: octet_length(p_artifact::text) > 25MB rejects).
107
+ // EXACT stored/read field map, derived from the RPC bodies — the compactor
108
+ // below must keep every one of these byte-identical and may drop anything
109
+ // else:
110
+ //
111
+ // 0050 graph_ingest_validate: nodes[] needs id/label/file_type (+optional
112
+ // tier); links[] (or edges[]) needs source/target/relation/confidence
113
+ // (+owner_source when relation=owned_by); hyperedges[] needs
114
+ // id/label/relation/confidence + non-empty nodes[] (+optional tier via
115
+ // confidence). Top level needs nodes + links/edges arrays (hyperedges
116
+ // optional).
117
+ // 0063 graph_content_sync (the ONLY projection into graph_nodes/graph_edges,
118
+ // full-replace on each promoted ingest): nodes keep id, label, file_type,
119
+ // source_file, source_location, community, norm_label, _origin, tier,
120
+ // metadata; edges keep source, target, relation, confidence,
121
+ // confidence_score, weight, source_file, context, owner_source.
122
+ // Hyperedges are NOT projected into tables but ARE persisted verbatim in
123
+ // the content-addressed graph_artifacts row — kept whole.
124
+ // 20260922230000 graph_read_contract_v1 (signature/doc derivation): reads
125
+ // ONLY meta->>signature/def and meta->>doc/docstring/description, i.e.
126
+ // keys INSIDE the node's metadata object — metadata is preserved whole,
127
+ // so the derivation is unaffected.
128
+ //
129
+ // Everything else observed in real artifacts is dropped: top-level extras
130
+ // (directed, multigraph, graph — which DUPLICATES hyperedges —,
131
+ // built_at_commit), stray per-node keys (e.g. source_url/captured_at/
132
+ // author/contributor), per-edge source_location (never read or stored).
133
+ // Minification comes free via JSON.stringify (no whitespace).
134
+ //
135
+ // Exported for scripts/test/graph-compact.validate.test.mjs (same
136
+ // import.meta.url guard convention as resolveRepoRoot above).
137
+ export const GRAPH_ARTIFACT_SIZE_CAP_BYTES = 25 * 1024 * 1024;
138
+ export const GRAPH_COMPACT_KEEP_NODE_FIELDS = new Set([
139
+ "id", "label", "file_type", "source_file", "source_location",
140
+ "community", "norm_label", "_origin", "tier", "metadata",
141
+ ]);
142
+ export const GRAPH_COMPACT_KEEP_EDGE_FIELDS = new Set([
143
+ "source", "target", "relation", "confidence", "confidence_score",
144
+ "weight", "source_file", "context", "owner_source",
145
+ ]);
146
+
147
+ export function compactArtifact(artifact) {
148
+ const removedBytes = {};
149
+ const dropStr = (obj, keepSet, section) => {
150
+ const out = {};
151
+ for (const [k, v] of Object.entries(obj)) {
152
+ if (keepSet.has(k)) {
153
+ out[k] = v;
154
+ } else {
155
+ const b = Buffer.byteLength(JSON.stringify({ [k]: v }), "utf-8");
156
+ removedBytes[`${section}.${k}`] = (removedBytes[`${section}.${k}`] ?? 0) + b;
157
+ }
158
+ }
159
+ return out;
160
+ };
161
+ const compacted = {};
162
+ // Top level: keep exactly what the RPCs read. Prefer `links` (graphify's
163
+ // real key); keep `edges` too if present so a future rename is not
164
+ // penalized — same acceptance rule as graph_ingest_validate.
165
+ for (const [k, v] of Object.entries(artifact)) {
166
+ if (k === "nodes" || k === "links" || k === "edges" || k === "hyperedges") {
167
+ compacted[k] = v;
168
+ } else {
169
+ const b = Buffer.byteLength(JSON.stringify({ [k]: v }), "utf-8");
170
+ removedBytes[`top.${k}`] = (removedBytes[`top.${k}`] ?? 0) + b;
171
+ }
172
+ }
173
+ if (Array.isArray(compacted.nodes)) {
174
+ compacted.nodes = compacted.nodes.map((n) => dropStr(n, GRAPH_COMPACT_KEEP_NODE_FIELDS, "node"));
175
+ }
176
+ for (const key of ["links", "edges"]) {
177
+ if (Array.isArray(compacted[key])) {
178
+ compacted[key] = compacted[key].map((e) => dropStr(e, GRAPH_COMPACT_KEEP_EDGE_FIELDS, "edge"));
179
+ }
180
+ }
181
+ // Hyperedges are persisted verbatim (content-addressed artifact) — never
182
+ // stripped, only minified by the final stringify.
183
+ return { compacted, removedBytes };
184
+ }
185
+
186
+ export function artifactBytes(obj) {
187
+ return Buffer.byteLength(JSON.stringify(obj), "utf-8");
188
+ }
189
+
190
+ export const GRAPH_CHUNK_PART_MAX_BYTES = 8 * 1024 * 1024;
191
+
192
+ // buildCanonicalText: THE canonical serialization for chunked ingest.
193
+ // Returns { head, chunks, str, sha256, counts, meta } where
194
+ // str = head + chunks.join("") + '}'
195
+ // byte-identically to what graph_ingest_commit_with_token reassembles
196
+ // (head + verbatim-concatenated textchunk payloads + '}'):
197
+ // head = {"directed":<d>,"multigraph":<m>,"graph":<g>,
198
+ // chunks are verbatim slices of the body
199
+ // '"nodes":[...],"links":[...],"hyperedges":[...]' (WITHOUT the trailing
200
+ // brace), split only at element boundaries (never mid-codepoint: element
201
+ // granularity via JSON.stringify per element). Inter-element and
202
+ // inter-section commas live INSIDE the chunk bytes — no join separator is
203
+ // added on either side. sha256 is over str. The commit RPC verifies sha
204
+ // BEFORE parsing, so these bytes round-trip untouched (no jsonb ::text
205
+ // re-serialization involved).
206
+ //
207
+ // Number-format contract: JSON.stringify emits integral floats as "1" (not
208
+ // "1.0"), matching Postgres jsonb numeric output; the server never
209
+ // re-serializes before hashing, so client bytes are authoritative.
210
+ export function buildCanonicalText(artifact, maxBytes = GRAPH_CHUNK_PART_MAX_BYTES) {
211
+ const links = Array.isArray(artifact.links) ? artifact.links
212
+ : Array.isArray(artifact.edges) ? artifact.edges : [];
213
+ const nodes = Array.isArray(artifact.nodes) ? artifact.nodes : [];
214
+ const hyperedges = Array.isArray(artifact.hyperedges) ? artifact.hyperedges : [];
215
+ const head =
216
+ '{"directed":' + JSON.stringify(artifact.directed ?? false) +
217
+ ',"multigraph":' + JSON.stringify(artifact.multigraph ?? false) +
218
+ ',"graph":' + JSON.stringify(artifact.graph ?? {}) + ',';
219
+ // Split one JSON array's elements into verbatim slices <= maxBytes.
220
+ // MIDDLE slices (neither first nor last of their array) are bare element
221
+ // runs with NO key prefix and NO closing bracket — they are only valid
222
+ // concatenated between their siblings. The FIRST slice carries the
223
+ // '"name":[' prefix; the LAST slice carries the ']' suffix; a single-slice
224
+ // array carries both. Slices therefore must NEVER merge across arrays:
225
+ // each array's slices form one ordered group, groups join with ','.
226
+ const splitArray = (name, elements) => {
227
+ const prefix = '"' + name + '":[';
228
+ if (elements.length === 0) return [prefix + "]"];
229
+ const slices = [];
230
+ let cur = prefix, first = true;
231
+ for (const el of elements) {
232
+ const piece = (first ? "" : ",") + JSON.stringify(el);
233
+ if (!first && Buffer.byteLength(cur + piece + "]", "utf-8") > maxBytes) {
234
+ slices.push(cur);
235
+ cur = JSON.stringify(el);
236
+ first = false;
237
+ continue;
238
+ }
239
+ cur += piece;
240
+ first = false;
241
+ }
242
+ slices.push(cur + "]");
243
+ return slices;
244
+ };
245
+ // Groups join with ',' — the comma is OUTSIDE the chunk bytes (it is the
246
+ // join separator on both sides: server comma-joins too)... NO — verbatim
247
+ // protocol says no separators are added. Resolve: keep commas inside by
248
+ // attaching the trailing comma to each group except the last: group text
249
+ // is slices.join(",") and groups are concatenated directly.
250
+ const groups = [splitArray("nodes", nodes), splitArray("links", links), splitArray("hyperedges", hyperedges)];
251
+ const parts = [];
252
+ groups.forEach((slices, gi) => {
253
+ const lastGroup = gi === groups.length - 1;
254
+ slices.forEach((sl, i) => {
255
+ const lastSlice = i === slices.length - 1;
256
+ // Attach inter-group comma to the group's final slice (except the
257
+ // very last slice overall); inter-slice commas within a group attach
258
+ // to each slice except the group's last.
259
+ let chunk = sl;
260
+ if (!lastSlice) chunk = chunk + ",";
261
+ else if (!lastGroup) chunk = chunk + ",";
262
+ parts.push(chunk);
263
+ });
264
+ });
265
+ // Merge SMALL adjacent chunks (verbatim concat stays valid: commas are
266
+ // inside the bytes, so plain concatenation preserves the text).
267
+ const merged = [];
268
+ for (const c of parts) {
269
+ if (merged.length > 0 && Buffer.byteLength(merged[merged.length - 1] + c, "utf-8") <= maxBytes) {
270
+ merged[merged.length - 1] += c;
271
+ } else merged.push(c);
272
+ }
273
+ const body = merged.join("");
274
+ const str = head + body + "}";
275
+ return {
276
+ head,
277
+ meta: { directed: artifact.directed ?? false, multigraph: artifact.multigraph ?? false, graph: artifact.graph ?? {} },
278
+ chunks: merged,
279
+ str,
280
+ sha256: createHash("sha256").update(str, "utf-8").digest("hex"),
281
+ counts: { nodes: nodes.length, links: links.length, hyperedges: hyperedges.length },
282
+ };
283
+ const sha256 = createHash("sha256").update(str, "utf-8").digest("hex");
284
+ return {
285
+ head,
286
+ meta: { directed: artifact.directed ?? false, multigraph: artifact.multigraph ?? false, graph: artifact.graph ?? {} },
287
+ chunks: parts,
288
+ str,
289
+ sha256,
290
+ counts: { nodes: nodes.length, links: links.length, hyperedges: hyperedges.length },
291
+ };
292
+ }
293
+
294
+ export function chunkArray(elements, maxBytes = GRAPH_CHUNK_PART_MAX_BYTES) {
295
+ const parts = [];
296
+ let cur = [];
297
+ let curBytes = 2; // "[]"
298
+ for (const el of elements) {
299
+ const elBytes = Buffer.byteLength(JSON.stringify(el), "utf-8");
300
+ const add = (cur.length === 0 ? elBytes : elBytes + 1);
301
+ if (cur.length > 0 && curBytes + add > maxBytes) {
302
+ parts.push(cur);
303
+ cur = [];
304
+ curBytes = 2;
305
+ }
306
+ cur.push(el);
307
+ curBytes += cur.length === 1 ? elBytes : elBytes + 1;
308
+ }
309
+ if (cur.length > 0) parts.push(cur);
310
+ return parts;
311
+ }
312
+
313
+ export function buildCanonical(artifact) {
314
+ const links = Array.isArray(artifact.links) ? artifact.links
315
+ : Array.isArray(artifact.edges) ? artifact.edges : [];
316
+ const canonical = {
317
+ directed: artifact.directed ?? false,
318
+ multigraph: artifact.multigraph ?? false,
319
+ graph: artifact.graph ?? {},
320
+ nodes: Array.isArray(artifact.nodes) ? artifact.nodes : [],
321
+ links,
322
+ hyperedges: Array.isArray(artifact.hyperedges) ? artifact.hyperedges : [],
323
+ };
324
+ const str = JSON.stringify(canonical);
325
+ const sha256 = createHash("sha256").update(str, "utf-8").digest("hex");
326
+ return { canonical, str, sha256 };
327
+ }
328
+
100
329
  const DRY_RUN = flag("--dry-run");
330
+ const FORCE_CHUNKED = flag("--force-chunked");
331
+ const COMPACT = !flag("--no-compact");
101
332
  const SKIP_BUILD = flag("--skip-build") || !!opt("--artifact-path");
102
333
  const ARTIFACT_PATH_OVERRIDE = opt("--artifact-path");
103
334
 
@@ -194,7 +425,12 @@ async function main() {
194
425
  // §B.2 preamble).
195
426
  if (!supabaseUrl) die("SUPABASE_URL is required");
196
427
  if (!anonKey) die("SUPABASE_ANON_KEY is required");
197
- if (!ingestToken) die("GRAPH_INGEST_TOKEN is required (this repo's ingest-scoped token — mint via graph_ingest_token_mint)");
428
+ // --dry-run for a repo that is not chartered yet (pre-build extraction before
429
+ // the owner has minted a token) is legitimate: build + validate need no
430
+ // token. Everything that DOES need one (head report, ingest) is skipped
431
+ // below with an explicit line, never silently.
432
+ if (!ingestToken && !DRY_RUN) die("GRAPH_INGEST_TOKEN is required (this repo's ingest-scoped token — mint via graph_ingest_token_mint)");
433
+ if (!ingestToken && DRY_RUN) console.log("dry-run without GRAPH_INGEST_TOKEN: head report will be SKIPPED (token-less pre-build)");
198
434
 
199
435
  let commitSha = process.env.GITHUB_SHA;
200
436
  if (!commitSha) {
@@ -220,17 +456,22 @@ async function main() {
220
456
  // work happens. This is what makes freshness honest even when step 2
221
457
  // fails below — the hub already knows main advanced. Token-scoped: no
222
458
  // repo_id is sent, it's resolved server-side from GRAPH_INGEST_TOKEN.
223
- section("Step 1: graph_head_report_with_token (authoritative tip, before build)");
224
- {
225
- const { error } = await supabase.rpc("graph_head_report_with_token", {
226
- p_token: ingestToken,
227
- p_sha: commitSha,
228
- p_run_id: String(runId),
229
- });
230
- if (error) die(`graph_head_report_with_token failed: ${error.message} (nothing downstream can be trusted if the tip itself was never recorded)`);
231
- console.log(`✓ head reported: sha=${commitSha}`);
459
+ if (!ingestToken && DRY_RUN) {
460
+ section("Step 1: graph_head_report_with_token — SKIPPED (dry-run, no token)");
461
+ } else {
462
+ section("Step 1: graph_head_report_with_token (authoritative tip, before build)");
463
+ {
464
+ const { error } = await supabase.rpc("graph_head_report_with_token", {
465
+ p_token: ingestToken,
466
+ p_sha: commitSha,
467
+ p_run_id: String(runId),
468
+ });
469
+ if (error) die(`graph_head_report_with_token failed: ${error.message} (nothing downstream can be trusted if the tip itself was never recorded)`);
470
+ console.log(`✓ head reported: sha=${commitSha}`);
471
+ }
232
472
  }
233
473
 
474
+
234
475
  // ── Step 2: build (unless testing against a fixture/existing artifact).
235
476
  section("Step 2: graphify build");
236
477
  const graphJsonPath = join(repoRoot, "graphify-out", "graph.json");
@@ -263,11 +504,69 @@ async function main() {
263
504
  } catch (err) {
264
505
  die(`failed to parse artifact JSON at ${artifactPath}: ${err.message}`);
265
506
  }
507
+ // ── Step 2b: deterministic augmentation (SQL functions, gateway
508
+ // routes, contract schemas as EXTRACTED nodes). Runs after the graphify
509
+ // build, before validation, so the SAME augmented artifact is validated
510
+ // (Step 4) and ingested (Step 5). Uses only vocab relations/file_types —
511
+ // no vocabulary change required. Persisted back to the artifact file.
512
+ section("Step 2b: deterministic augmentation (graph-augment.mjs)");
513
+ {
514
+ try {
515
+ const { augmentArtifact } =
516
+ await import(join(repoRoot, "scripts", "graph-augment.mjs"));
517
+ const res = augmentArtifact(artifact, repoRoot);
518
+ const { writeFileSync } = await import("node:fs");
519
+ writeFileSync(artifactPath, JSON.stringify(res.artifact, null, 2));
520
+ artifact = res.artifact;
521
+ console.log(`✓ augment: +${res.addedNodes} nodes +${res.addedEdges} edges ` +
522
+ `(sql ${res.per.sql.nodes}n/${res.per.sql.edges}e, ` +
523
+ `routes ${res.per.routes.nodes}n/${res.per.routes.edges}e, ` +
524
+ `schemas ${res.per.schemas.nodes}n/${res.per.schemas.edges}e)`);
525
+ } catch (err) {
526
+ die(`augmentation failed: ${err.message}`);
527
+ }
528
+ }
529
+
266
530
  const nodeCount = artifact.nodes?.length ?? 0;
267
531
  const edgeCount = (artifact.links ?? artifact.edges ?? []).length;
268
532
  const hyperedgeCount = (artifact.hyperedges ?? artifact.graph?.hyperedges ?? []).length;
533
+ const rawBytes = artifactBytes(artifact);
269
534
  console.log(`artifact: ${artifactPath}`);
270
535
  console.log(` nodes=${nodeCount} edges=${edgeCount} hyperedges=${hyperedgeCount}`);
536
+ console.log(` raw size: ${rawBytes} bytes (${(rawBytes / 1048576).toFixed(2)} MB)`);
537
+
538
+ // ── Step 2b: compaction (default ON, --no-compact disables). Removes
539
+ // every field no RPC reads or stores (see the map above) + minifies.
540
+ // Counts and all stored fields are preserved byte-identical.
541
+ if (COMPACT) {
542
+ const { compacted, removedBytes } = compactArtifact(artifact);
543
+ const compactBytes = artifactBytes(compacted);
544
+ const cn = compacted.nodes?.length ?? 0;
545
+ const ce = (compacted.links ?? compacted.edges ?? []).length;
546
+ const ch = (compacted.hyperedges ?? []).length;
547
+ if (cn !== nodeCount || ce !== edgeCount) {
548
+ die(`compaction changed counts (nodes ${nodeCount}->${cn}, edges ${edgeCount}->${ce}) — refusing to publish a compacted artifact that disagrees with the build`);
549
+ }
550
+ console.log(` compacted size: ${compactBytes} bytes (${(compactBytes / 1048576).toFixed(2)} MB)`);
551
+ const entries = Object.entries(removedBytes).sort((a, b) => b[1] - a[1]);
552
+ if (entries.length > 0) {
553
+ console.log(` removed fields:`);
554
+ for (const [k, b] of entries) {
555
+ console.log(` ${k}: ${b} bytes`);
556
+ }
557
+ } else {
558
+ console.log(` (compaction removed nothing — artifact already minimal)`);
559
+ }
560
+ artifact = compacted;
561
+ } else {
562
+ console.log(` (--no-compact given: skipping compaction)`);
563
+ }
564
+ const finalBytes = artifactBytes(artifact);
565
+ const capState = finalBytes > GRAPH_ARTIFACT_SIZE_CAP_BYTES ? "FAIL" : "PASS";
566
+ console.log(` ingest size check (${(GRAPH_ARTIFACT_SIZE_CAP_BYTES / 1048576).toFixed(0)} MB cap): ${finalBytes} bytes — ${capState}`);
567
+ if (capState === "FAIL") {
568
+ console.log(` (artifact exceeds the 25 MB ingest cap even after compaction — ingest WILL be rejected; options: raise the cap via migration, or shard by top-level directory)`);
569
+ }
271
570
 
272
571
  // ── Step 3: resolve the current vocab version (never hardcode — the
273
572
  // vocabulary is the versioned artifact, per B.2.3). graph_vocab_current()
@@ -314,7 +613,54 @@ async function main() {
314
613
  // last-good serving completely untouched (0050's own guarantee), and
315
614
  // (finding 2) the exact validated artifact is persisted content-addressed
316
615
  // in the SAME transaction as promotion.
616
+ //
617
+ // Chunked path: when the minified artifact exceeds the 25 MB cap (or
618
+ // --force-chunked), canonicalize + split into <=8MB parts and ingest via
619
+ // graph_ingest_begin_with_token / graph_ingest_append_with_token /
620
+ // graph_ingest_commit_with_token. Single-call path unchanged otherwise.
317
621
  section("Step 5: graph_ingest_with_token (publish)");
622
+ const useChunked = finalBytes > GRAPH_ARTIFACT_SIZE_CAP_BYTES || FORCE_CHUNKED;
623
+ if (useChunked) {
624
+ const minified = JSON.parse(JSON.stringify(artifact));
625
+ const { meta, chunks, str, sha256, counts } = buildCanonicalText(minified);
626
+ const totalBytes = Buffer.byteLength(str, "utf-8");
627
+ console.log(`chunked ingest: sha=${sha256} textchunks=${chunks.length} nodes=${counts.nodes} links=${counts.links} hyperedges=${counts.hyperedges} total=${totalBytes} bytes`);
628
+ if (DRY_RUN) { console.log("=== DRY-RUN: skipping chunked ingest RPCs ==="); process.exit(0); }
629
+ const { data: beginData, error: beginErr } = await supabase.rpc("graph_ingest_begin_with_token", {
630
+ p_token: ingestToken, p_source_sha: commitSha,
631
+ p_run_id: String(runId), p_workflow_ref: workflowRef,
632
+ p_meta: { vocab_version: vocabVersion, compacted: COMPACT },
633
+ p_declared_node_count: counts.nodes,
634
+ p_declared_link_count: counts.links,
635
+ p_declared_hyperedge_count: counts.hyperedges,
636
+ p_sha256: sha256,
637
+ });
638
+ if (beginErr) die(`CHUNKED BEGIN FAILED: ${beginErr.message}`);
639
+ const uploadId = beginData.upload_id ?? beginData;
640
+ let partNo = 0;
641
+ const append = async (kind, part) => {
642
+ partNo++;
643
+ const bytes = Buffer.byteLength(JSON.stringify(part), "utf-8");
644
+ const { error } = await supabase.rpc("graph_ingest_append_with_token", {
645
+ p_token: ingestToken, p_upload_id: uploadId, p_part_no: partNo, p_kind: kind, p_payload: part,
646
+ });
647
+ if (error) die(`CHUNKED APPEND FAILED part ${partNo} (${kind}): ${error.message}`);
648
+ console.log(` part ${partNo}: kind=${kind} bytes=${bytes}`);
649
+ };
650
+ await append("meta", meta);
651
+ for (const c of chunks) await append("textchunk", c);
652
+ // NOTE: append() JSON-encodes the chunk string as a jsonb string payload;
653
+ // the server concatenates chunk text verbatim (payload #>> '{}').
654
+ const { data: commitData, error: commitErr } = await supabase.rpc("graph_ingest_commit_with_token", {
655
+ p_token: ingestToken, p_upload_id: uploadId,
656
+ });
657
+ if (commitErr) die(`CHUNKED COMMIT FAILED: ${commitErr.message}`);
658
+ console.log(`chunked ingest committed: parts=${partNo} total=${totalBytes} bytes`);
659
+ console.log(JSON.stringify(commitData));
660
+ console.log("");
661
+ console.log("=== SUCCESS ===");
662
+ return;
663
+ }
318
664
  {
319
665
  const { data, error } = await supabase.rpc("graph_ingest_with_token", {
320
666
  p_token: ingestToken,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bragi-gmbh/codebus",
3
- "version": "1.6.5",
3
+ "version": "1.6.7",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "codebus": "bin/codebus.js",