sensemaking 0.18.0 → 0.18.2
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/README.md +1 -1
- package/dist/cjs/cli/status.js +20 -14
- package/dist/cjs/cli/status.js.map +1 -1
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/output/column-hint.js +8 -7
- package/dist/cjs/output/column-hint.js.map +1 -1
- package/dist/cjs/scan/reparse.d.cts +6 -1
- package/dist/cjs/scan/reparse.d.ts +6 -1
- package/dist/cjs/scan/reparse.js +375 -48
- package/dist/cjs/scan/reparse.js.map +1 -1
- package/dist/cjs/scan/worker-error.d.cts +9 -0
- package/dist/cjs/scan/worker-error.d.ts +9 -0
- package/dist/cjs/scan/worker-error.js +44 -0
- package/dist/cjs/scan/worker-error.js.map +1 -0
- package/dist/cjs/store/duckdb/reconcile.js +8 -3
- package/dist/cjs/store/duckdb/reconcile.js.map +1 -1
- package/dist/cjs/store/index.d.cts +1 -0
- package/dist/cjs/store/index.d.ts +1 -0
- package/dist/cjs/store/index.js +15 -2
- package/dist/cjs/store/index.js.map +1 -1
- package/dist/cjs/store/sqlite/reconcile.js +13 -8
- package/dist/cjs/store/sqlite/reconcile.js.map +1 -1
- package/dist/cjs/watch.js +77 -58
- package/dist/cjs/watch.js.map +1 -1
- package/dist/cjs/workers/parse.d.cts +18 -0
- package/dist/cjs/workers/parse.d.ts +18 -0
- package/dist/cjs/workers/parse.js +43 -0
- package/dist/cjs/workers/parse.js.map +1 -0
- package/dist/esm/cli/status.js +6 -3
- package/dist/esm/cli/status.js.map +1 -1
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/output/column-hint.js +8 -7
- package/dist/esm/output/column-hint.js.map +1 -1
- package/dist/esm/scan/reparse.d.ts +6 -1
- package/dist/esm/scan/reparse.js +108 -8
- package/dist/esm/scan/reparse.js.map +1 -1
- package/dist/esm/scan/worker-error.d.ts +9 -0
- package/dist/esm/scan/worker-error.js +19 -0
- package/dist/esm/scan/worker-error.js.map +1 -0
- package/dist/esm/store/duckdb/reconcile.js +1 -1
- package/dist/esm/store/duckdb/reconcile.js.map +1 -1
- package/dist/esm/store/index.d.ts +1 -0
- package/dist/esm/store/index.js +14 -2
- package/dist/esm/store/index.js.map +1 -1
- package/dist/esm/store/sqlite/reconcile.js +1 -1
- package/dist/esm/store/sqlite/reconcile.js.map +1 -1
- package/dist/esm/watch.js +30 -17
- package/dist/esm/watch.js.map +1 -1
- package/dist/esm/workers/parse.d.ts +18 -0
- package/dist/esm/workers/parse.js +25 -0
- package/dist/esm/workers/parse.js.map +1 -0
- package/package.json +3 -1
- package/skills/sense-setup/SKILL.md +1 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Config, FeatureName } from '../config/index.js';
|
|
2
|
+
import type { ParsedDoc } from '../scan/index.js';
|
|
3
|
+
import type { FileStat } from '../scan/list.js';
|
|
4
|
+
import type { WorkerErrorPayload } from '../scan/worker-error.js';
|
|
5
|
+
export interface ParseWorkerData {
|
|
6
|
+
cfg: Config;
|
|
7
|
+
featureNames: FeatureName[];
|
|
8
|
+
}
|
|
9
|
+
export type ParseTask = FileStat;
|
|
10
|
+
export type ParseTaskResult = {
|
|
11
|
+
ok: true;
|
|
12
|
+
doc: ParsedDoc;
|
|
13
|
+
warnings: string[];
|
|
14
|
+
} | {
|
|
15
|
+
ok: false;
|
|
16
|
+
error: WorkerErrorPayload;
|
|
17
|
+
};
|
|
18
|
+
export default function parseTask(file: ParseTask): ParseTaskResult;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "default", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return parseTask;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
var _tinypool = /*#__PURE__*/ _interop_require_default(require("tinypool"));
|
|
12
|
+
var _indexts = require("../features/index.js");
|
|
13
|
+
var _indexts1 = require("../scan/index.js");
|
|
14
|
+
var _reparsets = require("../scan/reparse.js");
|
|
15
|
+
var _workererrorts = require("../scan/worker-error.js");
|
|
16
|
+
function _interop_require_default(obj) {
|
|
17
|
+
return obj && obj.__esModule ? obj : {
|
|
18
|
+
default: obj
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
// Read once per worker, not per task.
|
|
22
|
+
var _Tinypool_workerData = _tinypool.default.workerData, cfg = _Tinypool_workerData.cfg, featureNames = _Tinypool_workerData.featureNames;
|
|
23
|
+
// Filtering the registry (rather than mapping the names) keeps registry order, which is the
|
|
24
|
+
// order `extracted` keys land in on the serial path.
|
|
25
|
+
var selected = _indexts.FEATURES.filter(function(feature) {
|
|
26
|
+
return featureNames.includes(feature.name);
|
|
27
|
+
});
|
|
28
|
+
function parseTask(file) {
|
|
29
|
+
try {
|
|
30
|
+
var _parseFile = (0, _indexts1.parseFile)(file, (0, _reparsets.featuresForFile)(selected, cfg, file), cfg), doc = _parseFile.doc, warnings = _parseFile.warnings;
|
|
31
|
+
return {
|
|
32
|
+
ok: true,
|
|
33
|
+
doc: doc,
|
|
34
|
+
warnings: warnings
|
|
35
|
+
};
|
|
36
|
+
} catch (err) {
|
|
37
|
+
return {
|
|
38
|
+
ok: false,
|
|
39
|
+
error: (0, _workererrorts.serializeError)(err)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/workers/parse.ts"],"sourcesContent":["import Tinypool from 'tinypool';\nimport type { Config, FeatureName } from '../config/index.ts';\nimport { FEATURES } from '../features/index.ts';\nimport type { ParsedDoc } from '../scan/index.ts';\nimport { parseFile } from '../scan/index.ts';\nimport type { FileStat } from '../scan/list.ts';\nimport { featuresForFile } from '../scan/reparse.ts';\nimport type { WorkerErrorPayload } from '../scan/worker-error.ts';\nimport { serializeError } from '../scan/worker-error.ts';\n\n// Constant for the whole dispatch, so it crosses once per worker instead of once per task.\n// A Feature carries closures and cannot cross the thread boundary; its name can, and the\n// registry on this side resolves it back, which keeps the caller's selection intact.\nexport interface ParseWorkerData {\n cfg: Config;\n featureNames: FeatureName[];\n}\n\n// tinypool's task, in and out. The task itself is one FileStat. Result carries only what\n// parseFile already returns -- extracted text and per-feature values, never the mdast tree.\nexport type ParseTask = FileStat;\n\nexport type ParseTaskResult = { ok: true; doc: ParsedDoc; warnings: string[] } | { ok: false; error: WorkerErrorPayload };\n\n// Read once per worker, not per task.\nconst { cfg, featureNames } = Tinypool.workerData as ParseWorkerData;\n// Filtering the registry (rather than mapping the names) keeps registry order, which is the\n// order `extracted` keys land in on the serial path.\nconst selected = FEATURES.filter((feature) => featureNames.includes(feature.name));\n\nexport default function parseTask(file: ParseTask): ParseTaskResult {\n try {\n const { doc, warnings } = parseFile(file, featuresForFile(selected, cfg, file), cfg);\n return { ok: true, doc, warnings };\n } catch (err) {\n return { ok: false, error: serializeError(err) };\n }\n}\n"],"names":["parseTask","Tinypool","workerData","cfg","featureNames","selected","FEATURES","filter","feature","includes","name","file","parseFile","featuresForFile","doc","warnings","ok","err","error","serializeError"],"mappings":";;;;+BA8BA;;;eAAwBA;;;+DA9BH;uBAEI;wBAEC;yBAEM;6BAED;;;;;;AAgB/B,sCAAsC;AACtC,IAA8BC,uBAAAA,iBAAQ,CAACC,UAAU,EAAzCC,MAAsBF,qBAAtBE,KAAKC,eAAiBH,qBAAjBG;AACb,4FAA4F;AAC5F,qDAAqD;AACrD,IAAMC,WAAWC,iBAAQ,CAACC,MAAM,CAAC,SAACC;WAAYJ,aAAaK,QAAQ,CAACD,QAAQE,IAAI;;AAEjE,SAASV,UAAUW,IAAe;IAC/C,IAAI;QACF,IAA0BC,aAAAA,IAAAA,mBAAS,EAACD,MAAME,IAAAA,0BAAe,EAACR,UAAUF,KAAKQ,OAAOR,MAAxEW,MAAkBF,WAAlBE,KAAKC,WAAaH,WAAbG;QACb,OAAO;YAAEC,IAAI;YAAMF,KAAAA;YAAKC,UAAAA;QAAS;IACnC,EAAE,OAAOE,KAAK;QACZ,OAAO;YAAED,IAAI;YAAOE,OAAOC,IAAAA,6BAAc,EAACF;QAAK;IACjD;AACF"}
|
package/dist/esm/cli/status.js
CHANGED
|
@@ -5,7 +5,7 @@ import { languageDistribution } from '../embed/distribution.js';
|
|
|
5
5
|
import { probeReachable } from '../embed/http.js';
|
|
6
6
|
import { isDownloadable, MODEL_FILENAMES, modelDir, modelPresent, readLanguages } from '../embed/store.js';
|
|
7
7
|
import { featuresLine, presetsLines, stringifyJson } from '../output/output.js';
|
|
8
|
-
import { docCount, getMeta, openStore
|
|
8
|
+
import { docCount, getMeta, openStore } from '../store/index.js';
|
|
9
9
|
import { USAGE } from './index.js';
|
|
10
10
|
import { CONFIG, FORMAT, formatOf, parse, printWarnings } from './shared.js';
|
|
11
11
|
// status names what you cannot read off the config file: locations sense chose, values it
|
|
@@ -68,6 +68,9 @@ const status = async (ctx)=>{
|
|
|
68
68
|
const declaredLanguages = staticLanguages && staticLanguages.length > 0 ? staticLanguages : null;
|
|
69
69
|
const detectedLanguages = (_ref = await languageDistribution(store)) !== null && _ref !== void 0 ? _ref : null;
|
|
70
70
|
const heartbeat = await getMeta(store, 'watch_heartbeat');
|
|
71
|
+
// Written by the store's open() on every open, so it is this cache's true version on either
|
|
72
|
+
// store (sqlite 18, duckdb 2); a version mismatch already triggered a rebuild before this read.
|
|
73
|
+
const cacheSchema = await getMeta(store, 'schema_version');
|
|
71
74
|
// Derived at open() (F): 3x the largest reconcile this cache has ever held its write
|
|
72
75
|
// transaction for, floored at 30s -- read back from the connection rather than
|
|
73
76
|
// recomputed here, so this always reports what open() actually set. The pragma is
|
|
@@ -86,7 +89,7 @@ const status = async (ctx)=>{
|
|
|
86
89
|
tree: cfg.baseDir,
|
|
87
90
|
cache: dirname(dbPath),
|
|
88
91
|
db: dbPath,
|
|
89
|
-
cacheSchema
|
|
92
|
+
cacheSchema,
|
|
90
93
|
docs: await docCount(store),
|
|
91
94
|
unparseableFrontmatter: await parseErrorCount(store),
|
|
92
95
|
features: features.on,
|
|
@@ -117,7 +120,7 @@ const status = async (ctx)=>{
|
|
|
117
120
|
// A load-time stderr warning has scrolled away by the time anyone looks here.
|
|
118
121
|
if (cfg.unknownKeys) console.log(` unknown keys, ignored: ${cfg.unknownKeys.join(', ')}`);
|
|
119
122
|
console.log(`tree: ${result.tree}`);
|
|
120
|
-
console.log(`cache: ${result.cache} (schema ${
|
|
123
|
+
console.log(`cache: ${result.cache} (schema ${result.cacheSchema}; delete this directory to reset)`);
|
|
121
124
|
console.log(`docs: ${result.docs}${result.unparseableFrontmatter > 0 ? ` (${result.unparseableFrontmatter} with unparseable frontmatter: WHERE _parse_error IS NOT NULL)` : ''}`);
|
|
122
125
|
console.log('');
|
|
123
126
|
console.log(featuresLine(features));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/status.ts"],"sourcesContent":["import { dirname } from 'node:path';\nimport { presetCoverage } from '../commands/status.ts';\nimport { embedConfig, featureStates, SUPPORTED_CONFIG_VERSION } from '../config/index.ts';\nimport { languageDistribution } from '../embed/distribution.ts';\nimport { probeReachable } from '../embed/http.ts';\nimport { isDownloadable, MODEL_FILENAMES, modelDir, modelPresent, readLanguages } from '../embed/store.ts';\nimport { featuresLine, presetsLines, stringifyJson } from '../output/output.ts';\nimport { docCount, getMeta, openStore, SCHEMA_VERSION } from '../store/index.ts';\nimport type { Store } from '../store/types.ts';\nimport { USAGE } from './index.ts';\nimport { CONFIG, FORMAT, formatOf, parse, printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\n// status names what you cannot read off the config file: locations sense chose, values it\n// derived, state it stored. Config content is not echoed -- the config line says which file to\n// read, and duplicating its globs here would create two places that disagree after an edit.\n// The exception is the embed block's url and key, whose effect is remote and otherwise invisible.\n\n// Vectors are computed on the first semantic query, not at reconcile, so a tree can have a\n// semantic-on preset with nothing embedded yet -- reported here because a fast reconcile on\n// such a tree otherwise reads as \"the feature isn't on\".\nasync function vectorState(store: Store): Promise<{ embedded: number; pending: number } | null> {\n try {\n const stmt = await store.prepare('SELECT COUNT(vector) AS embedded, COUNT(*) - COUNT(vector) AS pending FROM embeddings');\n return (await stmt.get()) as { embedded: number; pending: number };\n } catch {\n return null;\n }\n}\n\nasync function parseErrorCount(store: Store): Promise<number> {\n const stmt = await store.prepare(`SELECT COUNT(*) AS n FROM frontmatter WHERE \"_parse_error\" IS NOT NULL`);\n return ((await stmt.get()) as { n: number }).n;\n}\n\n// A URL can carry credentials in its userinfo (https://user:pass@host/v1). status output gets\n// pasted into issues and transcripts, so it is always safe to paste.\nexport function redactUrl(url: string): string {\n try {\n const parsed = new URL(url);\n if (!parsed.username && !parsed.password) return url;\n parsed.username = '';\n parsed.password = '';\n return `${parsed.toString().replace(/\\/$/, '')} (credentials redacted)`;\n } catch {\n return url;\n }\n}\n\n// A stopped Ollama/LM Studio/Cohere endpoint prints identically to a running one otherwise; any\n// HTTP response counts as reachable (probeReachable), so this only distinguishes up from down.\nasync function embedReachable(e: { provider: string; url?: string }): Promise<boolean | null> {\n if (e.provider === 'static' || (e.provider === 'openai' && !e.url)) return null;\n const base = (e.url ?? 'https://api.cohere.com').replace(/\\/+$/, '');\n return probeReachable(e.provider === 'openai' ? `${base}/models` : base);\n}\n\nconst status: Command = async (ctx) => {\n const { values } = parse(ctx.argv, `usage: ${ctx.name} ${USAGE.status}`, { ...FORMAT, ...CONFIG });\n const format = formatOf(values);\n const cfg = ctx.resolveConfig(values.config as string | undefined);\n const { store, dbPath, warnings } = await openStore(cfg);\n printWarnings(warnings);\n\n const features = featureStates(cfg);\n const e = embedConfig(cfg);\n const hasModel = e ? modelPresent(cfg) : false;\n const reachable = e ? await embedReachable(e) : null;\n const vectors = e ? await vectorState(store) : null;\n const presets = await presetCoverage(store, cfg);\n const staticLanguages = e && e.provider === 'static' ? readLanguages(e.model) : undefined;\n const declaredLanguages = staticLanguages && staticLanguages.length > 0 ? staticLanguages : null;\n const detectedLanguages = (await languageDistribution(store)) ?? null;\n const heartbeat = await getMeta(store, 'watch_heartbeat');\n // Derived at open() (F): 3x the largest reconcile this cache has ever held its write\n // transaction for, floored at 30s -- read back from the connection rather than\n // recomputed here, so this always reports what open() actually set. The pragma is\n // sqlite-engine-specific; other stores have no such setting and report null.\n const busyTimeoutMs = store.name === 'sqlite' ? ((await (await store.prepare('PRAGMA busy_timeout')).get()) as { timeout: number }).timeout : null;\n // The env var holds the token; only its name and whether it is set are ever reported.\n const keyEnv = e?.key ? { name: e.key, set: (process.env[e.key] ?? '') !== '' } : null;\n const result = {\n config: cfg.configPath,\n configVersion: SUPPORTED_CONFIG_VERSION,\n migratedFrom: cfg.migratedFrom ?? null,\n unknownConfigKeys: cfg.unknownKeys ?? null,\n tree: cfg.baseDir,\n cache: dirname(dbPath),\n db: dbPath,\n cacheSchema: SCHEMA_VERSION,\n docs: await docCount(store),\n unparseableFrontmatter: await parseErrorCount(store),\n features: features.on,\n featuresOff: features.off,\n embed: e ? { provider: e.provider, model: e.model, dir: e.provider === 'static' ? modelDir(e.model) : null, url: e.url ? redactUrl(e.url) : null, reachable, keyEnv, downloaded: hasModel, languages: declaredLanguages, detectedLanguages, ...(vectors ?? {}) } : null,\n presets,\n queries: Object.keys(cfg.queries ?? {}).length,\n watcherPid: await getMeta(store, 'watch_pid'),\n watcherHeartbeatSecondsAgo: heartbeat ? Math.round((Date.now() - Date.parse(heartbeat)) / 1000) : null,\n busyTimeoutMs,\n };\n\n if (format === 'json') {\n console.log(stringifyJson(result, 2));\n } else {\n const migrated = cfg.migratedFrom !== undefined ? `, migrated from v${cfg.migratedFrom} on this run` : '';\n console.log(`config: ${result.config} (v${SUPPORTED_CONFIG_VERSION}${migrated})`);\n // A load-time stderr warning has scrolled away by the time anyone looks here.\n if (cfg.unknownKeys) console.log(` unknown keys, ignored: ${cfg.unknownKeys.join(', ')}`);\n console.log(`tree: ${result.tree}`);\n console.log(`cache: ${result.cache} (schema ${SCHEMA_VERSION}; delete this directory to reset)`);\n console.log(`docs: ${result.docs}${result.unparseableFrontmatter > 0 ? ` (${result.unparseableFrontmatter} with unparseable frontmatter: WHERE _parse_error IS NOT NULL)` : ''}`);\n console.log('');\n console.log(featuresLine(features));\n // One authoritative block, off states included: an absent model reads as a degraded search,\n // not as an empty corpus.\n if (!e) console.log('embed: off (no preset asks for vectors)');\n else {\n console.log(`embed: ${e.provider} ${e.model}`);\n if (e.provider === 'static') console.log(` model: ${modelDir(e.model)} (${hasModel ? 'present' : `missing, ${isDownloadable(e.model) ? `fetches automatically at first search, or run \\`${ctx.name} download\\` to prefetch` : `no ${MODEL_FILENAMES} in that directory`}`})`);\n if (declaredLanguages) console.log(` languages: ${declaredLanguages.join(', ')} (declared by the model card)`);\n // Cohere defaults to api.cohere.com with no url in the config; show it anyway so the\n // reachability probe below has something to name.\n if (e.url || reachable !== null) {\n const shown = e.url ? redactUrl(e.url) : 'https://api.cohere.com (default)';\n console.log(` url: ${shown}${reachable === null ? '' : reachable ? ' (reachable)' : ' (unreachable)'}`);\n }\n if (keyEnv) console.log(` key: env ${keyEnv.name} (${keyEnv.set ? 'set' : 'NOT SET'})`);\n if (!hasModel) console.log(' vectors: off until the model is present; search runs on words and links');\n else if (vectors) console.log(` vectors: ${vectors.embedded} embedded, ${vectors.pending} pending (embedded on the first semantic search)`);\n if (detectedLanguages) {\n const total = Object.values(detectedLanguages).reduce((a, b) => a + b, 0);\n const breakdown = Object.entries(detectedLanguages)\n .sort((a, b) => b[1] - a[1])\n .map(([code, n]) => `${code} ${Math.round((n / total) * 100)}%`)\n .join(', ');\n console.log(` detected: ${breakdown} (${total} chunks classified)`);\n }\n }\n console.log('');\n for (const line of presetsLines(presets)) console.log(line);\n console.log('');\n console.log(`queries: ${result.queries} saved (${ctx.name} --list)`);\n const pid = result.watcherPid ? `pid ${result.watcherPid}, ` : '';\n console.log(result.watcherHeartbeatSecondsAgo === null ? 'watcher: none' : `watcher: ${pid}last heartbeat ${result.watcherHeartbeatSecondsAgo}s ago`);\n if (result.busyTimeoutMs !== null) console.log(`sqlite: busy_timeout ${result.busyTimeoutMs}ms (derived: 3x the largest reconcile this cache has recorded, floored at 30000ms)`);\n }\n await store.close();\n};\nexport default status;\n"],"names":["dirname","presetCoverage","embedConfig","featureStates","SUPPORTED_CONFIG_VERSION","languageDistribution","probeReachable","isDownloadable","MODEL_FILENAMES","modelDir","modelPresent","readLanguages","featuresLine","presetsLines","stringifyJson","docCount","getMeta","openStore","SCHEMA_VERSION","USAGE","CONFIG","FORMAT","formatOf","parse","printWarnings","vectorState","store","stmt","prepare","get","parseErrorCount","n","redactUrl","url","parsed","URL","username","password","toString","replace","embedReachable","e","provider","base","status","ctx","process","cfg","values","argv","name","format","resolveConfig","config","dbPath","warnings","features","hasModel","reachable","vectors","presets","staticLanguages","model","undefined","declaredLanguages","length","detectedLanguages","heartbeat","busyTimeoutMs","timeout","keyEnv","key","set","env","result","configPath","configVersion","migratedFrom","unknownConfigKeys","unknownKeys","tree","baseDir","cache","db","cacheSchema","docs","unparseableFrontmatter","on","featuresOff","off","embed","dir","downloaded","languages","queries","Object","keys","watcherPid","watcherHeartbeatSecondsAgo","Math","round","Date","now","console","log","migrated","join","shown","embedded","pending","total","reduce","a","b","breakdown","entries","sort","map","code","line","pid","close"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/status.ts"],"sourcesContent":["import { dirname } from 'node:path';\nimport { presetCoverage } from '../commands/status.ts';\nimport { embedConfig, featureStates, SUPPORTED_CONFIG_VERSION } from '../config/index.ts';\nimport { languageDistribution } from '../embed/distribution.ts';\nimport { probeReachable } from '../embed/http.ts';\nimport { isDownloadable, MODEL_FILENAMES, modelDir, modelPresent, readLanguages } from '../embed/store.ts';\nimport { featuresLine, presetsLines, stringifyJson } from '../output/output.ts';\nimport { docCount, getMeta, openStore } from '../store/index.ts';\nimport type { Store } from '../store/types.ts';\nimport { USAGE } from './index.ts';\nimport { CONFIG, FORMAT, formatOf, parse, printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\n// status names what you cannot read off the config file: locations sense chose, values it\n// derived, state it stored. Config content is not echoed -- the config line says which file to\n// read, and duplicating its globs here would create two places that disagree after an edit.\n// The exception is the embed block's url and key, whose effect is remote and otherwise invisible.\n\n// Vectors are computed on the first semantic query, not at reconcile, so a tree can have a\n// semantic-on preset with nothing embedded yet -- reported here because a fast reconcile on\n// such a tree otherwise reads as \"the feature isn't on\".\nasync function vectorState(store: Store): Promise<{ embedded: number; pending: number } | null> {\n try {\n const stmt = await store.prepare('SELECT COUNT(vector) AS embedded, COUNT(*) - COUNT(vector) AS pending FROM embeddings');\n return (await stmt.get()) as { embedded: number; pending: number };\n } catch {\n return null;\n }\n}\n\nasync function parseErrorCount(store: Store): Promise<number> {\n const stmt = await store.prepare(`SELECT COUNT(*) AS n FROM frontmatter WHERE \"_parse_error\" IS NOT NULL`);\n return ((await stmt.get()) as { n: number }).n;\n}\n\n// A URL can carry credentials in its userinfo (https://user:pass@host/v1). status output gets\n// pasted into issues and transcripts, so it is always safe to paste.\nexport function redactUrl(url: string): string {\n try {\n const parsed = new URL(url);\n if (!parsed.username && !parsed.password) return url;\n parsed.username = '';\n parsed.password = '';\n return `${parsed.toString().replace(/\\/$/, '')} (credentials redacted)`;\n } catch {\n return url;\n }\n}\n\n// A stopped Ollama/LM Studio/Cohere endpoint prints identically to a running one otherwise; any\n// HTTP response counts as reachable (probeReachable), so this only distinguishes up from down.\nasync function embedReachable(e: { provider: string; url?: string }): Promise<boolean | null> {\n if (e.provider === 'static' || (e.provider === 'openai' && !e.url)) return null;\n const base = (e.url ?? 'https://api.cohere.com').replace(/\\/+$/, '');\n return probeReachable(e.provider === 'openai' ? `${base}/models` : base);\n}\n\nconst status: Command = async (ctx) => {\n const { values } = parse(ctx.argv, `usage: ${ctx.name} ${USAGE.status}`, { ...FORMAT, ...CONFIG });\n const format = formatOf(values);\n const cfg = ctx.resolveConfig(values.config as string | undefined);\n const { store, dbPath, warnings } = await openStore(cfg);\n printWarnings(warnings);\n\n const features = featureStates(cfg);\n const e = embedConfig(cfg);\n const hasModel = e ? modelPresent(cfg) : false;\n const reachable = e ? await embedReachable(e) : null;\n const vectors = e ? await vectorState(store) : null;\n const presets = await presetCoverage(store, cfg);\n const staticLanguages = e && e.provider === 'static' ? readLanguages(e.model) : undefined;\n const declaredLanguages = staticLanguages && staticLanguages.length > 0 ? staticLanguages : null;\n const detectedLanguages = (await languageDistribution(store)) ?? null;\n const heartbeat = await getMeta(store, 'watch_heartbeat');\n // Written by the store's open() on every open, so it is this cache's true version on either\n // store (sqlite 18, duckdb 2); a version mismatch already triggered a rebuild before this read.\n const cacheSchema = await getMeta(store, 'schema_version');\n // Derived at open() (F): 3x the largest reconcile this cache has ever held its write\n // transaction for, floored at 30s -- read back from the connection rather than\n // recomputed here, so this always reports what open() actually set. The pragma is\n // sqlite-engine-specific; other stores have no such setting and report null.\n const busyTimeoutMs = store.name === 'sqlite' ? ((await (await store.prepare('PRAGMA busy_timeout')).get()) as { timeout: number }).timeout : null;\n // The env var holds the token; only its name and whether it is set are ever reported.\n const keyEnv = e?.key ? { name: e.key, set: (process.env[e.key] ?? '') !== '' } : null;\n const result = {\n config: cfg.configPath,\n configVersion: SUPPORTED_CONFIG_VERSION,\n migratedFrom: cfg.migratedFrom ?? null,\n unknownConfigKeys: cfg.unknownKeys ?? null,\n tree: cfg.baseDir,\n cache: dirname(dbPath),\n db: dbPath,\n cacheSchema,\n docs: await docCount(store),\n unparseableFrontmatter: await parseErrorCount(store),\n features: features.on,\n featuresOff: features.off,\n embed: e ? { provider: e.provider, model: e.model, dir: e.provider === 'static' ? modelDir(e.model) : null, url: e.url ? redactUrl(e.url) : null, reachable, keyEnv, downloaded: hasModel, languages: declaredLanguages, detectedLanguages, ...(vectors ?? {}) } : null,\n presets,\n queries: Object.keys(cfg.queries ?? {}).length,\n watcherPid: await getMeta(store, 'watch_pid'),\n watcherHeartbeatSecondsAgo: heartbeat ? Math.round((Date.now() - Date.parse(heartbeat)) / 1000) : null,\n busyTimeoutMs,\n };\n\n if (format === 'json') {\n console.log(stringifyJson(result, 2));\n } else {\n const migrated = cfg.migratedFrom !== undefined ? `, migrated from v${cfg.migratedFrom} on this run` : '';\n console.log(`config: ${result.config} (v${SUPPORTED_CONFIG_VERSION}${migrated})`);\n // A load-time stderr warning has scrolled away by the time anyone looks here.\n if (cfg.unknownKeys) console.log(` unknown keys, ignored: ${cfg.unknownKeys.join(', ')}`);\n console.log(`tree: ${result.tree}`);\n console.log(`cache: ${result.cache} (schema ${result.cacheSchema}; delete this directory to reset)`);\n console.log(`docs: ${result.docs}${result.unparseableFrontmatter > 0 ? ` (${result.unparseableFrontmatter} with unparseable frontmatter: WHERE _parse_error IS NOT NULL)` : ''}`);\n console.log('');\n console.log(featuresLine(features));\n // One authoritative block, off states included: an absent model reads as a degraded search,\n // not as an empty corpus.\n if (!e) console.log('embed: off (no preset asks for vectors)');\n else {\n console.log(`embed: ${e.provider} ${e.model}`);\n if (e.provider === 'static') console.log(` model: ${modelDir(e.model)} (${hasModel ? 'present' : `missing, ${isDownloadable(e.model) ? `fetches automatically at first search, or run \\`${ctx.name} download\\` to prefetch` : `no ${MODEL_FILENAMES} in that directory`}`})`);\n if (declaredLanguages) console.log(` languages: ${declaredLanguages.join(', ')} (declared by the model card)`);\n // Cohere defaults to api.cohere.com with no url in the config; show it anyway so the\n // reachability probe below has something to name.\n if (e.url || reachable !== null) {\n const shown = e.url ? redactUrl(e.url) : 'https://api.cohere.com (default)';\n console.log(` url: ${shown}${reachable === null ? '' : reachable ? ' (reachable)' : ' (unreachable)'}`);\n }\n if (keyEnv) console.log(` key: env ${keyEnv.name} (${keyEnv.set ? 'set' : 'NOT SET'})`);\n if (!hasModel) console.log(' vectors: off until the model is present; search runs on words and links');\n else if (vectors) console.log(` vectors: ${vectors.embedded} embedded, ${vectors.pending} pending (embedded on the first semantic search)`);\n if (detectedLanguages) {\n const total = Object.values(detectedLanguages).reduce((a, b) => a + b, 0);\n const breakdown = Object.entries(detectedLanguages)\n .sort((a, b) => b[1] - a[1])\n .map(([code, n]) => `${code} ${Math.round((n / total) * 100)}%`)\n .join(', ');\n console.log(` detected: ${breakdown} (${total} chunks classified)`);\n }\n }\n console.log('');\n for (const line of presetsLines(presets)) console.log(line);\n console.log('');\n console.log(`queries: ${result.queries} saved (${ctx.name} --list)`);\n const pid = result.watcherPid ? `pid ${result.watcherPid}, ` : '';\n console.log(result.watcherHeartbeatSecondsAgo === null ? 'watcher: none' : `watcher: ${pid}last heartbeat ${result.watcherHeartbeatSecondsAgo}s ago`);\n if (result.busyTimeoutMs !== null) console.log(`sqlite: busy_timeout ${result.busyTimeoutMs}ms (derived: 3x the largest reconcile this cache has recorded, floored at 30000ms)`);\n }\n await store.close();\n};\nexport default status;\n"],"names":["dirname","presetCoverage","embedConfig","featureStates","SUPPORTED_CONFIG_VERSION","languageDistribution","probeReachable","isDownloadable","MODEL_FILENAMES","modelDir","modelPresent","readLanguages","featuresLine","presetsLines","stringifyJson","docCount","getMeta","openStore","USAGE","CONFIG","FORMAT","formatOf","parse","printWarnings","vectorState","store","stmt","prepare","get","parseErrorCount","n","redactUrl","url","parsed","URL","username","password","toString","replace","embedReachable","e","provider","base","status","ctx","process","cfg","values","argv","name","format","resolveConfig","config","dbPath","warnings","features","hasModel","reachable","vectors","presets","staticLanguages","model","undefined","declaredLanguages","length","detectedLanguages","heartbeat","cacheSchema","busyTimeoutMs","timeout","keyEnv","key","set","env","result","configPath","configVersion","migratedFrom","unknownConfigKeys","unknownKeys","tree","baseDir","cache","db","docs","unparseableFrontmatter","on","featuresOff","off","embed","dir","downloaded","languages","queries","Object","keys","watcherPid","watcherHeartbeatSecondsAgo","Math","round","Date","now","console","log","migrated","join","shown","embedded","pending","total","reduce","a","b","breakdown","entries","sort","map","code","line","pid","close"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config/types.ts"],"sourcesContent":["import type { SignalWeights } from './signals.ts';\n\nexport const CONFIG_FILENAME = 'sense.config.json';\nexport const STATE_DIR = '.sense';\n\n// Highest sense.config.json `version` this build understands. Older versions auto-migrate on load.\nexport const SUPPORTED_CONFIG_VERSION = 5;\n\n// Each feature owns its tables, parse-time extraction, and reconcile step; commands degrade when one is off.\n// links/sections/tags/rank are opt-out toggles in the top-level `features` block; embed is not a\n// member of that block -- it is on exactly when an `embed` block names a model.\nexport const FEATURE_NAMES = ['links', 'sections', 'tags', 'rank', 'embed'] as const;\nexport type FeatureName = (typeof FEATURE_NAMES)[number];\n\n// `embed` names the model and gives the tree vectors at all; a preset's `signals` says which\n// engines that scope's searches use.\nexport interface EmbedConfig {\n model?: string;\n provider?: 'static' | 'openai' | 'cohere';\n url?: string; // openai provider: OpenAI-compatible base URL, e.g. http://localhost:11434/v1\n key?: string; // openai/cohere provider: name of the env var holding the bearer token, if any\n languages?: string[]; // owner-declared model languages; enables the language-fit check where no card exists\n chunkTokens?: number; // chunk size ceiling in estimated tokens for small-context models; default 500\n}\n\nexport const DEFAULT_EMBED_MODEL = 'minishlab/potion-retrieval-32M';\n\n// A named, self-contained file-selection scope. include/exclude are globby patterns resolved\n// relative to the config file. No inheritance between presets -- every field a preset wants\n// it declares itself. Presets may overlap freely; they are views, not partitions.\nexport interface Preset {\n include: string[];\n exclude?: string[];\n k?: number; // result count for `search` scoped to this preset; default 10\n // Exhaustive when present: every signal this preset's searches compose, each mapped to\n // its RRF weight (a finite number > 0; presence is enablement). Absent means every signal\n // whose prerequisite holds, each at weight 1 -- words always, links when the feature is on,\n // vectors when the tree names a model. A layer searched for exact wording (ingested sources,\n // archives, generated output) declares {\"words\":1,\"links\":1} and costs no embedding at all.\n signals?: SignalWeights;\n where?: string; // standing SQL condition against frontmatter alias `f`\n}\n\n// A saved `search` invocation: `sense <name>` runs like\n// `sense search <search> [--preset] [--include] [--exclude] [--where] [--k]`.\nexport interface SavedSearch {\n search: string;\n preset?: string;\n include?: string[];\n exclude?: string[];\n where?: string;\n k?: number;\n}\n\n// An entry names the verb it runs, one to one with the commands: { sql } runs like\n// `sense sql`, { search } like `sense search`.\nexport type SavedQuery = { sql: string } | SavedSearch;\n\nexport interface Config {\n // Editor-only pointer to schema.json; never read by sense.\n $schema?: string;\n version?: number;\n // File selection, index-time, and per-preset search defaults. A file is indexed iff any\n // preset's include/exclude covers it (union). `default` is used when a command names no preset.\n presets: Record<string, Preset>;\n // Global feature defaults; absent block or key means enabled. embed is not a member here --\n // see the `embed` block.\n features?: { links?: boolean; sections?: boolean; tags?: boolean; rank?: boolean };\n // Names the embedding model. Present means every indexed file gets vectors; absent means none do.\n embed?: EmbedConfig;\n // Settings for the `content` FTS5 table. `tokenize` decides which languages the tree can be\n // word-searched in at all; changing it rebuilds the index.\n content?: { tokenize?: string };\n // Backing store engine for this tree
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config/types.ts"],"sourcesContent":["import type { SignalWeights } from './signals.ts';\n\nexport const CONFIG_FILENAME = 'sense.config.json';\nexport const STATE_DIR = '.sense';\n\n// Highest sense.config.json `version` this build understands. Older versions auto-migrate on load.\nexport const SUPPORTED_CONFIG_VERSION = 5;\n\n// Each feature owns its tables, parse-time extraction, and reconcile step; commands degrade when one is off.\n// links/sections/tags/rank are opt-out toggles in the top-level `features` block; embed is not a\n// member of that block -- it is on exactly when an `embed` block names a model.\nexport const FEATURE_NAMES = ['links', 'sections', 'tags', 'rank', 'embed'] as const;\nexport type FeatureName = (typeof FEATURE_NAMES)[number];\n\n// `embed` names the model and gives the tree vectors at all; a preset's `signals` says which\n// engines that scope's searches use.\nexport interface EmbedConfig {\n model?: string;\n provider?: 'static' | 'openai' | 'cohere';\n url?: string; // openai provider: OpenAI-compatible base URL, e.g. http://localhost:11434/v1\n key?: string; // openai/cohere provider: name of the env var holding the bearer token, if any\n languages?: string[]; // owner-declared model languages; enables the language-fit check where no card exists\n chunkTokens?: number; // chunk size ceiling in estimated tokens for small-context models; default 500\n}\n\nexport const DEFAULT_EMBED_MODEL = 'minishlab/potion-retrieval-32M';\n\n// A named, self-contained file-selection scope. include/exclude are globby patterns resolved\n// relative to the config file. No inheritance between presets -- every field a preset wants\n// it declares itself. Presets may overlap freely; they are views, not partitions.\nexport interface Preset {\n include: string[];\n exclude?: string[];\n k?: number; // result count for `search` scoped to this preset; default 10\n // Exhaustive when present: every signal this preset's searches compose, each mapped to\n // its RRF weight (a finite number > 0; presence is enablement). Absent means every signal\n // whose prerequisite holds, each at weight 1 -- words always, links when the feature is on,\n // vectors when the tree names a model. A layer searched for exact wording (ingested sources,\n // archives, generated output) declares {\"words\":1,\"links\":1} and costs no embedding at all.\n signals?: SignalWeights;\n where?: string; // standing SQL condition against frontmatter alias `f`\n}\n\n// A saved `search` invocation: `sense <name>` runs like\n// `sense search <search> [--preset] [--include] [--exclude] [--where] [--k]`.\nexport interface SavedSearch {\n search: string;\n preset?: string;\n include?: string[];\n exclude?: string[];\n where?: string;\n k?: number;\n}\n\n// An entry names the verb it runs, one to one with the commands: { sql } runs like\n// `sense sql`, { search } like `sense search`.\nexport type SavedQuery = { sql: string } | SavedSearch;\n\nexport interface Config {\n // Editor-only pointer to schema.json; never read by sense.\n $schema?: string;\n version?: number;\n // File selection, index-time, and per-preset search defaults. A file is indexed iff any\n // preset's include/exclude covers it (union). `default` is used when a command names no preset.\n presets: Record<string, Preset>;\n // Global feature defaults; absent block or key means enabled. embed is not a member here --\n // see the `embed` block.\n features?: { links?: boolean; sections?: boolean; tags?: boolean; rank?: boolean };\n // Names the embedding model. Present means every indexed file gets vectors; absent means none do.\n embed?: EmbedConfig;\n // Settings for the `content` FTS5 table. `tokenize` decides which languages the tree can be\n // word-searched in at all; changing it rebuilds the index.\n content?: { tokenize?: string };\n // Backing store engine for this tree: \"sqlite\" (default, zero-dependency) or \"duckdb\"\n // (an optional dependency, installed on first use). Both implement the same commands.\n store?: 'sqlite' | 'duckdb';\n queries: Record<string, SavedQuery>;\n}\n\nexport interface ResolvedConfig extends Config {\n baseDir: string;\n configPath: string | null;\n // Keys the file declares that this build does not read; cli reports them.\n unknownKeys?: string[];\n // Set when loadConfig auto-migrated the file on disk; cli reports it.\n migratedFrom?: number;\n}\n\nexport interface SearchOverrides {\n preset?: string;\n k?: number;\n where?: string;\n include?: string[];\n exclude?: string[];\n noExclude?: boolean; // drop the preset's exclude for this command; --exclude still applies\n}\n\nexport interface EffectiveSearch {\n presetName: string;\n k: number;\n where?: string;\n include: string[];\n exclude?: string[];\n signals: SignalWeights; // this preset's effective signal weights\n}\n"],"names":["CONFIG_FILENAME","STATE_DIR","SUPPORTED_CONFIG_VERSION","FEATURE_NAMES","DEFAULT_EMBED_MODEL"],"mappings":"AAEA,OAAO,MAAMA,kBAAkB,oBAAoB;AACnD,OAAO,MAAMC,YAAY,SAAS;AAElC,mGAAmG;AACnG,OAAO,MAAMC,2BAA2B,EAAE;AAE1C,6GAA6G;AAC7G,iGAAiG;AACjG,gFAAgF;AAChF,OAAO,MAAMC,gBAAgB;IAAC;IAAS;IAAY;IAAQ;IAAQ;CAAQ,CAAU;AAcrF,OAAO,MAAMC,sBAAsB,iCAAiC"}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
// A frontmatter key with punctuation (`plugin-id`, `a.b`) is a real column, but
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// A frontmatter key with punctuation (`plugin-id`, `a.b`) is a real column, but unquoted in SQL
|
|
2
|
+
// it parses as an expression: `plugin - id`, or table `a` column `b`. SQLite names a fragment
|
|
3
|
+
// (`no such column: plugin`), DuckDB names the fragment or the table (`Referenced column/table
|
|
4
|
+
// "..." not found`); this maps either back to the real column so the error names the fix.
|
|
5
5
|
function startsWithBoundary(name, prefix) {
|
|
6
6
|
return name.length > prefix.length && name.startsWith(prefix) && /\W/.test(name[prefix.length]);
|
|
7
7
|
}
|
|
8
8
|
// columns: the tree's frontmatter columns (store.docs.columns()), read by the caller so this
|
|
9
9
|
// stays a pure function rather than a store dependency.
|
|
10
10
|
export function columnHint(columns, err) {
|
|
11
|
-
|
|
11
|
+
var _ref, _match_;
|
|
12
|
+
const match = /no such column: (\S+)|Referenced column "([^"]+)" not found in FROM clause|Referenced table "([^"]+)" not found/.exec(err.message);
|
|
12
13
|
if (!match) return err;
|
|
13
|
-
const token = match[1];
|
|
14
|
+
const token = (_ref = (_match_ = match[1]) !== null && _match_ !== void 0 ? _match_ : match[2]) !== null && _ref !== void 0 ? _ref : match[3];
|
|
14
15
|
// `f.plugin` -> `plugin`; left as-is when there's no leading alias segment, which also
|
|
15
16
|
// covers a column literally named `a.b` -- the full-token check below matches that case
|
|
16
17
|
// directly, so stripping here never needs to special-case it.
|
|
@@ -29,5 +30,5 @@ export function columnHint(columns, err) {
|
|
|
29
30
|
const quoted = [
|
|
30
31
|
...candidates
|
|
31
32
|
].map((name)=>`"${name.split('"').join('""')}"`).join(', ');
|
|
32
|
-
return new Error(`${err.message} -- ${quoted} needs double quotes in SQL: unquoted,
|
|
33
|
+
return new Error(`${err.message} -- ${quoted} needs double quotes in SQL: unquoted, the engine parses the punctuation as an operator or a qualifier, not part of a column name.`);
|
|
33
34
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/output/column-hint.ts"],"sourcesContent":["// A frontmatter key with punctuation (`plugin-id`, `a.b`) is a real column, but
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/output/column-hint.ts"],"sourcesContent":["// A frontmatter key with punctuation (`plugin-id`, `a.b`) is a real column, but unquoted in SQL\n// it parses as an expression: `plugin - id`, or table `a` column `b`. SQLite names a fragment\n// (`no such column: plugin`), DuckDB names the fragment or the table (`Referenced column/table\n// \"...\" not found`); this maps either back to the real column so the error names the fix.\n\nfunction startsWithBoundary(name: string, prefix: string): boolean {\n return name.length > prefix.length && name.startsWith(prefix) && /\\W/.test(name[prefix.length]);\n}\n\n// columns: the tree's frontmatter columns (store.docs.columns()), read by the caller so this\n// stays a pure function rather than a store dependency.\nexport function columnHint(columns: string[], err: Error): Error {\n const match = /no such column: (\\S+)|Referenced column \"([^\"]+)\" not found in FROM clause|Referenced table \"([^\"]+)\" not found/.exec(err.message);\n if (!match) return err;\n const token = match[1] ?? match[2] ?? match[3];\n // `f.plugin` -> `plugin`; left as-is when there's no leading alias segment, which also\n // covers a column literally named `a.b` -- the full-token check below matches that case\n // directly, so stripping here never needs to special-case it.\n const aliasStripped = token.replace(/^[A-Za-z_]\\w*\\./, '');\n const forms = new Set([token, aliasStripped]);\n const candidates = new Set<string>();\n for (const name of columns) {\n for (const form of forms) {\n if (name === form || startsWithBoundary(name, form)) candidates.add(name);\n }\n }\n if (candidates.size === 0) return err;\n const quoted = [...candidates].map((name) => `\"${name.split('\"').join('\"\"')}\"`).join(', ');\n return new Error(`${err.message} -- ${quoted} needs double quotes in SQL: unquoted, the engine parses the punctuation as an operator or a qualifier, not part of a column name.`);\n}\n"],"names":["startsWithBoundary","name","prefix","length","startsWith","test","columnHint","columns","err","match","exec","message","token","aliasStripped","replace","forms","Set","candidates","form","add","size","quoted","map","split","join","Error"],"mappings":"AAAA,gGAAgG;AAChG,8FAA8F;AAC9F,+FAA+F;AAC/F,0FAA0F;AAE1F,SAASA,mBAAmBC,IAAY,EAAEC,MAAc;IACtD,OAAOD,KAAKE,MAAM,GAAGD,OAAOC,MAAM,IAAIF,KAAKG,UAAU,CAACF,WAAW,KAAKG,IAAI,CAACJ,IAAI,CAACC,OAAOC,MAAM,CAAC;AAChG;AAEA,6FAA6F;AAC7F,wDAAwD;AACxD,OAAO,SAASG,WAAWC,OAAiB,EAAEC,GAAU;QAGxCC,MAAAA;IAFd,MAAMA,QAAQ,kHAAkHC,IAAI,CAACF,IAAIG,OAAO;IAChJ,IAAI,CAACF,OAAO,OAAOD;IACnB,MAAMI,SAAQH,QAAAA,UAAAA,KAAK,CAAC,EAAE,cAARA,qBAAAA,UAAYA,KAAK,CAAC,EAAE,cAApBA,kBAAAA,OAAwBA,KAAK,CAAC,EAAE;IAC9C,uFAAuF;IACvF,wFAAwF;IACxF,8DAA8D;IAC9D,MAAMI,gBAAgBD,MAAME,OAAO,CAAC,mBAAmB;IACvD,MAAMC,QAAQ,IAAIC,IAAI;QAACJ;QAAOC;KAAc;IAC5C,MAAMI,aAAa,IAAID;IACvB,KAAK,MAAMf,QAAQM,QAAS;QAC1B,KAAK,MAAMW,QAAQH,MAAO;YACxB,IAAId,SAASiB,QAAQlB,mBAAmBC,MAAMiB,OAAOD,WAAWE,GAAG,CAAClB;QACtE;IACF;IACA,IAAIgB,WAAWG,IAAI,KAAK,GAAG,OAAOZ;IAClC,MAAMa,SAAS;WAAIJ;KAAW,CAACK,GAAG,CAAC,CAACrB,OAAS,CAAC,CAAC,EAAEA,KAAKsB,KAAK,CAAC,KAAKC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAEA,IAAI,CAAC;IACrF,OAAO,IAAIC,MAAM,GAAGjB,IAAIG,OAAO,CAAC,IAAI,EAAEU,OAAO,kIAAkI,CAAC;AAClL"}
|
|
@@ -7,4 +7,9 @@ export interface ReparseResult {
|
|
|
7
7
|
warnings: string[];
|
|
8
8
|
newColumns: string[];
|
|
9
9
|
}
|
|
10
|
-
export
|
|
10
|
+
export interface ReparseOptions {
|
|
11
|
+
threshold?: number;
|
|
12
|
+
maxWorkers?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function featuresForFile(features: Feature[], cfg: Config, file: FileStat): Feature[];
|
|
15
|
+
export declare function reparseFiles(files: FileStat[], features: Feature[], cfg: Config, knownColumns: ReadonlySet<string>, onParsed?: (done: number) => void, options?: ReparseOptions): Promise<ReparseResult>;
|
package/dist/esm/scan/reparse.js
CHANGED
|
@@ -1,18 +1,118 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { availableParallelism } from 'node:os';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
1
4
|
import { parseFile } from './index.js';
|
|
5
|
+
import { reviveError } from './worker-error.js';
|
|
6
|
+
// Tinypool is ESM-only; our floor (>=22.20) has native require(esm), so the tier-2 house
|
|
7
|
+
// deferral reaches it. Deferred (not a top-level import) because most reconciles stay below
|
|
8
|
+
// DEFAULT_THRESHOLD and should not pay for constructing pool machinery they never use.
|
|
9
|
+
const _require = typeof require === 'undefined' ? createRequire(import.meta.url) : require;
|
|
10
|
+
// Measured 2026-08-29 on a 14-logical-core Apple M4 Pro: serial and pooled cross between 160
|
|
11
|
+
// and 180 files (pool fixed cost here is ~85-100ms, not the ~300ms a heavier module graph
|
|
12
|
+
// costs elsewhere), so 200 keeps a safety margin without giving up real wins. 8 workers matches
|
|
13
|
+
// an interleaved worker-count sweep at 6,566 files -- 8 and 10 tie within noise, 12+ trends
|
|
14
|
+
// worse (main-thread + OS contention on the 6 non-parse-worker cores).
|
|
15
|
+
const DEFAULT_THRESHOLD = 200;
|
|
16
|
+
const DEFAULT_MAX_WORKERS = 8;
|
|
17
|
+
// Worker MUST always load from dist/cjs/ (house pattern: install-module-linked,
|
|
18
|
+
// install-optional, node-version-install, node-exec-path all carry this same rule). A
|
|
19
|
+
// worker_threads thread is a fresh realm and does not inherit the main thread's TS loader
|
|
20
|
+
// hook, so a source path cannot work on any platform; dist/cjs loads from the esm and cjs
|
|
21
|
+
// builds alike. Root is found the way cli.ts finds it, so this also resolves when the suite
|
|
22
|
+
// runs against src/ (which already requires a build for the exports specs).
|
|
23
|
+
// Resolved on first pooled dispatch, not at import: a tree under the threshold never pools and
|
|
24
|
+
// must not pay for this, let alone fail on it.
|
|
25
|
+
let workerFile;
|
|
26
|
+
function resolveWorkerFile() {
|
|
27
|
+
if (workerFile) return workerFile;
|
|
28
|
+
const load = createRequire(import.meta.url);
|
|
29
|
+
for (const rel of [
|
|
30
|
+
'..',
|
|
31
|
+
'../..',
|
|
32
|
+
'../../..'
|
|
33
|
+
]){
|
|
34
|
+
try {
|
|
35
|
+
if (load(`${rel}/package.json`).name === 'sensemaking') {
|
|
36
|
+
workerFile = join(dirname(load.resolve(`${rel}/package.json`)), 'dist', 'cjs', 'workers', 'parse.js');
|
|
37
|
+
return workerFile;
|
|
38
|
+
}
|
|
39
|
+
} catch {}
|
|
40
|
+
}
|
|
41
|
+
throw new Error('cannot locate the sensemaking package root, so the parse worker cannot be found; run npm run build');
|
|
42
|
+
}
|
|
43
|
+
// Per-file feature filtering, shared by the serial loop (which reads `features` straight from
|
|
44
|
+
// the caller, real registry or a test double) and the worker path (which can only pass `cfg`
|
|
45
|
+
// across the thread boundary, so it derives `features` from activeFeatures(cfg) itself).
|
|
46
|
+
export function featuresForFile(features, cfg, file) {
|
|
47
|
+
return features.filter((feature)=>!feature.enabledForFile || feature.enabledForFile(cfg, file));
|
|
48
|
+
}
|
|
49
|
+
function reparseSerial(files, features, cfg, onParsed) {
|
|
50
|
+
const results = [];
|
|
51
|
+
let done = 0;
|
|
52
|
+
for (const file of files){
|
|
53
|
+
const fileFeatures = featuresForFile(features, cfg, file);
|
|
54
|
+
results.push(parseFile(file, fileFeatures, cfg));
|
|
55
|
+
onParsed === null || onParsed === void 0 ? void 0 : onParsed(++done);
|
|
56
|
+
}
|
|
57
|
+
return results;
|
|
58
|
+
}
|
|
59
|
+
// Never the tree: a worker task carries one FileStat and returns only what parseFile already
|
|
60
|
+
// returns -- extracted text and feature values, never an mdast tree. The worker reads the file
|
|
61
|
+
// itself. Pool created once here and destroyed once dispatch finishes; never per task.
|
|
62
|
+
async function reparsePooled(files, features, cfg, onParsed, maxWorkers) {
|
|
63
|
+
const { Tinypool } = _require('tinypool');
|
|
64
|
+
// cfg and the caller's feature selection are constant for the whole dispatch, so they cross
|
|
65
|
+
// once per worker as workerData rather than once per file in the task payload. Features carry
|
|
66
|
+
// closures and cannot cross at all; their names can, and the worker resolves them back
|
|
67
|
+
// against the same registry, so a caller passing a subset gets that subset in both modes.
|
|
68
|
+
const workerData = {
|
|
69
|
+
cfg,
|
|
70
|
+
featureNames: features.map((feature)=>feature.name)
|
|
71
|
+
};
|
|
72
|
+
const pool = new Tinypool({
|
|
73
|
+
filename: resolveWorkerFile(),
|
|
74
|
+
minThreads: maxWorkers,
|
|
75
|
+
maxThreads: maxWorkers,
|
|
76
|
+
workerData
|
|
77
|
+
});
|
|
78
|
+
let done = 0;
|
|
79
|
+
try {
|
|
80
|
+
// Promise.all over a mapped array is load-bearing: the resolved array keeps `files`
|
|
81
|
+
// order regardless of which task finishes first, which is what first-seen column order
|
|
82
|
+
// and warning order depend on downstream. Progress ticks fire in completion order instead
|
|
83
|
+
// (only the count matters for a progress bar) -- free, since it rides the same per-task
|
|
84
|
+
// response tinypool already sends, no extra ping.
|
|
85
|
+
const results = await Promise.all(files.map(async (file)=>{
|
|
86
|
+
const result = await pool.run(file);
|
|
87
|
+
if (!result.ok) throw reviveError(result.error);
|
|
88
|
+
onParsed === null || onParsed === void 0 ? void 0 : onParsed(++done);
|
|
89
|
+
return {
|
|
90
|
+
doc: result.doc,
|
|
91
|
+
warnings: result.warnings
|
|
92
|
+
};
|
|
93
|
+
}));
|
|
94
|
+
await pool.destroy();
|
|
95
|
+
return results;
|
|
96
|
+
} catch (err) {
|
|
97
|
+
// Tear the pool down before rethrowing, but never let a destroy failure replace the parse
|
|
98
|
+
// failure that caused it: that one names the file, this one names nothing. Not a `finally`
|
|
99
|
+
// for exactly that reason.
|
|
100
|
+
await pool.destroy().catch(()=>{});
|
|
101
|
+
throw err;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
2
104
|
// `onParsed` receives the running count (1-based) after each file, mirroring a Progress.tick
|
|
3
105
|
// call; pass one in to keep progress reporting working without this module owning a reporter.
|
|
4
|
-
export function reparseFiles(files, features, cfg, knownColumns, onParsed) {
|
|
106
|
+
export async function reparseFiles(files, features, cfg, knownColumns, onParsed, options = {}) {
|
|
107
|
+
var _options_threshold, _options_maxWorkers;
|
|
108
|
+
const threshold = (_options_threshold = options.threshold) !== null && _options_threshold !== void 0 ? _options_threshold : DEFAULT_THRESHOLD;
|
|
109
|
+
const maxWorkers = (_options_maxWorkers = options.maxWorkers) !== null && _options_maxWorkers !== void 0 ? _options_maxWorkers : Math.min(DEFAULT_MAX_WORKERS, availableParallelism());
|
|
110
|
+
const results = files.length >= threshold ? await reparsePooled(files, features, cfg, onParsed, maxWorkers) : reparseSerial(files, features, cfg, onParsed);
|
|
5
111
|
const docs = [];
|
|
6
112
|
const warnings = [];
|
|
7
113
|
const newColumns = [];
|
|
8
114
|
const seen = new Set(knownColumns);
|
|
9
|
-
|
|
10
|
-
for (const file of files){
|
|
11
|
-
// A doc only gets extract/store from features that apply to it (currently: embed, via
|
|
12
|
-
// FileStat.embed -- true iff the config names an embedding model).
|
|
13
|
-
const fileFeatures = features.filter((feature)=>!feature.enabledForFile || feature.enabledForFile(cfg, file));
|
|
14
|
-
const { doc, warnings: fileWarnings } = parseFile(file, fileFeatures, cfg);
|
|
15
|
-
onParsed === null || onParsed === void 0 ? void 0 : onParsed(++done);
|
|
115
|
+
for (const { doc, warnings: fileWarnings } of results){
|
|
16
116
|
warnings.push(...fileWarnings);
|
|
17
117
|
for (const key of Object.keys(doc.data)){
|
|
18
118
|
if (!seen.has(key)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/scan/reparse.ts"],"sourcesContent":["import type { Config } from '../config/index.ts';\nimport type { Feature } from '../features/types.ts';\nimport type { ParsedDoc } from './index.ts';\nimport { parseFile } from './index.ts';\nimport type { FileStat } from './list.ts';\n\n// Store-agnostic per-file parse pass shared by every store's reconcile(). Index-preserving by\n// construction (one pass over `files`, pushed in order) so a future concurrent dispatch can\n// replace the loop body without changing this contract or either call site.\nexport interface ReparseResult {\n docs: ParsedDoc[];\n warnings: string[];\n // Frontmatter keys not in `knownColumns`, first-seen order across `files` -- the order\n // callers ALTER TABLE ADD COLUMN in.\n newColumns: string[];\n}\n\n// `onParsed` receives the running count (1-based) after each file, mirroring a Progress.tick\n// call; pass one in to keep progress reporting working without this module owning a reporter.\nexport function reparseFiles(files: FileStat[], features: Feature[], cfg: Config, knownColumns: ReadonlySet<string>, onParsed?: (done: number) => void): ReparseResult {\n const docs: ParsedDoc[] = [];\n const warnings: string[] = [];\n const newColumns: string[] = [];\n const seen = new Set(knownColumns);\n let done = 0;\n\n for (const file of files) {\n // A doc only gets extract/store from features that apply to it (currently: embed, via\n // FileStat.embed -- true iff the config names an embedding model).\n const fileFeatures = features.filter((feature) => !feature.enabledForFile || feature.enabledForFile(cfg, file));\n const { doc, warnings: fileWarnings } = parseFile(file, fileFeatures, cfg);\n onParsed?.(++done);\n warnings.push(...fileWarnings);\n for (const key of Object.keys(doc.data)) {\n if (!seen.has(key)) {\n seen.add(key);\n newColumns.push(key);\n }\n }\n docs.push(doc);\n }\n\n return { docs, warnings, newColumns };\n}\n"],"names":["parseFile","reparseFiles","files","features","cfg","knownColumns","onParsed","docs","warnings","newColumns","seen","Set","done","file","fileFeatures","filter","feature","enabledForFile","doc","fileWarnings","push","key","Object","keys","data","has","add"],"mappings":"AAGA,SAASA,SAAS,QAAQ,aAAa;AAcvC,6FAA6F;AAC7F,8FAA8F;AAC9F,OAAO,SAASC,aAAaC,KAAiB,EAAEC,QAAmB,EAAEC,GAAW,EAAEC,YAAiC,EAAEC,QAAiC;IACpJ,MAAMC,OAAoB,EAAE;IAC5B,MAAMC,WAAqB,EAAE;IAC7B,MAAMC,aAAuB,EAAE;IAC/B,MAAMC,OAAO,IAAIC,IAAIN;IACrB,IAAIO,OAAO;IAEX,KAAK,MAAMC,QAAQX,MAAO;QACxB,sFAAsF;QACtF,mEAAmE;QACnE,MAAMY,eAAeX,SAASY,MAAM,CAAC,CAACC,UAAY,CAACA,QAAQC,cAAc,IAAID,QAAQC,cAAc,CAACb,KAAKS;QACzG,MAAM,EAAEK,GAAG,EAAEV,UAAUW,YAAY,EAAE,GAAGnB,UAAUa,MAAMC,cAAcV;QACtEE,qBAAAA,+BAAAA,SAAW,EAAEM;QACbJ,SAASY,IAAI,IAAID;QACjB,KAAK,MAAME,OAAOC,OAAOC,IAAI,CAACL,IAAIM,IAAI,EAAG;YACvC,IAAI,CAACd,KAAKe,GAAG,CAACJ,MAAM;gBAClBX,KAAKgB,GAAG,CAACL;gBACTZ,WAAWW,IAAI,CAACC;YAClB;QACF;QACAd,KAAKa,IAAI,CAACF;IACZ;IAEA,OAAO;QAAEX;QAAMC;QAAUC;IAAW;AACtC"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/scan/reparse.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { availableParallelism } from 'node:os';\nimport { dirname, join } from 'node:path';\n// Type-only: erased at build, but keeps depcheck's usage check satisfied for the tier-2\n// `_require` below (see coding-standards' deferral tiers).\nimport type * as TinypoolNS from 'tinypool';\nimport type { Config } from '../config/index.ts';\nimport type { Feature } from '../features/types.ts';\nimport type { ParseTask, ParseTaskResult, ParseWorkerData } from '../workers/parse.ts';\nimport type { ParsedDoc } from './index.ts';\nimport { parseFile } from './index.ts';\nimport type { FileStat } from './list.ts';\nimport { reviveError } from './worker-error.ts';\n\n// Tinypool is ESM-only; our floor (>=22.20) has native require(esm), so the tier-2 house\n// deferral reaches it. Deferred (not a top-level import) because most reconciles stay below\n// DEFAULT_THRESHOLD and should not pay for constructing pool machinery they never use.\nconst _require = typeof require === 'undefined' ? createRequire(import.meta.url) : require;\n\n// Store-agnostic per-file parse pass shared by every store's reconcile(). Index-preserving by\n// construction (one pass over `files`, pushed in order) so a future concurrent dispatch can\n// replace the loop body without changing this contract or either call site.\nexport interface ReparseResult {\n docs: ParsedDoc[];\n warnings: string[];\n // Frontmatter keys not in `knownColumns`, first-seen order across `files` -- the order\n // callers ALTER TABLE ADD COLUMN in.\n newColumns: string[];\n}\n\nexport interface ReparseOptions {\n // Overrides DEFAULT_THRESHOLD below, so tests can force either dispatch mode without\n // needing thousands of fixture files. Internal: no caller in src/ passes it.\n threshold?: number;\n // Overrides DEFAULT_MAX_WORKERS below.\n maxWorkers?: number;\n}\n\n// Measured 2026-08-29 on a 14-logical-core Apple M4 Pro: serial and pooled cross between 160\n// and 180 files (pool fixed cost here is ~85-100ms, not the ~300ms a heavier module graph\n// costs elsewhere), so 200 keeps a safety margin without giving up real wins. 8 workers matches\n// an interleaved worker-count sweep at 6,566 files -- 8 and 10 tie within noise, 12+ trends\n// worse (main-thread + OS contention on the 6 non-parse-worker cores).\nconst DEFAULT_THRESHOLD = 200;\nconst DEFAULT_MAX_WORKERS = 8;\n\n// Worker MUST always load from dist/cjs/ (house pattern: install-module-linked,\n// install-optional, node-version-install, node-exec-path all carry this same rule). A\n// worker_threads thread is a fresh realm and does not inherit the main thread's TS loader\n// hook, so a source path cannot work on any platform; dist/cjs loads from the esm and cjs\n// builds alike. Root is found the way cli.ts finds it, so this also resolves when the suite\n// runs against src/ (which already requires a build for the exports specs).\n// Resolved on first pooled dispatch, not at import: a tree under the threshold never pools and\n// must not pay for this, let alone fail on it.\nlet workerFile: string | undefined;\nfunction resolveWorkerFile(): string {\n if (workerFile) return workerFile;\n const load = createRequire(import.meta.url);\n for (const rel of ['..', '../..', '../../..']) {\n try {\n if ((load(`${rel}/package.json`) as { name?: string }).name === 'sensemaking') {\n workerFile = join(dirname(load.resolve(`${rel}/package.json`)), 'dist', 'cjs', 'workers', 'parse.js');\n return workerFile;\n }\n } catch {}\n }\n throw new Error('cannot locate the sensemaking package root, so the parse worker cannot be found; run npm run build');\n}\n\ntype FileResult = { doc: ParsedDoc; warnings: string[] };\n\n// Per-file feature filtering, shared by the serial loop (which reads `features` straight from\n// the caller, real registry or a test double) and the worker path (which can only pass `cfg`\n// across the thread boundary, so it derives `features` from activeFeatures(cfg) itself).\nexport function featuresForFile(features: Feature[], cfg: Config, file: FileStat): Feature[] {\n return features.filter((feature) => !feature.enabledForFile || feature.enabledForFile(cfg, file));\n}\n\nfunction reparseSerial(files: FileStat[], features: Feature[], cfg: Config, onParsed?: (done: number) => void): FileResult[] {\n const results: FileResult[] = [];\n let done = 0;\n for (const file of files) {\n const fileFeatures = featuresForFile(features, cfg, file);\n results.push(parseFile(file, fileFeatures, cfg));\n onParsed?.(++done);\n }\n return results;\n}\n\n// Never the tree: a worker task carries one FileStat and returns only what parseFile already\n// returns -- extracted text and feature values, never an mdast tree. The worker reads the file\n// itself. Pool created once here and destroyed once dispatch finishes; never per task.\nasync function reparsePooled(files: FileStat[], features: Feature[], cfg: Config, onParsed: ((done: number) => void) | undefined, maxWorkers: number): Promise<FileResult[]> {\n const { Tinypool } = _require('tinypool') as typeof TinypoolNS;\n // cfg and the caller's feature selection are constant for the whole dispatch, so they cross\n // once per worker as workerData rather than once per file in the task payload. Features carry\n // closures and cannot cross at all; their names can, and the worker resolves them back\n // against the same registry, so a caller passing a subset gets that subset in both modes.\n const workerData: ParseWorkerData = { cfg, featureNames: features.map((feature) => feature.name) };\n const pool = new Tinypool({ filename: resolveWorkerFile(), minThreads: maxWorkers, maxThreads: maxWorkers, workerData });\n let done = 0;\n try {\n // Promise.all over a mapped array is load-bearing: the resolved array keeps `files`\n // order regardless of which task finishes first, which is what first-seen column order\n // and warning order depend on downstream. Progress ticks fire in completion order instead\n // (only the count matters for a progress bar) -- free, since it rides the same per-task\n // response tinypool already sends, no extra ping.\n const results = await Promise.all(\n files.map(async (file): Promise<FileResult> => {\n const result = (await pool.run(file as ParseTask)) as ParseTaskResult;\n if (!result.ok) throw reviveError(result.error);\n onParsed?.(++done);\n return { doc: result.doc, warnings: result.warnings };\n })\n );\n await pool.destroy();\n return results;\n } catch (err) {\n // Tear the pool down before rethrowing, but never let a destroy failure replace the parse\n // failure that caused it: that one names the file, this one names nothing. Not a `finally`\n // for exactly that reason.\n await pool.destroy().catch(() => {});\n throw err;\n }\n}\n\n// `onParsed` receives the running count (1-based) after each file, mirroring a Progress.tick\n// call; pass one in to keep progress reporting working without this module owning a reporter.\nexport async function reparseFiles(files: FileStat[], features: Feature[], cfg: Config, knownColumns: ReadonlySet<string>, onParsed?: (done: number) => void, options: ReparseOptions = {}): Promise<ReparseResult> {\n const threshold = options.threshold ?? DEFAULT_THRESHOLD;\n const maxWorkers = options.maxWorkers ?? Math.min(DEFAULT_MAX_WORKERS, availableParallelism());\n const results = files.length >= threshold ? await reparsePooled(files, features, cfg, onParsed, maxWorkers) : reparseSerial(files, features, cfg, onParsed);\n\n const docs: ParsedDoc[] = [];\n const warnings: string[] = [];\n const newColumns: string[] = [];\n const seen = new Set(knownColumns);\n for (const { doc, warnings: fileWarnings } of results) {\n warnings.push(...fileWarnings);\n for (const key of Object.keys(doc.data)) {\n if (!seen.has(key)) {\n seen.add(key);\n newColumns.push(key);\n }\n }\n docs.push(doc);\n }\n\n return { docs, warnings, newColumns };\n}\n"],"names":["createRequire","availableParallelism","dirname","join","parseFile","reviveError","_require","require","url","DEFAULT_THRESHOLD","DEFAULT_MAX_WORKERS","workerFile","resolveWorkerFile","load","rel","name","resolve","Error","featuresForFile","features","cfg","file","filter","feature","enabledForFile","reparseSerial","files","onParsed","results","done","fileFeatures","push","reparsePooled","maxWorkers","Tinypool","workerData","featureNames","map","pool","filename","minThreads","maxThreads","Promise","all","result","run","ok","error","doc","warnings","destroy","err","catch","reparseFiles","knownColumns","options","threshold","Math","min","length","docs","newColumns","seen","Set","fileWarnings","key","Object","keys","data","has","add"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type SenseErrorCode } from '../errors.js';
|
|
2
|
+
export interface WorkerErrorPayload {
|
|
3
|
+
name: string;
|
|
4
|
+
code?: SenseErrorCode;
|
|
5
|
+
message: string;
|
|
6
|
+
stack?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function serializeError(err: unknown): WorkerErrorPayload;
|
|
9
|
+
export declare function reviveError(payload: WorkerErrorPayload): Error;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SenseError } from '../errors.js';
|
|
2
|
+
export function serializeError(err) {
|
|
3
|
+
if (err instanceof Error) return {
|
|
4
|
+
name: err.name,
|
|
5
|
+
code: err instanceof SenseError ? err.code : undefined,
|
|
6
|
+
message: err.message,
|
|
7
|
+
stack: err.stack
|
|
8
|
+
};
|
|
9
|
+
return {
|
|
10
|
+
name: 'Error',
|
|
11
|
+
message: String(err)
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function reviveError(payload) {
|
|
15
|
+
const err = payload.code ? new SenseError(payload.code, payload.message) : new Error(payload.message);
|
|
16
|
+
err.name = payload.name;
|
|
17
|
+
if (payload.stack) err.stack = payload.stack;
|
|
18
|
+
return err;
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/scan/worker-error.ts"],"sourcesContent":["import { SenseError, type SenseErrorCode } from '../errors.ts';\n\n// Structured clone across a worker_threads boundary drops a subclass and its custom\n// properties, so a SenseError arrives as a plain Error with `code` undefined -- and `code`\n// drives caller behavior. The worker serializes explicitly (computed on its side, where\n// `instanceof SenseError` is still meaningful) and the main thread rebuilds by hand.\nexport interface WorkerErrorPayload {\n name: string;\n code?: SenseErrorCode;\n message: string;\n stack?: string;\n}\n\nexport function serializeError(err: unknown): WorkerErrorPayload {\n if (err instanceof Error) return { name: err.name, code: err instanceof SenseError ? err.code : undefined, message: err.message, stack: err.stack };\n return { name: 'Error', message: String(err) };\n}\n\nexport function reviveError(payload: WorkerErrorPayload): Error {\n const err = payload.code ? new SenseError(payload.code, payload.message) : new Error(payload.message);\n err.name = payload.name;\n if (payload.stack) err.stack = payload.stack;\n return err;\n}\n"],"names":["SenseError","serializeError","err","Error","name","code","undefined","message","stack","String","reviveError","payload"],"mappings":"AAAA,SAASA,UAAU,QAA6B,eAAe;AAa/D,OAAO,SAASC,eAAeC,GAAY;IACzC,IAAIA,eAAeC,OAAO,OAAO;QAAEC,MAAMF,IAAIE,IAAI;QAAEC,MAAMH,eAAeF,aAAaE,IAAIG,IAAI,GAAGC;QAAWC,SAASL,IAAIK,OAAO;QAAEC,OAAON,IAAIM,KAAK;IAAC;IAClJ,OAAO;QAAEJ,MAAM;QAASG,SAASE,OAAOP;IAAK;AAC/C;AAEA,OAAO,SAASQ,YAAYC,OAA2B;IACrD,MAAMT,MAAMS,QAAQN,IAAI,GAAG,IAAIL,WAAWW,QAAQN,IAAI,EAAEM,QAAQJ,OAAO,IAAI,IAAIJ,MAAMQ,QAAQJ,OAAO;IACpGL,IAAIE,IAAI,GAAGO,QAAQP,IAAI;IACvB,IAAIO,QAAQH,KAAK,EAAEN,IAAIM,KAAK,GAAGG,QAAQH,KAAK;IAC5C,OAAON;AACT"}
|
|
@@ -56,7 +56,7 @@ export async function reconcile(conn, cfg, baseDir) {
|
|
|
56
56
|
const features = activeFeatures(cfg);
|
|
57
57
|
const seenColumns = await getColumns(conn);
|
|
58
58
|
const report = progress('reparsing files', toReparse.length);
|
|
59
|
-
const { docs: parsedDocs, warnings, newColumns } = reparseFiles(toReparse, features, cfg, seenColumns, report.tick);
|
|
59
|
+
const { docs: parsedDocs, warnings, newColumns } = await reparseFiles(toReparse, features, cfg, seenColumns, report.tick);
|
|
60
60
|
report.finish();
|
|
61
61
|
for (const col of newColumns)seenColumns.add(col);
|
|
62
62
|
const allColumns = [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/reconcile.ts"],"sourcesContent":["import type { Config } from '../../config/index.ts';\nimport { SenseError } from '../../errors.ts';\nimport { activeFeatures } from '../../features/index.ts';\nimport type { ExtractedDoc, ReconcileDelta } from '../../features/types.ts';\nimport { progress } from '../../output/progress.ts';\nimport type { ParsedDoc } from '../../scan/index.ts';\nimport { listFiles, RESERVED_COLUMNS } from '../../scan/index.ts';\nimport { reparseFiles } from '../../scan/reparse.ts';\nimport { getColumns, quoteIdent } from '../shared.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Connection } from '../types.ts';\n\n// Mirrors src/store/sqlite/reconcile.ts's frontmatter-upsert shape (dynamic ALTER TABLE per\n// discovered key, ON CONFLICT upsert keeping the row's identity stable). `content` is a plain\n// table, not FTS-virtual (D1: DuckDB's fts index is built lazily, on first lexical query, from\n// this table -- see duckdb/lexical.ts), so its rows are maintained here unconditionally. The one\n// forced divergence for frontmatter itself is the ALTER TABLE type: DuckDB requires a declared\n// column type where sqlite accepts none, so every dynamic frontmatter column is VARIANT (the\n// only DuckDB type that can hold the different JS types mapValue() produces across files for\n// the same key).\nconst CORE_FRONTMATTER_COLUMNS = new Set(['path', '_mtime', '_ctime', '_size', '_parse_error']);\n\n// No rowid coupling needed (unlike sqlite's content, which links to frontmatter's rowid):\n// `path` is content's own primary key, so this is a plain per-doc row.\nconst INSERT_CONTENT_SQL = `INSERT INTO content (\"path\", title, summary, text) VALUES (?, ?, ?, ?)`;\n\nfunction contentRow(doc: ParsedDoc): unknown[] {\n return [doc.relPath, doc.search.title, doc.search.summary, doc.search.text];\n}\n\n// No compile-time column cap in DuckDB (unlike SQLite's SQLITE_MAX_COLUMN); kept as a sanity\n// fence anyway so a runaway frontmatter generator fails with a clear message instead of an\n// unbounded ALTER TABLE loop.\nconst MAX_FRONTMATTER_COLUMNS = 10_000;\n\nexport async function reconcile(conn: Connection, cfg: Config, baseDir: string): Promise<{ parsed: number; warnings: string[] }> {\n const files = listFiles(cfg, baseDir);\n const currentSet = new Set(files.map((f) => f.relPath));\n\n const existingStmt = await conn.prepare('SELECT \"path\", \"_mtime\", \"_size\" FROM frontmatter');\n const existingRows = (await existingStmt.all()) as Array<{ path: string; _mtime: number; _size: number }>;\n const existing = new Map(existingRows.map((r) => [r.path, r]));\n const vanished = existingRows.filter((r) => !currentSet.has(r.path)).map((r) => r.path);\n\n const toReparse = files.filter((f) => {\n const row = existing.get(f.relPath);\n return !row || row._mtime !== f.mtimeMs || row._size !== f.size;\n });\n\n if (vanished.length === 0 && toReparse.length === 0) return { parsed: 0, warnings: [] };\n\n const features = activeFeatures(cfg);\n const seenColumns = await getColumns(conn);\n\n const report = progress('reparsing files', toReparse.length);\n const { docs: parsedDocs, warnings, newColumns } = reparseFiles(toReparse, features, cfg, seenColumns, report.tick);\n report.finish();\n for (const col of newColumns) seenColumns.add(col);\n\n const allColumns = [...seenColumns];\n if (allColumns.length > MAX_FRONTMATTER_COLUMNS) {\n throw new SenseError('COLUMN_LIMIT', `frontmatter would need ${allColumns.length} columns, crossing this store's sanity limit (${MAX_FRONTMATTER_COLUMNS}). Narrow the presets' include globs so fewer/other files are indexed, or fix whatever is generating unbounded frontmatter keys.`);\n }\n const writableColumns = allColumns.filter((c) => CORE_FRONTMATTER_COLUMNS.has(c) || !RESERVED_COLUMNS.has(c));\n const insertSql = `INSERT INTO frontmatter (${writableColumns.map(quoteIdent).join(', ')}) VALUES (${writableColumns.map(() => '?').join(', ')}) ON CONFLICT(\"path\") DO UPDATE SET ${writableColumns\n .filter((c) => c !== 'path')\n .map((c) => `${quoteIdent(c)} = excluded.${quoteIdent(c)}`)\n .join(', ')}`;\n\n const added = toReparse.filter((f) => !existing.has(f.relPath)).map((f) => f.relPath);\n const delta: ReconcileDelta = { files, reparsed: parsedDocs.map((d) => d.relPath), added, vanished };\n const addedSet = new Set(added);\n const reparsedExisting = parsedDocs.map((d) => d.relPath).filter((p) => !addedSet.has(p));\n\n await withTransaction(conn, async () => {\n // DuckDB's ALTER TABLE ADD COLUMN requires a type; VARIANT is the only one that can hold\n // the mixed bigint/number/string/null shapes mapValue() produces for one key across files.\n for (const col of newColumns) await conn.exec(`ALTER TABLE frontmatter ADD COLUMN ${quoteIdent(col)} VARIANT`);\n\n if (parsedDocs.length > 0) {\n const rows = parsedDocs.map((doc) =>\n writableColumns.map((col) => {\n if (col === 'path') return doc.relPath;\n if (col === '_mtime') return doc.mtimeMs;\n if (col === '_ctime') return doc.ctimeMs;\n if (col === '_size') return doc.size;\n if (col === '_parse_error') return doc.parseError;\n return doc.data[col] ?? null;\n })\n );\n await conn.runBatch(insertSql, rows);\n }\n\n const contentTouched = [...vanished, ...reparsedExisting];\n if (contentTouched.length > 0)\n await conn.runBatch(\n 'DELETE FROM content WHERE \"path\" = ?',\n contentTouched.map((p) => [p])\n );\n if (parsedDocs.length > 0) await conn.runBatch(INSERT_CONTENT_SQL, parsedDocs.map(contentRow));\n\n if (vanished.length > 0)\n await conn.runBatch(\n 'DELETE FROM frontmatter WHERE \"path\" = ?',\n vanished.map((p) => [p])\n );\n\n // A preset edit forces a full rebuild, so an unchanged doc's coverage is already correct;\n // new docs have nothing to clear, which keeps cold builds linear.\n const presetTouched = [...vanished, ...reparsedExisting];\n if (presetTouched.length > 0)\n await conn.runBatch(\n 'DELETE FROM preset_files WHERE \"path\" = ?',\n presetTouched.map((p) => [p])\n );\n const presetRows: unknown[][] = [];\n for (const doc of parsedDocs) for (const presetName of doc.presets) presetRows.push([doc.relPath, presetName]);\n if (presetRows.length > 0) await conn.runBatch('INSERT INTO preset_files (\"path\", preset) VALUES (?, ?)', presetRows);\n\n const removedPaths = [...vanished, ...reparsedExisting];\n if (removedPaths.length > 0) for (const feature of features) await feature.remove?.(conn, removedPaths, delta);\n for (const feature of features) {\n const docsForFeature: ExtractedDoc[] = parsedDocs.map((doc) => ({ path: doc.relPath, extracted: doc.extracted[feature.name] }));\n await feature.store?.(conn, docsForFeature, delta);\n }\n for (const feature of features) await feature.afterReconcile?.(conn, delta);\n });\n\n return { parsed: parsedDocs.length, warnings };\n}\n"],"names":["SenseError","activeFeatures","progress","listFiles","RESERVED_COLUMNS","reparseFiles","getColumns","quoteIdent","withTransaction","CORE_FRONTMATTER_COLUMNS","Set","INSERT_CONTENT_SQL","contentRow","doc","relPath","search","title","summary","text","MAX_FRONTMATTER_COLUMNS","reconcile","conn","cfg","baseDir","files","currentSet","map","f","existingStmt","prepare","existingRows","all","existing","Map","r","path","vanished","filter","has","toReparse","row","get","_mtime","mtimeMs","_size","size","length","parsed","warnings","features","seenColumns","report","docs","parsedDocs","newColumns","tick","finish","col","add","allColumns","writableColumns","c","insertSql","join","added","delta","reparsed","d","addedSet","reparsedExisting","p","feature","exec","rows","ctimeMs","parseError","data","runBatch","contentTouched","presetTouched","presetRows","presetName","presets","push","removedPaths","remove","docsForFeature","extracted","name","store","afterReconcile"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/reconcile.ts"],"sourcesContent":["import type { Config } from '../../config/index.ts';\nimport { SenseError } from '../../errors.ts';\nimport { activeFeatures } from '../../features/index.ts';\nimport type { ExtractedDoc, ReconcileDelta } from '../../features/types.ts';\nimport { progress } from '../../output/progress.ts';\nimport type { ParsedDoc } from '../../scan/index.ts';\nimport { listFiles, RESERVED_COLUMNS } from '../../scan/index.ts';\nimport { reparseFiles } from '../../scan/reparse.ts';\nimport { getColumns, quoteIdent } from '../shared.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Connection } from '../types.ts';\n\n// Mirrors src/store/sqlite/reconcile.ts's frontmatter-upsert shape (dynamic ALTER TABLE per\n// discovered key, ON CONFLICT upsert keeping the row's identity stable). `content` is a plain\n// table, not FTS-virtual (D1: DuckDB's fts index is built lazily, on first lexical query, from\n// this table -- see duckdb/lexical.ts), so its rows are maintained here unconditionally. The one\n// forced divergence for frontmatter itself is the ALTER TABLE type: DuckDB requires a declared\n// column type where sqlite accepts none, so every dynamic frontmatter column is VARIANT (the\n// only DuckDB type that can hold the different JS types mapValue() produces across files for\n// the same key).\nconst CORE_FRONTMATTER_COLUMNS = new Set(['path', '_mtime', '_ctime', '_size', '_parse_error']);\n\n// No rowid coupling needed (unlike sqlite's content, which links to frontmatter's rowid):\n// `path` is content's own primary key, so this is a plain per-doc row.\nconst INSERT_CONTENT_SQL = `INSERT INTO content (\"path\", title, summary, text) VALUES (?, ?, ?, ?)`;\n\nfunction contentRow(doc: ParsedDoc): unknown[] {\n return [doc.relPath, doc.search.title, doc.search.summary, doc.search.text];\n}\n\n// No compile-time column cap in DuckDB (unlike SQLite's SQLITE_MAX_COLUMN); kept as a sanity\n// fence anyway so a runaway frontmatter generator fails with a clear message instead of an\n// unbounded ALTER TABLE loop.\nconst MAX_FRONTMATTER_COLUMNS = 10_000;\n\nexport async function reconcile(conn: Connection, cfg: Config, baseDir: string): Promise<{ parsed: number; warnings: string[] }> {\n const files = listFiles(cfg, baseDir);\n const currentSet = new Set(files.map((f) => f.relPath));\n\n const existingStmt = await conn.prepare('SELECT \"path\", \"_mtime\", \"_size\" FROM frontmatter');\n const existingRows = (await existingStmt.all()) as Array<{ path: string; _mtime: number; _size: number }>;\n const existing = new Map(existingRows.map((r) => [r.path, r]));\n const vanished = existingRows.filter((r) => !currentSet.has(r.path)).map((r) => r.path);\n\n const toReparse = files.filter((f) => {\n const row = existing.get(f.relPath);\n return !row || row._mtime !== f.mtimeMs || row._size !== f.size;\n });\n\n if (vanished.length === 0 && toReparse.length === 0) return { parsed: 0, warnings: [] };\n\n const features = activeFeatures(cfg);\n const seenColumns = await getColumns(conn);\n\n const report = progress('reparsing files', toReparse.length);\n const { docs: parsedDocs, warnings, newColumns } = await reparseFiles(toReparse, features, cfg, seenColumns, report.tick);\n report.finish();\n for (const col of newColumns) seenColumns.add(col);\n\n const allColumns = [...seenColumns];\n if (allColumns.length > MAX_FRONTMATTER_COLUMNS) {\n throw new SenseError('COLUMN_LIMIT', `frontmatter would need ${allColumns.length} columns, crossing this store's sanity limit (${MAX_FRONTMATTER_COLUMNS}). Narrow the presets' include globs so fewer/other files are indexed, or fix whatever is generating unbounded frontmatter keys.`);\n }\n const writableColumns = allColumns.filter((c) => CORE_FRONTMATTER_COLUMNS.has(c) || !RESERVED_COLUMNS.has(c));\n const insertSql = `INSERT INTO frontmatter (${writableColumns.map(quoteIdent).join(', ')}) VALUES (${writableColumns.map(() => '?').join(', ')}) ON CONFLICT(\"path\") DO UPDATE SET ${writableColumns\n .filter((c) => c !== 'path')\n .map((c) => `${quoteIdent(c)} = excluded.${quoteIdent(c)}`)\n .join(', ')}`;\n\n const added = toReparse.filter((f) => !existing.has(f.relPath)).map((f) => f.relPath);\n const delta: ReconcileDelta = { files, reparsed: parsedDocs.map((d) => d.relPath), added, vanished };\n const addedSet = new Set(added);\n const reparsedExisting = parsedDocs.map((d) => d.relPath).filter((p) => !addedSet.has(p));\n\n await withTransaction(conn, async () => {\n // DuckDB's ALTER TABLE ADD COLUMN requires a type; VARIANT is the only one that can hold\n // the mixed bigint/number/string/null shapes mapValue() produces for one key across files.\n for (const col of newColumns) await conn.exec(`ALTER TABLE frontmatter ADD COLUMN ${quoteIdent(col)} VARIANT`);\n\n if (parsedDocs.length > 0) {\n const rows = parsedDocs.map((doc) =>\n writableColumns.map((col) => {\n if (col === 'path') return doc.relPath;\n if (col === '_mtime') return doc.mtimeMs;\n if (col === '_ctime') return doc.ctimeMs;\n if (col === '_size') return doc.size;\n if (col === '_parse_error') return doc.parseError;\n return doc.data[col] ?? null;\n })\n );\n await conn.runBatch(insertSql, rows);\n }\n\n const contentTouched = [...vanished, ...reparsedExisting];\n if (contentTouched.length > 0)\n await conn.runBatch(\n 'DELETE FROM content WHERE \"path\" = ?',\n contentTouched.map((p) => [p])\n );\n if (parsedDocs.length > 0) await conn.runBatch(INSERT_CONTENT_SQL, parsedDocs.map(contentRow));\n\n if (vanished.length > 0)\n await conn.runBatch(\n 'DELETE FROM frontmatter WHERE \"path\" = ?',\n vanished.map((p) => [p])\n );\n\n // A preset edit forces a full rebuild, so an unchanged doc's coverage is already correct;\n // new docs have nothing to clear, which keeps cold builds linear.\n const presetTouched = [...vanished, ...reparsedExisting];\n if (presetTouched.length > 0)\n await conn.runBatch(\n 'DELETE FROM preset_files WHERE \"path\" = ?',\n presetTouched.map((p) => [p])\n );\n const presetRows: unknown[][] = [];\n for (const doc of parsedDocs) for (const presetName of doc.presets) presetRows.push([doc.relPath, presetName]);\n if (presetRows.length > 0) await conn.runBatch('INSERT INTO preset_files (\"path\", preset) VALUES (?, ?)', presetRows);\n\n const removedPaths = [...vanished, ...reparsedExisting];\n if (removedPaths.length > 0) for (const feature of features) await feature.remove?.(conn, removedPaths, delta);\n for (const feature of features) {\n const docsForFeature: ExtractedDoc[] = parsedDocs.map((doc) => ({ path: doc.relPath, extracted: doc.extracted[feature.name] }));\n await feature.store?.(conn, docsForFeature, delta);\n }\n for (const feature of features) await feature.afterReconcile?.(conn, delta);\n });\n\n return { parsed: parsedDocs.length, warnings };\n}\n"],"names":["SenseError","activeFeatures","progress","listFiles","RESERVED_COLUMNS","reparseFiles","getColumns","quoteIdent","withTransaction","CORE_FRONTMATTER_COLUMNS","Set","INSERT_CONTENT_SQL","contentRow","doc","relPath","search","title","summary","text","MAX_FRONTMATTER_COLUMNS","reconcile","conn","cfg","baseDir","files","currentSet","map","f","existingStmt","prepare","existingRows","all","existing","Map","r","path","vanished","filter","has","toReparse","row","get","_mtime","mtimeMs","_size","size","length","parsed","warnings","features","seenColumns","report","docs","parsedDocs","newColumns","tick","finish","col","add","allColumns","writableColumns","c","insertSql","join","added","delta","reparsed","d","addedSet","reparsedExisting","p","feature","exec","rows","ctimeMs","parseError","data","runBatch","contentTouched","presetTouched","presetRows","presetName","presets","push","removedPaths","remove","docsForFeature","extracted","name","store","afterReconcile"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ResolvedConfig } from '../config/index.js';
|
|
2
2
|
import type { OpenResult } from './sqlite/open.js';
|
|
3
3
|
export declare function openStore(cfg: ResolvedConfig): Promise<OpenResult>;
|
|
4
|
+
export declare function requireWatchConcurrency(cfg: ResolvedConfig): void;
|
|
4
5
|
export { getMeta, setMeta } from './meta.js';
|
|
5
6
|
export type { OpenResult } from './sqlite/open.js';
|
|
6
7
|
export { clearCache, DB_FILENAME, docCount, SCHEMA_VERSION } from './sqlite/open.js';
|