latticesql 2.2.3 → 2.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.
package/dist/cli.js CHANGED
@@ -21571,10 +21571,14 @@ var textDecoder = new TextDecoder("utf-8");
21571
21571
  function decodeUtf8(bytes) {
21572
21572
  return textDecoder.decode(bytes);
21573
21573
  }
21574
- async function loadOptional(specifier) {
21574
+ async function loadParser(specifier) {
21575
21575
  try {
21576
21576
  return await import(specifier);
21577
- } catch {
21577
+ } catch (err) {
21578
+ console.error(
21579
+ `[latticesql] document parser "${specifier}" failed to load \u2014 document extraction is degraded (likely a broken/partial install or incompatible build). Reinstall dependencies (\`npm install\`). Cause:`,
21580
+ err
21581
+ );
21578
21582
  return null;
21579
21583
  }
21580
21584
  }
@@ -21716,7 +21720,7 @@ function stripHtml(html) {
21716
21720
  return text.replace(/[ \t\f\r]+/g, " ").replace(/ *\n */g, "\n").replace(/\n{3,}/g, "\n\n").trim();
21717
21721
  }
21718
21722
  async function unzip(path2) {
21719
- const fflate = await loadOptional("fflate");
21723
+ const fflate = await loadParser("fflate");
21720
21724
  if (!fflate || typeof fflate.unzipSync !== "function") return null;
21721
21725
  try {
21722
21726
  const buf = await readFile(path2);
@@ -21735,7 +21739,7 @@ async function unzip(path2) {
21735
21739
  }
21736
21740
  }
21737
21741
  async function extractDocx(path2) {
21738
- const mod = await loadOptional("mammoth");
21742
+ const mod = await loadParser("mammoth");
21739
21743
  const lib = mod?.default ?? mod;
21740
21744
  if (!lib || typeof lib.extractRawText !== "function") return null;
21741
21745
  try {
@@ -21746,7 +21750,7 @@ async function extractDocx(path2) {
21746
21750
  }
21747
21751
  }
21748
21752
  async function extractDoc(path2) {
21749
- const mod = await loadOptional(
21753
+ const mod = await loadParser(
21750
21754
  "word-extractor"
21751
21755
  );
21752
21756
  const Ctor = mod && "default" in mod ? mod.default : mod;
@@ -21759,7 +21763,7 @@ async function extractDoc(path2) {
21759
21763
  }
21760
21764
  }
21761
21765
  async function extractPdf(path2) {
21762
- const unpdf = await loadOptional("unpdf");
21766
+ const unpdf = await loadParser("unpdf");
21763
21767
  if (!unpdf || typeof unpdf.getDocumentProxy !== "function") return null;
21764
21768
  try {
21765
21769
  const buf = await readFile(path2);
@@ -22461,8 +22465,17 @@ function titleFromUrl(rawUrl) {
22461
22465
  }
22462
22466
  }
22463
22467
  async function sniffMime(body) {
22468
+ let ft;
22469
+ try {
22470
+ ft = await import("file-type");
22471
+ } catch (err) {
22472
+ console.error(
22473
+ `[latticesql] mime sniffer "file-type" failed to load \u2014 crawl mime detection is degraded (likely a broken/partial install). Reinstall dependencies (\`npm install\`). Cause:`,
22474
+ err
22475
+ );
22476
+ return "";
22477
+ }
22464
22478
  try {
22465
- const ft = await import("file-type");
22466
22479
  const result = await ft.fileTypeFromBuffer(body);
22467
22480
  return result?.mime ?? "";
22468
22481
  } catch {
@@ -22606,6 +22619,45 @@ function labelColumn(cols) {
22606
22619
  const text = Object.keys(cols).find((c) => !STRUCTURAL.has(c) && !c.endsWith("_id"));
22607
22620
  return text ?? null;
22608
22621
  }
22622
+ var TEXT_COL_RE = /\b(TEXT|VARCHAR|CHAR|CLOB|CHARACTER|STRING|NAME|CITEXT)\b/i;
22623
+ async function requiredTextFileColumns(db) {
22624
+ const out = /* @__PURE__ */ new Set();
22625
+ try {
22626
+ if (db.getDialect() === "postgres") {
22627
+ const rows = await allAsyncOrSync(
22628
+ db.adapter,
22629
+ `SELECT column_name AS name, data_type AS type, is_nullable, column_default AS dflt
22630
+ FROM information_schema.columns
22631
+ WHERE table_name = 'files' AND table_schema = current_schema()`
22632
+ );
22633
+ for (const r of rows) {
22634
+ if (String(r.is_nullable).toUpperCase() === "NO" && r.dflt == null && TEXT_COL_RE.test(String(r.type))) {
22635
+ out.add(String(r.name));
22636
+ }
22637
+ }
22638
+ } else {
22639
+ const rows = await allAsyncOrSync(db.adapter, `PRAGMA table_info("files")`);
22640
+ for (const r of rows) {
22641
+ if (Number(r.notnull) === 1 && r.dflt_value == null && Number(r.pk) === 0 && TEXT_COL_RE.test(String(r.type))) {
22642
+ out.add(String(r.name));
22643
+ }
22644
+ }
22645
+ }
22646
+ } catch {
22647
+ }
22648
+ return out;
22649
+ }
22650
+ async function requiredFileDefaults(db, displayName, id, provided) {
22651
+ const required = await requiredTextFileColumns(db);
22652
+ const label = displayName.trim() || "file";
22653
+ const out = {};
22654
+ for (const col of required) {
22655
+ if (STRUCTURAL.has(col)) continue;
22656
+ if (provided[col] != null) continue;
22657
+ out[col] = /slug/i.test(col) ? fileSlug(displayName, id) : label;
22658
+ }
22659
+ return out;
22660
+ }
22609
22661
  async function buildCatalog(db, descriptions) {
22610
22662
  const out = [];
22611
22663
  for (const name of db.getRegisteredTableNames()) {
@@ -22907,9 +22959,19 @@ async function dispatchIngestRoute(req, res, ctx) {
22907
22959
  await rm(tmp, { force: true }).catch(() => void 0);
22908
22960
  }
22909
22961
  const fileId = crypto.randomUUID();
22910
- const { id: id2 } = await createRow(mctx, "files", {
22962
+ const rawFilePath = typeof req.headers["x-filepath"] === "string" && req.headers["x-filepath"] || "";
22963
+ let realPath = "";
22964
+ if (rawFilePath) {
22965
+ try {
22966
+ realPath = decodeURIComponent(rawFilePath);
22967
+ } catch {
22968
+ realPath = rawFilePath;
22969
+ }
22970
+ }
22971
+ const uploadRow = {
22911
22972
  id: fileId,
22912
22973
  ...fileIdentity(name2, fileId),
22974
+ ...realPath ? { path: realPath } : {},
22913
22975
  original_name: name2,
22914
22976
  mime: mime2,
22915
22977
  size_bytes: buf.length,
@@ -22917,6 +22979,10 @@ async function dispatchIngestRoute(req, res, ctx) {
22917
22979
  description: describe(result.text, mime2, name2),
22918
22980
  extraction_status: result.skip ? "skipped" : "extracted",
22919
22981
  ...blob ? { ref_kind: "blob", blob_path: blob.blob_path, sha256: blob.sha256 } : {}
22982
+ };
22983
+ const { id: id2 } = await createRow(mctx, "files", {
22984
+ ...await requiredFileDefaults(ctx.db, name2, fileId, uploadRow),
22985
+ ...uploadRow
22920
22986
  });
22921
22987
  let suggestedLinks = [];
22922
22988
  if (!result.skip) {
@@ -22961,7 +23027,7 @@ async function dispatchIngestRoute(req, res, ctx) {
22961
23027
  }
22962
23028
  }
22963
23029
  const textFileId = crypto.randomUUID();
22964
- const { id: id2 } = await createRow(mctx, "files", {
23030
+ const textRow = {
22965
23031
  id: textFileId,
22966
23032
  ...fileIdentity(title, textFileId),
22967
23033
  original_name: title,
@@ -22971,6 +23037,10 @@ async function dispatchIngestRoute(req, res, ctx) {
22971
23037
  description: describe(content, mime2, title),
22972
23038
  extraction_status: "extracted",
22973
23039
  ...sourceUrl ? { ref_kind: "cloud_ref", ref_uri: sourceUrl, ref_provider: "web" } : {}
23040
+ };
23041
+ const { id: id2 } = await createRow(mctx, "files", {
23042
+ ...await requiredFileDefaults(ctx.db, title, textFileId, textRow),
23043
+ ...textRow
22974
23044
  });
22975
23045
  const suggestedLinks = await enrichOrFail(mctx, ctx.db, id2, content, title, ctx, res);
22976
23046
  if (suggestedLinks === null) return true;
@@ -23002,7 +23072,7 @@ async function dispatchIngestRoute(req, res, ctx) {
23002
23072
  const name = basename10(abs);
23003
23073
  const mime = mimeFor(name);
23004
23074
  const localFileId = crypto.randomUUID();
23005
- const { id } = await createRow(mctx, "files", {
23075
+ const localRow = {
23006
23076
  id: localFileId,
23007
23077
  ...fileIdentity(name, localFileId),
23008
23078
  path: abs,
@@ -23010,6 +23080,10 @@ async function dispatchIngestRoute(req, res, ctx) {
23010
23080
  mime,
23011
23081
  size_bytes: size,
23012
23082
  extraction_status: "pending"
23083
+ };
23084
+ const { id } = await createRow(mctx, "files", {
23085
+ ...await requiredFileDefaults(ctx.db, name, localFileId, localRow),
23086
+ ...localRow
23013
23087
  });
23014
23088
  try {
23015
23089
  const result = await extractSource(ctx.db, abs, mime, name);
package/dist/index.cjs CHANGED
@@ -9607,8 +9607,17 @@ function titleFromUrl(rawUrl) {
9607
9607
  }
9608
9608
  }
9609
9609
  async function sniffMime(body) {
9610
+ let ft;
9611
+ try {
9612
+ ft = await import("file-type");
9613
+ } catch (err) {
9614
+ console.error(
9615
+ `[latticesql] mime sniffer "file-type" failed to load \u2014 crawl mime detection is degraded (likely a broken/partial install). Reinstall dependencies (\`npm install\`). Cause:`,
9616
+ err
9617
+ );
9618
+ return "";
9619
+ }
9610
9620
  try {
9611
- const ft = await import("file-type");
9612
9621
  const result = await ft.fileTypeFromBuffer(body);
9613
9622
  return result?.mime ?? "";
9614
9623
  } catch {
package/dist/index.js CHANGED
@@ -9472,8 +9472,17 @@ function titleFromUrl(rawUrl) {
9472
9472
  }
9473
9473
  }
9474
9474
  async function sniffMime(body) {
9475
+ let ft;
9476
+ try {
9477
+ ft = await import("file-type");
9478
+ } catch (err) {
9479
+ console.error(
9480
+ `[latticesql] mime sniffer "file-type" failed to load \u2014 crawl mime detection is degraded (likely a broken/partial install). Reinstall dependencies (\`npm install\`). Cause:`,
9481
+ err
9482
+ );
9483
+ return "";
9484
+ }
9475
9485
  try {
9476
- const ft = await import("file-type");
9477
9486
  const result = await ft.fileTypeFromBuffer(body);
9478
9487
  return result?.mime ?? "";
9479
9488
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "latticesql",
3
- "version": "2.2.3",
3
+ "version": "2.3.0",
4
4
  "description": "Persistent structured memory for AI agent systems — pluggable SQLite or Postgres backend, LLM context bridge",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -43,10 +43,16 @@
43
43
  "prepublishOnly": "npm run check:generic && npm run build && npm run typecheck && npm test"
44
44
  },
45
45
  "dependencies": {
46
+ "@anthropic-ai/sdk": "^0.71.2",
46
47
  "@mozilla/readability": "^0.5.0",
47
48
  "@scarf/scarf": "^1.4.0",
49
+ "fflate": "^0.8.3",
50
+ "file-type": "^19.6.0",
48
51
  "jsdom": "^25.0.1",
52
+ "mammoth": "^1.12.0",
53
+ "unpdf": "^1.6.2",
49
54
  "uuid": "^13.0.0",
55
+ "word-extractor": "^1.0.4",
50
56
  "yaml": "^2.8.3"
51
57
  },
52
58
  "scarfSettings": {
@@ -56,18 +62,11 @@
56
62
  "better-sqlite3": ">=11 <13"
57
63
  },
58
64
  "optionalDependencies": {
59
- "@anthropic-ai/sdk": "^0.71.2",
60
- "fflate": "^0.8.3",
61
- "file-type": "^19.6.0",
62
- "mammoth": "^1.12.0",
63
65
  "pg": "^8.11.0",
64
66
  "playwright": "^1.48.0",
65
- "sharp": "^0.33.5",
66
- "unpdf": "^1.6.2",
67
- "word-extractor": "^1.0.4"
67
+ "sharp": "^0.33.5"
68
68
  },
69
69
  "devDependencies": {
70
- "@anthropic-ai/sdk": "^0.71.0",
71
70
  "@eslint/js": "^9.0.0",
72
71
  "@playwright/test": "^1.48.0",
73
72
  "@types/better-sqlite3": "^7.6.12",