sensemaking 0.19.2 → 0.21.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/README.md +0 -1
- package/dist/cjs/chunk/extract.d.cts +1 -0
- package/dist/cjs/chunk/extract.d.ts +1 -0
- package/dist/cjs/chunk/extract.js +23 -5
- package/dist/cjs/chunk/extract.js.map +1 -1
- package/dist/cjs/chunk/index.d.cts +1 -1
- package/dist/cjs/chunk/index.d.ts +1 -1
- package/dist/cjs/chunk/index.js +3 -0
- package/dist/cjs/chunk/index.js.map +1 -1
- package/dist/cjs/config/access.d.cts +0 -1
- package/dist/cjs/config/access.d.ts +0 -1
- package/dist/cjs/config/access.js +10 -17
- package/dist/cjs/config/access.js.map +1 -1
- package/dist/cjs/config/index.d.cts +1 -1
- package/dist/cjs/config/index.d.ts +1 -1
- package/dist/cjs/config/index.js +0 -3
- package/dist/cjs/config/index.js.map +1 -1
- package/dist/cjs/config/types.d.cts +0 -3
- package/dist/cjs/config/types.d.ts +0 -3
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/config/validate.js +6 -24
- package/dist/cjs/config/validate.js.map +1 -1
- package/dist/cjs/errors.d.cts +1 -1
- package/dist/cjs/errors.d.ts +1 -1
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/features/links.js +4 -5
- package/dist/cjs/features/links.js.map +1 -1
- package/dist/cjs/features/sections.js +4 -1
- package/dist/cjs/features/sections.js.map +1 -1
- package/dist/cjs/scan/pool.d.cts +15 -0
- package/dist/cjs/scan/pool.d.ts +15 -0
- package/dist/cjs/scan/pool.js +263 -0
- package/dist/cjs/scan/pool.js.map +1 -0
- package/dist/cjs/scan/reparse.d.cts +2 -0
- package/dist/cjs/scan/reparse.d.ts +2 -0
- package/dist/cjs/scan/reparse.js +15 -76
- package/dist/cjs/scan/reparse.js.map +1 -1
- package/dist/cjs/store/builder.d.cts +19 -0
- package/dist/cjs/store/builder.d.ts +19 -0
- package/dist/cjs/store/builder.js +618 -0
- package/dist/cjs/store/builder.js.map +1 -0
- package/dist/cjs/store/duckdb/lexical.d.cts +1 -0
- package/dist/cjs/store/duckdb/lexical.d.ts +1 -0
- package/dist/cjs/store/duckdb/lexical.js +21 -5
- package/dist/cjs/store/duckdb/lexical.js.map +1 -1
- package/dist/cjs/store/duckdb/open.d.cts +9 -8
- package/dist/cjs/store/duckdb/open.d.ts +9 -8
- package/dist/cjs/store/duckdb/open.js +77 -123
- package/dist/cjs/store/duckdb/open.js.map +1 -1
- package/dist/cjs/store/duckdb/reconcile.js +34 -5
- package/dist/cjs/store/duckdb/reconcile.js.map +1 -1
- package/dist/cjs/store/duckdb/store.d.cts +1 -2
- package/dist/cjs/store/duckdb/store.d.ts +1 -2
- package/dist/cjs/store/duckdb/store.js +1 -26
- package/dist/cjs/store/duckdb/store.js.map +1 -1
- package/dist/cjs/store/embed-scope.d.cts +2 -0
- package/dist/cjs/store/embed-scope.d.ts +2 -0
- package/dist/cjs/store/embed-scope.js +85 -0
- package/dist/cjs/store/embed-scope.js.map +1 -0
- package/dist/cjs/store/feature-scope.d.cts +8 -0
- package/dist/cjs/store/feature-scope.d.ts +8 -0
- package/dist/cjs/store/feature-scope.js +107 -0
- package/dist/cjs/store/feature-scope.js.map +1 -0
- package/dist/cjs/store/index.d.cts +8 -3
- package/dist/cjs/store/index.d.ts +8 -3
- package/dist/cjs/store/index.js +36 -7
- package/dist/cjs/store/index.js.map +1 -1
- package/dist/cjs/store/open.d.cts +15 -0
- package/dist/cjs/store/open.d.ts +15 -0
- package/dist/cjs/store/open.js +608 -0
- package/dist/cjs/store/open.js.map +1 -0
- package/dist/cjs/store/preset-scope.d.cts +3 -0
- package/dist/cjs/store/preset-scope.d.ts +3 -0
- package/dist/cjs/store/preset-scope.js +293 -0
- package/dist/cjs/store/preset-scope.js.map +1 -0
- package/dist/cjs/store/reconcile.d.cts +2 -1
- package/dist/cjs/store/reconcile.d.ts +2 -1
- package/dist/cjs/store/reconcile.js +175 -179
- package/dist/cjs/store/reconcile.js.map +1 -1
- package/dist/cjs/store/{sqlite/signature.d.cts → signature.d.cts} +0 -3
- package/dist/cjs/store/{sqlite/signature.d.ts → signature.d.ts} +0 -3
- package/dist/cjs/store/signature.js +164 -0
- package/dist/cjs/store/signature.js.map +1 -0
- package/dist/cjs/store/sqlite/lexical.d.cts +1 -1
- package/dist/cjs/store/sqlite/lexical.d.ts +1 -1
- package/dist/cjs/store/sqlite/lexical.js +2 -2
- package/dist/cjs/store/sqlite/lexical.js.map +1 -1
- package/dist/cjs/store/sqlite/open.d.cts +8 -9
- package/dist/cjs/store/sqlite/open.d.ts +8 -9
- package/dist/cjs/store/sqlite/open.js +82 -311
- package/dist/cjs/store/sqlite/open.js.map +1 -1
- package/dist/cjs/store/sqlite/reconcile.d.cts +1 -1
- package/dist/cjs/store/sqlite/reconcile.d.ts +1 -1
- package/dist/cjs/store/sqlite/reconcile.js +78 -16
- package/dist/cjs/store/sqlite/reconcile.js.map +1 -1
- package/dist/cjs/store/sqlite/sql-functions.d.cts +1 -1
- package/dist/cjs/store/sqlite/sql-functions.d.ts +1 -1
- package/dist/cjs/store/sqlite/sql-functions.js +3 -3
- package/dist/cjs/store/sqlite/sql-functions.js.map +1 -1
- package/dist/cjs/store/sqlite/store.d.cts +1 -2
- package/dist/cjs/store/sqlite/store.d.ts +1 -2
- package/dist/cjs/store/sqlite/store.js +2 -15
- package/dist/cjs/store/sqlite/store.js.map +1 -1
- package/dist/cjs/store/turso/open.d.cts +6 -11
- package/dist/cjs/store/turso/open.d.ts +6 -11
- package/dist/cjs/store/turso/open.js +85 -171
- package/dist/cjs/store/turso/open.js.map +1 -1
- package/dist/cjs/store/turso/reconcile.d.cts +2 -0
- package/dist/cjs/store/turso/reconcile.d.ts +2 -0
- package/dist/cjs/store/turso/reconcile.js +88 -8
- package/dist/cjs/store/turso/reconcile.js.map +1 -1
- package/dist/cjs/store/turso/store.d.cts +1 -2
- package/dist/cjs/store/turso/store.d.ts +1 -2
- package/dist/cjs/store/turso/store.js +1 -13
- package/dist/cjs/store/turso/store.js.map +1 -1
- package/dist/cjs/store/types.d.cts +16 -6
- package/dist/cjs/store/types.d.ts +16 -6
- package/dist/cjs/store/types.js.map +1 -1
- package/dist/cjs/text/strip.js +3 -6
- package/dist/cjs/text/strip.js.map +1 -1
- package/dist/cjs/watch.js +28 -16
- package/dist/cjs/watch.js.map +1 -1
- package/dist/esm/chunk/extract.d.ts +1 -0
- package/dist/esm/chunk/extract.js +10 -2
- package/dist/esm/chunk/extract.js.map +1 -1
- package/dist/esm/chunk/index.d.ts +1 -1
- package/dist/esm/chunk/index.js +1 -1
- package/dist/esm/chunk/index.js.map +1 -1
- package/dist/esm/config/access.d.ts +0 -1
- package/dist/esm/config/access.js +12 -17
- package/dist/esm/config/access.js.map +1 -1
- package/dist/esm/config/index.d.ts +1 -1
- package/dist/esm/config/index.js +1 -1
- package/dist/esm/config/index.js.map +1 -1
- package/dist/esm/config/types.d.ts +0 -3
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/config/validate.js +6 -22
- package/dist/esm/config/validate.js.map +1 -1
- package/dist/esm/errors.d.ts +1 -1
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/features/links.js +3 -4
- package/dist/esm/features/links.js.map +1 -1
- package/dist/esm/features/sections.js +4 -1
- package/dist/esm/features/sections.js.map +1 -1
- package/dist/esm/scan/pool.d.ts +15 -0
- package/dist/esm/scan/pool.js +77 -0
- package/dist/esm/scan/pool.js.map +1 -0
- package/dist/esm/scan/reparse.d.ts +2 -0
- package/dist/esm/scan/reparse.js +11 -58
- package/dist/esm/scan/reparse.js.map +1 -1
- package/dist/esm/store/builder.d.ts +19 -0
- package/dist/esm/store/builder.js +163 -0
- package/dist/esm/store/builder.js.map +1 -0
- package/dist/esm/store/duckdb/lexical.d.ts +1 -0
- package/dist/esm/store/duckdb/lexical.js +22 -9
- package/dist/esm/store/duckdb/lexical.js.map +1 -1
- package/dist/esm/store/duckdb/open.d.ts +9 -8
- package/dist/esm/store/duckdb/open.js +36 -58
- package/dist/esm/store/duckdb/open.js.map +1 -1
- package/dist/esm/store/duckdb/reconcile.js +15 -3
- package/dist/esm/store/duckdb/reconcile.js.map +1 -1
- package/dist/esm/store/duckdb/store.d.ts +1 -2
- package/dist/esm/store/duckdb/store.js +2 -11
- package/dist/esm/store/duckdb/store.js.map +1 -1
- package/dist/esm/store/embed-scope.d.ts +2 -0
- package/dist/esm/store/embed-scope.js +28 -0
- package/dist/esm/store/embed-scope.js.map +1 -0
- package/dist/esm/store/feature-scope.d.ts +8 -0
- package/dist/esm/store/feature-scope.js +49 -0
- package/dist/esm/store/feature-scope.js.map +1 -0
- package/dist/esm/store/index.d.ts +8 -3
- package/dist/esm/store/index.js +22 -7
- package/dist/esm/store/index.js.map +1 -1
- package/dist/esm/store/open.d.ts +15 -0
- package/dist/esm/store/open.js +187 -0
- package/dist/esm/store/open.js.map +1 -0
- package/dist/esm/store/preset-scope.d.ts +3 -0
- package/dist/esm/store/preset-scope.js +77 -0
- package/dist/esm/store/preset-scope.js.map +1 -0
- package/dist/esm/store/reconcile.d.ts +2 -1
- package/dist/esm/store/reconcile.js +33 -6
- package/dist/esm/store/reconcile.js.map +1 -1
- package/dist/esm/store/{sqlite/signature.d.ts → signature.d.ts} +0 -3
- package/dist/esm/store/signature.js +37 -0
- package/dist/esm/store/signature.js.map +1 -0
- package/dist/esm/store/sqlite/lexical.d.ts +1 -1
- package/dist/esm/store/sqlite/lexical.js +4 -4
- package/dist/esm/store/sqlite/lexical.js.map +1 -1
- package/dist/esm/store/sqlite/open.d.ts +8 -9
- package/dist/esm/store/sqlite/open.js +41 -147
- package/dist/esm/store/sqlite/open.js.map +1 -1
- package/dist/esm/store/sqlite/reconcile.d.ts +1 -1
- package/dist/esm/store/sqlite/reconcile.js +14 -14
- package/dist/esm/store/sqlite/reconcile.js.map +1 -1
- package/dist/esm/store/sqlite/sql-functions.d.ts +1 -1
- package/dist/esm/store/sqlite/sql-functions.js +3 -3
- package/dist/esm/store/sqlite/sql-functions.js.map +1 -1
- package/dist/esm/store/sqlite/store.d.ts +1 -2
- package/dist/esm/store/sqlite/store.js +3 -9
- package/dist/esm/store/sqlite/store.js.map +1 -1
- package/dist/esm/store/turso/open.d.ts +6 -11
- package/dist/esm/store/turso/open.js +34 -78
- package/dist/esm/store/turso/open.js.map +1 -1
- package/dist/esm/store/turso/reconcile.d.ts +2 -0
- package/dist/esm/store/turso/reconcile.js +18 -3
- package/dist/esm/store/turso/reconcile.js.map +1 -1
- package/dist/esm/store/turso/store.d.ts +1 -2
- package/dist/esm/store/turso/store.js +2 -7
- package/dist/esm/store/turso/store.js.map +1 -1
- package/dist/esm/store/types.d.ts +16 -6
- package/dist/esm/store/types.js.map +1 -1
- package/dist/esm/text/strip.js +3 -4
- package/dist/esm/text/strip.js.map +1 -1
- package/dist/esm/watch.js +5 -3
- package/dist/esm/watch.js.map +1 -1
- package/package.json +4 -3
- package/schema.json +0 -11
- package/dist/cjs/store/sqlite/signature.js +0 -380
- package/dist/cjs/store/sqlite/signature.js.map +0 -1
- package/dist/esm/store/sqlite/signature.js +0 -58
- package/dist/esm/store/sqlite/signature.js.map +0 -1
package/dist/cjs/errors.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type SenseErrorCode = 'CONFIG_NOT_FOUND' | 'CONFIG_EXISTS' | 'CONFIG_INVALID' | 'CONFIG_VERSION_UNSUPPORTED' | 'WATCH_ACTIVE' | 'NOTE_NOT_FOUND' | 'NOTE_AMBIGUOUS' | 'EMBED_DISABLED' | 'EMBED_MODEL' | 'EMBED_MODEL_MISSING' | 'EMBED_MODEL_MISMATCH' | 'SEARCH_SYNTAX' | 'COLUMN_LIMIT' | 'PRESET_UNKNOWN' | 'PRESET_NOT_SEMANTIC' | 'NOTE_NOT_EMBEDDED' | 'STORE_UNKNOWN' | 'STORE_DEPENDENCY_MISSING' | 'STORE_CAPABILITY_MISSING' | 'LEXICAL_NULL_PATH';
|
|
1
|
+
export type SenseErrorCode = 'CONFIG_NOT_FOUND' | 'CONFIG_EXISTS' | 'CONFIG_INVALID' | 'CONFIG_VERSION_UNSUPPORTED' | 'WATCH_ACTIVE' | 'NOTE_NOT_FOUND' | 'NOTE_AMBIGUOUS' | 'EMBED_DISABLED' | 'EMBED_MODEL' | 'EMBED_MODEL_MISSING' | 'EMBED_MODEL_MISMATCH' | 'SEARCH_SYNTAX' | 'COLUMN_LIMIT' | 'PRESET_UNKNOWN' | 'PRESET_NOT_SEMANTIC' | 'NOTE_NOT_EMBEDDED' | 'STORE_UNKNOWN' | 'STORE_DEPENDENCY_MISSING' | 'STORE_CAPABILITY_MISSING' | 'STORE_BUSY' | 'LEXICAL_NULL_PATH';
|
|
2
2
|
export declare class SenseError extends Error {
|
|
3
3
|
code: SenseErrorCode;
|
|
4
4
|
constructor(code: SenseErrorCode, message: string);
|
package/dist/cjs/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/errors.ts"],"sourcesContent":["// Library code throws; only cli.ts prints and exits.\nexport type SenseErrorCode =\n | 'CONFIG_NOT_FOUND'\n | 'CONFIG_EXISTS'\n | 'CONFIG_INVALID'\n | 'CONFIG_VERSION_UNSUPPORTED'\n | 'WATCH_ACTIVE'\n | 'NOTE_NOT_FOUND'\n | 'NOTE_AMBIGUOUS'\n | 'EMBED_DISABLED'\n | 'EMBED_MODEL'\n | 'EMBED_MODEL_MISSING'\n | 'EMBED_MODEL_MISMATCH'\n | 'SEARCH_SYNTAX'\n | 'COLUMN_LIMIT'\n | 'PRESET_UNKNOWN'\n | 'PRESET_NOT_SEMANTIC'\n | 'NOTE_NOT_EMBEDDED'\n | 'STORE_UNKNOWN'\n | 'STORE_DEPENDENCY_MISSING'\n | 'STORE_CAPABILITY_MISSING'\n | 'LEXICAL_NULL_PATH';\n\nexport class SenseError extends Error {\n code: SenseErrorCode;\n\n constructor(code: SenseErrorCode, message: string) {\n super(message);\n this.name = 'SenseError';\n this.code = code;\n }\n}\n"],"names":["SenseError","code","message","name","Error"],"mappings":"AAAA,qDAAqD;;;;;+
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/errors.ts"],"sourcesContent":["// Library code throws; only cli.ts prints and exits.\nexport type SenseErrorCode =\n | 'CONFIG_NOT_FOUND'\n | 'CONFIG_EXISTS'\n | 'CONFIG_INVALID'\n | 'CONFIG_VERSION_UNSUPPORTED'\n | 'WATCH_ACTIVE'\n | 'NOTE_NOT_FOUND'\n | 'NOTE_AMBIGUOUS'\n | 'EMBED_DISABLED'\n | 'EMBED_MODEL'\n | 'EMBED_MODEL_MISSING'\n | 'EMBED_MODEL_MISMATCH'\n | 'SEARCH_SYNTAX'\n | 'COLUMN_LIMIT'\n | 'PRESET_UNKNOWN'\n | 'PRESET_NOT_SEMANTIC'\n | 'NOTE_NOT_EMBEDDED'\n | 'STORE_UNKNOWN'\n | 'STORE_DEPENDENCY_MISSING'\n | 'STORE_CAPABILITY_MISSING'\n | 'STORE_BUSY'\n | 'LEXICAL_NULL_PATH';\n\nexport class SenseError extends Error {\n code: SenseErrorCode;\n\n constructor(code: SenseErrorCode, message: string) {\n super(message);\n this.name = 'SenseError';\n this.code = code;\n }\n}\n"],"names":["SenseError","code","message","name","Error"],"mappings":"AAAA,qDAAqD;;;;;+BAwBxCA;;;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAN,IAAA,AAAMA,2BAAN;;cAAMA;aAAAA,WAGCC,IAAoB,EAAEC,OAAe;gCAHtCF;;gBAIT,kBAJSA;YAIHE;;QACN,MAAKC,IAAI,GAAG;QACZ,MAAKF,IAAI,GAAGA;;;WANHD;qBAAmBI"}
|
|
@@ -946,17 +946,16 @@ var links = {
|
|
|
946
946
|
},
|
|
947
947
|
afterReconcile: function afterReconcile(db, delta) {
|
|
948
948
|
return _async_to_generator(function() {
|
|
949
|
-
var _removedWithLinks_get, removeHadLinks,
|
|
949
|
+
var _removedWithLinks_get, removeHadLinks, large, dstChanged, _tmp;
|
|
950
950
|
return _ts_generator(this, function(_state) {
|
|
951
951
|
switch(_state.label){
|
|
952
952
|
case 0:
|
|
953
953
|
// Any deleted rows mean the edge set shrank -- whether the file vanished or was reparsed
|
|
954
954
|
// down to fewer/no links.
|
|
955
955
|
removeHadLinks = (_removedWithLinks_get = removedWithLinks.get(delta)) !== null && _removedWithLinks_get !== void 0 ? _removedWithLinks_get : false;
|
|
956
|
-
|
|
957
|
-
//
|
|
958
|
-
|
|
959
|
-
large = delta.files.length === 0 || churn > 0.2 * delta.files.length;
|
|
956
|
+
// A cold build is the only case a full pass wins. Measured 2026-09-01 on 6,566 notes, resolve
|
|
957
|
+
// time alone: full is flat near 90ms at any churn, incremental 7ms at 10 files and 30ms at 2,000.
|
|
958
|
+
large = delta.files.length === 0 || delta.added.length === delta.files.length;
|
|
960
959
|
if (!large) return [
|
|
961
960
|
3,
|
|
962
961
|
2
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/links.ts"],"sourcesContent":["import posix from 'node:path/posix';\nimport type { FileStat } from '../scan/index.ts';\nimport type { Connection } from '../store/types.ts';\nimport { maskRegions } from './fences.ts';\nimport type { ExtractedDoc, Feature, ReconcileDelta } from './types.ts';\n\n// links(src, target, target_base, dst, embed): target as written, target_base its indexed baseKey, dst the resolved path or NULL (a queryable dead link).\n// embed distinguishes `![[x]]`/`` from `[[x]]`/`[](x.md)` -- Obsidian's grain: a target both linked and embedded in one note is two rows.\n\n// Alias stripped after |; a leading # keeps the anchor as the target (self-link needs a\n// heading name), else the anchor splits off. Null = not a link.\nfunction parseWikilinkInner(inner: string): string | null {\n const beforeAlias = inner.split('|')[0].trim();\n if (beforeAlias.startsWith('#')) return beforeAlias.length > 1 ? beforeAlias : null;\n const target = beforeAlias.split('#')[0].trim();\n return target || null;\n}\n\n// A frontmatter value counts only when it is exactly a wikilink (\"[[X]]\") -- mid-string and\n// ![[...]] forms don't count there, per Obsidian's rule (probe-verified).\nfunction extract(_raw: string, rawBody: string, _search?: { title: string; summary: string }, data?: Record<string, unknown>): Array<{ target: string; embed: boolean }> {\n const body = /\\[\\[|\\]\\(/.test(rawBody) ? maskRegions(rawBody) : rawBody;\n const seen = new Set<string>();\n const results: Array<{ target: string; embed: boolean }> = [];\n const add = (target: string, embed: boolean) => {\n const key = `${target}\\0${embed ? '1' : '0'}`;\n if (seen.has(key)) return;\n seen.add(key);\n results.push({ target, embed });\n };\n // To the first ]], as Obsidian parses it, so a heading or alias holding a lone ] still\n // matches; anchor and alias split off in parseWikilinkInner.\n for (const m of body.matchAll(/\\[\\[(.*?)\\]\\]/g)) {\n const target = parseWikilinkInner(m[1]);\n if (target) add(target, body[(m.index ?? 0) - 1] === '!');\n }\n // Any internal destination, not only .md-suffixed: \\](Zektor) resolves like [[Zektor]] and\n // \\](#anchor) is a self-edge. External URLs and titled links (dest stops at whitespace) skip.\n for (const m of body.matchAll(/(!)?\\[[^\\]]*\\]\\(((?:[^()\\s]|\\([^()\\s]*\\))+)(?:\\s+(?:\"[^)]*\"|'[^)]*'))?\\)/g)) {\n const dest = m[2].trim();\n // External: a URI scheme prefix (mailto:, tel:, data:, https:...), protocol-relative\n // //host, a www. shorthand, or a scheme anywhere (the malformed double-paren case).\n if (!dest || dest.includes('://')) continue;\n if (/^([a-z][a-z0-9+.-]*:|\\/\\/|www\\.)/i.test(dest)) continue;\n const target = dest.startsWith('#') ? dest : dest.split('#')[0];\n if (target) add(target, m[1] === '!');\n }\n for (const value of Object.values(data ?? {})) {\n for (const item of Array.isArray(value) ? value : [value]) {\n if (typeof item !== 'string') continue;\n const m = /^\\[\\[(.*)\\]\\]$/.exec(item.trim());\n if (!m) continue;\n const target = parseWikilinkInner(m[1]);\n if (target) add(target, false);\n }\n }\n return results;\n}\n\nfunction cleanTarget(target: string): string {\n return target.replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n}\n\nfunction baseKey(path: string): string {\n return posix.basename(path).replace(/\\.md$/i, '').toLowerCase();\n}\n\n// Obsidian-style: exact relative path (with/without .md), path relative to the linking note's\n// directory, then basename match (shortest path wins ties, verified against a cold-loaded vault).\nfunction resolveTarget(src: string, target: string, pathSet: Set<string>, byBase: Map<string, string[]>): string | null {\n // [[#Heading]]: a same-note anchor, always the note itself -- never depends on other files.\n if (target.startsWith('#')) return src;\n const clean = cleanTarget(target);\n const fromSrc = posix.normalize(posix.join(posix.dirname(src), clean));\n for (const candidate of [clean, `${clean}.md`, fromSrc, `${fromSrc}.md`]) {\n if (pathSet.has(candidate)) return candidate;\n }\n const candidates = byBase.get(baseKey(clean));\n if (!candidates) return null;\n // The linking note itself wins a basename collision (Obsidian resolves to self before the\n // shortest-path rule -- cold-load verified on the hub corpus).\n return candidates.includes(src) ? src : candidates[0];\n}\n\n// Shortest path wins a basename collision; equal lengths fall back to lexicographic, our own\n// deterministic tiebreak for a case Obsidian itself leaves registration-order-dependent.\nfunction buildByBase(files: FileStat[]): Map<string, string[]> {\n const byBase = new Map<string, string[]>();\n const paths = files.map((f) => f.relPath).sort((a, b) => a.length - b.length || (a < b ? -1 : a > b ? 1 : 0));\n for (const path of paths) {\n const key = baseKey(path);\n const list = byBase.get(key);\n if (list) list.push(path);\n else byBase.set(key, [path]);\n }\n return byBase;\n}\n\ninterface LinkRow {\n src: string;\n target: string;\n dst: string | null;\n embed: number;\n}\n\n// resolveTarget applied to `rows`, returning only the rows whose dst changed, so the caller\n// can write them in one runBatch call.\nfunction resolveRows(rows: LinkRow[], pathSet: Set<string>, byBase: Map<string, string[]>): LinkRow[] {\n const changed: LinkRow[] = [];\n for (const row of rows) {\n const dst = resolveTarget(row.src, row.target, pathSet, byBase);\n if (dst !== row.dst) changed.push({ ...row, dst });\n }\n return changed;\n}\n\nasync function writeResolved(db: Connection, rows: LinkRow[]): Promise<void> {\n if (rows.length === 0) return;\n await db.runBatch(\n 'UPDATE links SET dst = ? WHERE src = ? AND target = ?',\n rows.map((r) => [r.dst, r.src, r.target])\n );\n}\n\n// A new or deleted file can change any note's resolution, so re-resolve the whole table.\n// Fallback for cold builds and large deltas -- see afterReconcile's threshold.\nasync function resolveAll(db: Connection, files: FileStat[]): Promise<boolean> {\n const pathSet = new Set(files.map((f) => f.relPath));\n const byBase = buildByBase(files);\n const stmt = await db.prepare('SELECT src, target, dst, embed FROM links');\n const rows = (await stmt.all()) as unknown as LinkRow[];\n const changed = resolveRows(rows, pathSet, byBase);\n await writeResolved(db, changed);\n return changed.length > 0;\n}\n\n// Chunked so a large-but-under-threshold delta never exceeds SQLite's bound-variable limit\n// (SQLITE_MAX_VARIABLE_NUMBER, 32766); harmless headroom on DuckDB, which has no such cap.\nasync function selectIn(db: Connection, column: string, keys: string[]): Promise<LinkRow[]> {\n const rows: LinkRow[] = [];\n for (let i = 0; i < keys.length; i += 500) {\n const chunk = keys.slice(i, i + 500);\n const stmt = await db.prepare(`SELECT src, target, dst, embed FROM links WHERE ${column} IN (${chunk.map(() => '?').join(', ')})`);\n rows.push(...((await stmt.all(...chunk)) as unknown as LinkRow[]));\n }\n return rows;\n}\n\n// Re-resolves only what this reconcile could have touched: re-stored sources, links whose\n// target basename matches an added/vanished file, and links whose dst vanished -- all indexed.\nasync function resolveIncremental(db: Connection, delta: ReconcileDelta): Promise<boolean> {\n const pathSet = new Set(delta.files.map((f) => f.relPath));\n const byBase = buildByBase(delta.files);\n\n const changedBasenames = new Set<string>();\n for (const p of delta.added) changedBasenames.add(baseKey(p));\n for (const p of delta.vanished) changedBasenames.add(baseKey(p));\n\n // \\0 can't appear in a path or target, so the key never collides; a space can.\n const candidates = new Map<string, LinkRow>();\n const collect = (rows: LinkRow[]) => {\n for (const row of rows) candidates.set(`${row.src}\\0${row.target}\\0${row.embed}`, row);\n };\n collect(await selectIn(db, 'src', delta.reparsed));\n collect(await selectIn(db, 'target_base', [...changedBasenames]));\n collect(await selectIn(db, 'dst', delta.vanished));\n\n const changed = resolveRows([...candidates.values()], pathSet, byBase);\n await writeResolved(db, changed);\n return changed.length > 0;\n}\n\n// One edge per (src, target); an embed row is excluded only when the same pair also exists as\n// a link. Self-edges are dropped so PageRank mass is not self-recycled.\nexport const LINK_EDGES_SQL = `SELECT src, dst FROM links WHERE dst IS NOT NULL AND embed = 0 AND src != dst\n UNION ALL\n SELECT src, dst FROM links l WHERE dst IS NOT NULL AND embed = 1 AND src != dst\n AND NOT EXISTS (SELECT 1 FROM links l0 WHERE l0.src = l.src AND l0.target = l.target AND l0.embed = 0)`;\n\nexport function toEdges(rows: Array<{ src: string; dst: string }>): [string, string][] {\n return rows.map((r) => [r.src, r.dst]);\n}\n\n// Resolved edges, for rank (inside reconcile's afterReconcile pass).\nexport async function linkEdges(db: Connection): Promise<[string, string][]> {\n const stmt = await db.prepare(LINK_EDGES_SQL);\n return toEdges((await stmt.all()) as Array<{ src: string; dst: string }>);\n}\n\n// Records whether remove()/store() deleted any link row, since afterReconcile runs after both\n// and dstChanged alone misses the reparsed-to-zero-links case. Keyed on delta so state dies with it.\nconst removedWithLinks = new WeakMap<ReconcileDelta, boolean>();\n\nexport const links: Feature = {\n name: 'links',\n async schema(db) {\n await db.exec('CREATE TABLE IF NOT EXISTS links (src TEXT, target TEXT, target_base TEXT, dst TEXT, embed INTEGER, PRIMARY KEY (src, target, embed))');\n await db.exec('CREATE INDEX IF NOT EXISTS links_dst ON links(dst)');\n await db.exec('CREATE INDEX IF NOT EXISTS links_target_base ON links(target_base)');\n },\n extract,\n // Reparsed docs are diffed in store() instead of wiped here: delete-and-reinsert resets dst\n // to NULL, making every touch-only reparse look like an edge change and recompute PageRank.\n async remove(db, paths, delta) {\n const vanishedSet = new Set(delta.vanished);\n const vanished = paths.filter((p) => vanishedSet.has(p));\n if (vanished.length === 0) return;\n const existingRows = await selectIn(db, 'src', vanished);\n await db.runBatch(\n 'DELETE FROM links WHERE src = ?',\n vanished.map((p) => [p])\n );\n if (existingRows.length > 0) removedWithLinks.set(delta, true);\n },\n async store(db, docs: ExtractedDoc[], delta) {\n const byPath = new Map(docs.map((d) => [d.path, d.extracted as Array<{ target: string; embed: boolean }>]));\n const addedSet = new Set(delta.added);\n const reparsedExisting = docs.map((d) => d.path).filter((p) => !addedSet.has(p));\n\n // Stale rows (target/embed pairs missing from the new parse) are diffed in JS from one\n // bulk SELECT, so this pass costs one query plus one batched DELETE regardless of file count.\n const existingRows = await selectIn(db, 'src', reparsedExisting);\n const staleRows = existingRows.filter((row) => {\n const targets = byPath.get(row.src) ?? [];\n return !targets.some((t) => t.target === row.target && (t.embed ? 1 : 0) === row.embed);\n });\n if (staleRows.length > 0) {\n await db.runBatch(\n 'DELETE FROM links WHERE src = ? AND target = ? AND embed = ?',\n staleRows.map((r) => [r.src, r.target, r.embed])\n );\n removedWithLinks.set(delta, true);\n }\n\n // Upsert preserves dst on surviving rows; only new rows start at NULL. A same-note anchor\n // gets no target_base, so SQL's `IN` excludes it from resolveIncremental's lookup.\n const upsertRows: unknown[][] = [];\n for (const [path, targets] of byPath) {\n for (const { target, embed } of targets) upsertRows.push([path, target, target.startsWith('#') ? null : baseKey(cleanTarget(target)), embed ? 1 : 0]);\n }\n if (upsertRows.length > 0) {\n await db.runBatch('INSERT INTO links (src, target, target_base, dst, embed) VALUES (?, ?, ?, NULL, ?) ON CONFLICT(src, target, embed) DO UPDATE SET target_base = excluded.target_base', upsertRows);\n }\n },\n async afterReconcile(db, delta) {\n // Any deleted rows mean the edge set shrank -- whether the file vanished or was reparsed\n // down to fewer/no links.\n const removeHadLinks = removedWithLinks.get(delta) ?? false;\n\n const churn = delta.reparsed.length + delta.vanished.length;\n // Past this share of the tree, a full pass is the only one guaranteed to match\n // resolveTarget's ambiguity rules. A cold build clears the threshold on its own.\n const large = delta.files.length === 0 || churn > 0.2 * delta.files.length;\n\n const dstChanged = large ? await resolveAll(db, delta.files) : await resolveIncremental(db, delta);\n delta.linksChanged = dstChanged || removeHadLinks;\n },\n};\n"],"names":["LINK_EDGES_SQL","linkEdges","links","toEdges","parseWikilinkInner","inner","beforeAlias","split","trim","startsWith","length","target","extract","_raw","rawBody","_search","data","body","test","maskRegions","seen","Set","results","add","embed","key","has","push","matchAll","m","index","dest","includes","Object","values","value","Array","isArray","item","exec","cleanTarget","replace","baseKey","path","posix","basename","toLowerCase","resolveTarget","src","pathSet","byBase","clean","fromSrc","normalize","join","dirname","candidate","candidates","get","buildByBase","files","Map","paths","map","f","relPath","sort","a","b","list","set","resolveRows","rows","changed","row","dst","writeResolved","db","runBatch","r","resolveAll","stmt","prepare","all","selectIn","column","keys","i","chunk","slice","resolveIncremental","delta","changedBasenames","p","collect","added","vanished","reparsed","removedWithLinks","WeakMap","name","schema","remove","vanishedSet","existingRows","filter","store","docs","byPath","addedSet","reparsedExisting","staleRows","upsertRows","targets","d","extracted","some","t","afterReconcile","removeHadLinks","churn","large","dstChanged","linksChanged"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/links.ts"],"sourcesContent":["import posix from 'node:path/posix';\nimport type { FileStat } from '../scan/index.ts';\nimport type { Connection } from '../store/types.ts';\nimport { maskRegions } from './fences.ts';\nimport type { ExtractedDoc, Feature, ReconcileDelta } from './types.ts';\n\n// links(src, target, target_base, dst, embed): target as written, target_base its indexed baseKey, dst the resolved path or NULL (a queryable dead link).\n// embed distinguishes `![[x]]`/`` from `[[x]]`/`[](x.md)` -- Obsidian's grain: a target both linked and embedded in one note is two rows.\n\n// Alias stripped after |; a leading # keeps the anchor as the target (self-link needs a\n// heading name), else the anchor splits off. Null = not a link.\nfunction parseWikilinkInner(inner: string): string | null {\n const beforeAlias = inner.split('|')[0].trim();\n if (beforeAlias.startsWith('#')) return beforeAlias.length > 1 ? beforeAlias : null;\n const target = beforeAlias.split('#')[0].trim();\n return target || null;\n}\n\n// A frontmatter value counts only when it is exactly a wikilink (\"[[X]]\") -- mid-string and\n// ![[...]] forms don't count there, per Obsidian's rule (probe-verified).\nfunction extract(_raw: string, rawBody: string, _search?: { title: string; summary: string }, data?: Record<string, unknown>): Array<{ target: string; embed: boolean }> {\n const body = /\\[\\[|\\]\\(/.test(rawBody) ? maskRegions(rawBody) : rawBody;\n const seen = new Set<string>();\n const results: Array<{ target: string; embed: boolean }> = [];\n const add = (target: string, embed: boolean) => {\n const key = `${target}\\0${embed ? '1' : '0'}`;\n if (seen.has(key)) return;\n seen.add(key);\n results.push({ target, embed });\n };\n // To the first ]], as Obsidian parses it, so a heading or alias holding a lone ] still\n // matches; anchor and alias split off in parseWikilinkInner.\n for (const m of body.matchAll(/\\[\\[(.*?)\\]\\]/g)) {\n const target = parseWikilinkInner(m[1]);\n if (target) add(target, body[(m.index ?? 0) - 1] === '!');\n }\n // Any internal destination, not only .md-suffixed: \\](Zektor) resolves like [[Zektor]] and\n // \\](#anchor) is a self-edge. External URLs and titled links (dest stops at whitespace) skip.\n for (const m of body.matchAll(/(!)?\\[[^\\]]*\\]\\(((?:[^()\\s]|\\([^()\\s]*\\))+)(?:\\s+(?:\"[^)]*\"|'[^)]*'))?\\)/g)) {\n const dest = m[2].trim();\n // External: a URI scheme prefix (mailto:, tel:, data:, https:...), protocol-relative\n // //host, a www. shorthand, or a scheme anywhere (the malformed double-paren case).\n if (!dest || dest.includes('://')) continue;\n if (/^([a-z][a-z0-9+.-]*:|\\/\\/|www\\.)/i.test(dest)) continue;\n const target = dest.startsWith('#') ? dest : dest.split('#')[0];\n if (target) add(target, m[1] === '!');\n }\n for (const value of Object.values(data ?? {})) {\n for (const item of Array.isArray(value) ? value : [value]) {\n if (typeof item !== 'string') continue;\n const m = /^\\[\\[(.*)\\]\\]$/.exec(item.trim());\n if (!m) continue;\n const target = parseWikilinkInner(m[1]);\n if (target) add(target, false);\n }\n }\n return results;\n}\n\nfunction cleanTarget(target: string): string {\n return target.replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n}\n\nfunction baseKey(path: string): string {\n return posix.basename(path).replace(/\\.md$/i, '').toLowerCase();\n}\n\n// Obsidian-style: exact relative path (with/without .md), path relative to the linking note's\n// directory, then basename match (shortest path wins ties, verified against a cold-loaded vault).\nfunction resolveTarget(src: string, target: string, pathSet: Set<string>, byBase: Map<string, string[]>): string | null {\n // [[#Heading]]: a same-note anchor, always the note itself -- never depends on other files.\n if (target.startsWith('#')) return src;\n const clean = cleanTarget(target);\n const fromSrc = posix.normalize(posix.join(posix.dirname(src), clean));\n for (const candidate of [clean, `${clean}.md`, fromSrc, `${fromSrc}.md`]) {\n if (pathSet.has(candidate)) return candidate;\n }\n const candidates = byBase.get(baseKey(clean));\n if (!candidates) return null;\n // The linking note itself wins a basename collision (Obsidian resolves to self before the\n // shortest-path rule -- cold-load verified on the hub corpus).\n return candidates.includes(src) ? src : candidates[0];\n}\n\n// Shortest path wins a basename collision; equal lengths fall back to lexicographic, our own\n// deterministic tiebreak for a case Obsidian itself leaves registration-order-dependent.\nfunction buildByBase(files: FileStat[]): Map<string, string[]> {\n const byBase = new Map<string, string[]>();\n const paths = files.map((f) => f.relPath).sort((a, b) => a.length - b.length || (a < b ? -1 : a > b ? 1 : 0));\n for (const path of paths) {\n const key = baseKey(path);\n const list = byBase.get(key);\n if (list) list.push(path);\n else byBase.set(key, [path]);\n }\n return byBase;\n}\n\ninterface LinkRow {\n src: string;\n target: string;\n dst: string | null;\n embed: number;\n}\n\n// resolveTarget applied to `rows`, returning only the rows whose dst changed, so the caller\n// can write them in one runBatch call.\nfunction resolveRows(rows: LinkRow[], pathSet: Set<string>, byBase: Map<string, string[]>): LinkRow[] {\n const changed: LinkRow[] = [];\n for (const row of rows) {\n const dst = resolveTarget(row.src, row.target, pathSet, byBase);\n if (dst !== row.dst) changed.push({ ...row, dst });\n }\n return changed;\n}\n\nasync function writeResolved(db: Connection, rows: LinkRow[]): Promise<void> {\n if (rows.length === 0) return;\n await db.runBatch(\n 'UPDATE links SET dst = ? WHERE src = ? AND target = ?',\n rows.map((r) => [r.dst, r.src, r.target])\n );\n}\n\n// A new or deleted file can change any note's resolution, so re-resolve the whole table.\n// Fallback for cold builds and large deltas -- see afterReconcile's threshold.\nasync function resolveAll(db: Connection, files: FileStat[]): Promise<boolean> {\n const pathSet = new Set(files.map((f) => f.relPath));\n const byBase = buildByBase(files);\n const stmt = await db.prepare('SELECT src, target, dst, embed FROM links');\n const rows = (await stmt.all()) as unknown as LinkRow[];\n const changed = resolveRows(rows, pathSet, byBase);\n await writeResolved(db, changed);\n return changed.length > 0;\n}\n\n// Chunked so a large-but-under-threshold delta never exceeds SQLite's bound-variable limit\n// (SQLITE_MAX_VARIABLE_NUMBER, 32766); harmless headroom on DuckDB, which has no such cap.\nasync function selectIn(db: Connection, column: string, keys: string[]): Promise<LinkRow[]> {\n const rows: LinkRow[] = [];\n for (let i = 0; i < keys.length; i += 500) {\n const chunk = keys.slice(i, i + 500);\n const stmt = await db.prepare(`SELECT src, target, dst, embed FROM links WHERE ${column} IN (${chunk.map(() => '?').join(', ')})`);\n rows.push(...((await stmt.all(...chunk)) as unknown as LinkRow[]));\n }\n return rows;\n}\n\n// Re-resolves only what this reconcile could have touched: re-stored sources, links whose\n// target basename matches an added/vanished file, and links whose dst vanished -- all indexed.\nasync function resolveIncremental(db: Connection, delta: ReconcileDelta): Promise<boolean> {\n const pathSet = new Set(delta.files.map((f) => f.relPath));\n const byBase = buildByBase(delta.files);\n\n const changedBasenames = new Set<string>();\n for (const p of delta.added) changedBasenames.add(baseKey(p));\n for (const p of delta.vanished) changedBasenames.add(baseKey(p));\n\n // \\0 can't appear in a path or target, so the key never collides; a space can.\n const candidates = new Map<string, LinkRow>();\n const collect = (rows: LinkRow[]) => {\n for (const row of rows) candidates.set(`${row.src}\\0${row.target}\\0${row.embed}`, row);\n };\n collect(await selectIn(db, 'src', delta.reparsed));\n collect(await selectIn(db, 'target_base', [...changedBasenames]));\n collect(await selectIn(db, 'dst', delta.vanished));\n\n const changed = resolveRows([...candidates.values()], pathSet, byBase);\n await writeResolved(db, changed);\n return changed.length > 0;\n}\n\n// One edge per (src, target); an embed row is excluded only when the same pair also exists as\n// a link. Self-edges are dropped so PageRank mass is not self-recycled.\nexport const LINK_EDGES_SQL = `SELECT src, dst FROM links WHERE dst IS NOT NULL AND embed = 0 AND src != dst\n UNION ALL\n SELECT src, dst FROM links l WHERE dst IS NOT NULL AND embed = 1 AND src != dst\n AND NOT EXISTS (SELECT 1 FROM links l0 WHERE l0.src = l.src AND l0.target = l.target AND l0.embed = 0)`;\n\nexport function toEdges(rows: Array<{ src: string; dst: string }>): [string, string][] {\n return rows.map((r) => [r.src, r.dst]);\n}\n\n// Resolved edges, for rank (inside reconcile's afterReconcile pass).\nexport async function linkEdges(db: Connection): Promise<[string, string][]> {\n const stmt = await db.prepare(LINK_EDGES_SQL);\n return toEdges((await stmt.all()) as Array<{ src: string; dst: string }>);\n}\n\n// Records whether remove()/store() deleted any link row, since afterReconcile runs after both\n// and dstChanged alone misses the reparsed-to-zero-links case. Keyed on delta so state dies with it.\nconst removedWithLinks = new WeakMap<ReconcileDelta, boolean>();\n\nexport const links: Feature = {\n name: 'links',\n async schema(db) {\n await db.exec('CREATE TABLE IF NOT EXISTS links (src TEXT, target TEXT, target_base TEXT, dst TEXT, embed INTEGER, PRIMARY KEY (src, target, embed))');\n await db.exec('CREATE INDEX IF NOT EXISTS links_dst ON links(dst)');\n await db.exec('CREATE INDEX IF NOT EXISTS links_target_base ON links(target_base)');\n },\n extract,\n // Reparsed docs are diffed in store() instead of wiped here: delete-and-reinsert resets dst\n // to NULL, making every touch-only reparse look like an edge change and recompute PageRank.\n async remove(db, paths, delta) {\n const vanishedSet = new Set(delta.vanished);\n const vanished = paths.filter((p) => vanishedSet.has(p));\n if (vanished.length === 0) return;\n const existingRows = await selectIn(db, 'src', vanished);\n await db.runBatch(\n 'DELETE FROM links WHERE src = ?',\n vanished.map((p) => [p])\n );\n if (existingRows.length > 0) removedWithLinks.set(delta, true);\n },\n async store(db, docs: ExtractedDoc[], delta) {\n const byPath = new Map(docs.map((d) => [d.path, d.extracted as Array<{ target: string; embed: boolean }>]));\n const addedSet = new Set(delta.added);\n const reparsedExisting = docs.map((d) => d.path).filter((p) => !addedSet.has(p));\n\n // Stale rows (target/embed pairs missing from the new parse) are diffed in JS from one\n // bulk SELECT, so this pass costs one query plus one batched DELETE regardless of file count.\n const existingRows = await selectIn(db, 'src', reparsedExisting);\n const staleRows = existingRows.filter((row) => {\n const targets = byPath.get(row.src) ?? [];\n return !targets.some((t) => t.target === row.target && (t.embed ? 1 : 0) === row.embed);\n });\n if (staleRows.length > 0) {\n await db.runBatch(\n 'DELETE FROM links WHERE src = ? AND target = ? AND embed = ?',\n staleRows.map((r) => [r.src, r.target, r.embed])\n );\n removedWithLinks.set(delta, true);\n }\n\n // Upsert preserves dst on surviving rows; only new rows start at NULL. A same-note anchor\n // gets no target_base, so SQL's `IN` excludes it from resolveIncremental's lookup.\n const upsertRows: unknown[][] = [];\n for (const [path, targets] of byPath) {\n for (const { target, embed } of targets) upsertRows.push([path, target, target.startsWith('#') ? null : baseKey(cleanTarget(target)), embed ? 1 : 0]);\n }\n if (upsertRows.length > 0) {\n await db.runBatch('INSERT INTO links (src, target, target_base, dst, embed) VALUES (?, ?, ?, NULL, ?) ON CONFLICT(src, target, embed) DO UPDATE SET target_base = excluded.target_base', upsertRows);\n }\n },\n async afterReconcile(db, delta) {\n // Any deleted rows mean the edge set shrank -- whether the file vanished or was reparsed\n // down to fewer/no links.\n const removeHadLinks = removedWithLinks.get(delta) ?? false;\n\n // A cold build is the only case a full pass wins. Measured 2026-09-01 on 6,566 notes, resolve\n // time alone: full is flat near 90ms at any churn, incremental 7ms at 10 files and 30ms at 2,000.\n const large = delta.files.length === 0 || delta.added.length === delta.files.length;\n\n const dstChanged = large ? await resolveAll(db, delta.files) : await resolveIncremental(db, delta);\n delta.linksChanged = dstChanged || removeHadLinks;\n },\n};\n"],"names":["LINK_EDGES_SQL","linkEdges","links","toEdges","parseWikilinkInner","inner","beforeAlias","split","trim","startsWith","length","target","extract","_raw","rawBody","_search","data","body","test","maskRegions","seen","Set","results","add","embed","key","has","push","matchAll","m","index","dest","includes","Object","values","value","Array","isArray","item","exec","cleanTarget","replace","baseKey","path","posix","basename","toLowerCase","resolveTarget","src","pathSet","byBase","clean","fromSrc","normalize","join","dirname","candidate","candidates","get","buildByBase","files","Map","paths","map","f","relPath","sort","a","b","list","set","resolveRows","rows","changed","row","dst","writeResolved","db","runBatch","r","resolveAll","stmt","prepare","all","selectIn","column","keys","i","chunk","slice","resolveIncremental","delta","changedBasenames","p","collect","added","vanished","reparsed","removedWithLinks","WeakMap","name","schema","remove","vanishedSet","existingRows","filter","store","docs","byPath","addedSet","reparsedExisting","staleRows","upsertRows","targets","d","extracted","some","t","afterReconcile","removeHadLinks","large","dstChanged","linksChanged"],"mappings":"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"}
|
|
@@ -248,9 +248,12 @@ var sections = {
|
|
|
248
248
|
if (rows.length === 0) return [
|
|
249
249
|
2
|
|
250
250
|
];
|
|
251
|
+
// DO NOTHING, not a bare INSERT: reconcile's added/touched split is decided before this
|
|
252
|
+
// write's lock, so a path called "added" here can already have this row from a concurrent
|
|
253
|
+
// reconcile that committed first -- same file, same parse, same row.
|
|
251
254
|
return [
|
|
252
255
|
4,
|
|
253
|
-
db.runBatch('INSERT INTO sections ("path", idx, level, heading, start_line, end_line, tokens) VALUES (?, ?, ?, ?, ?, ?, ?)', rows)
|
|
256
|
+
db.runBatch('INSERT INTO sections ("path", idx, level, heading, start_line, end_line, tokens) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT("path", idx) DO NOTHING', rows)
|
|
254
257
|
];
|
|
255
258
|
case 1:
|
|
256
259
|
_state.sent();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/sections.ts"],"sourcesContent":["import type { Block } from '../chunk/index.ts';\nimport { estimateTokens, parse } from '../chunk/index.ts';\nimport { countLines } from '../scan/frontmatter.ts';\nimport type { Feature } from './types.ts';\n\n// sections(path, idx, level, heading, start_line, end_line, tokens): the heading outline,\n// 1-indexed over the raw file so a row is a direct Read range; tokens is estimateTokens (D5), CJK-aware.\n\nexport interface Section {\n level: number;\n heading: string;\n startLine: number;\n endLine: number;\n tokens: number;\n}\n\n// Heading blocks parse() already found (mdast/CommonMark fences), offset back onto the raw file;\n// a section runs to just before the next heading, or EOF.\nfunction sectionsFromBlocks(blocks: Block[], raw: string, body: string): Section[] {\n const rawLines = raw.split('\\n');\n const offset = rawLines.length - countLines(body);\n const found: Section[] = blocks.filter((b) => b.type === 'heading').map((b) => ({ level: b.depth ?? 1, heading: (b.text ?? '').trim(), startLine: b.startLine + offset, endLine: rawLines.length, tokens: 0 }));\n for (let s = 0; s < found.length; s++) {\n if (s + 1 < found.length) found[s].endLine = found[s + 1].startLine - 1;\n const text = rawLines.slice(found[s].startLine - 1, found[s].endLine).join('\\n');\n found[s].tokens = Math.ceil(estimateTokens(text));\n }\n return found;\n}\n\nexport const sections: Feature = {\n name: 'sections',\n async schema(db) {\n await db.exec(`CREATE TABLE IF NOT EXISTS sections (\"path\" TEXT, idx INTEGER, level INTEGER, heading TEXT, start_line INTEGER, end_line INTEGER, tokens INTEGER, PRIMARY KEY (\"path\", idx))`);\n },\n extract(raw, body, _search, _data, _cfg, blocks) {\n return sectionsFromBlocks(blocks ?? parse(body), raw, body);\n },\n async remove(db, paths) {\n if (paths.length === 0) return;\n await db.runBatch(\n 'DELETE FROM sections WHERE \"path\" = ?',\n paths.map((p) => [p])\n );\n },\n async store(db, docs) {\n const rows: unknown[][] = [];\n for (const { path, extracted } of docs) (extracted as Section[]).forEach((s, idx) => rows.push([path, idx, s.level, s.heading, s.startLine, s.endLine, s.tokens]));\n if (rows.length === 0) return;\n await db.runBatch('INSERT INTO sections (\"path\", idx, level, heading, start_line, end_line, tokens) VALUES (?, ?, ?, ?, ?, ?, ?)', rows);\n },\n};\n"],"names":["sections","sectionsFromBlocks","blocks","raw","body","rawLines","split","offset","length","countLines","found","filter","b","type","map","level","depth","heading","text","trim","startLine","endLine","tokens","s","slice","join","Math","ceil","estimateTokens","name","schema","db","exec","extract","_search","_data","_cfg","parse","remove","paths","runBatch","p","store","docs","rows","path","extracted","forEach","idx","push"],"mappings":";;;;+BA8BaA;;;eAAAA;;;uBA7ByB;6BACX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAc3B,iGAAiG;AACjG,0DAA0D;AAC1D,SAASC,mBAAmBC,MAAe,EAAEC,GAAW,EAAEC,IAAY;IACpE,IAAMC,WAAWF,IAAIG,KAAK,CAAC;IAC3B,IAAMC,SAASF,SAASG,MAAM,GAAGC,IAAAA,yBAAU,EAACL;IAC5C,IAAMM,QAAmBR,OAAOS,MAAM,CAAC,SAACC;eAAMA,EAAEC,IAAI,KAAK;OAAWC,GAAG,CAAC,SAACF;YAAgBA,UAAwBA;eAAjC;YAAEG,KAAK,GAAEH,WAAAA,EAAEI,KAAK,cAAPJ,sBAAAA,WAAW;YAAGK,SAAS,EAACL,UAAAA,EAAEM,IAAI,cAANN,qBAAAA,UAAU,IAAIO,IAAI;YAAIC,WAAWR,EAAEQ,SAAS,GAAGb;YAAQc,SAAShB,SAASG,MAAM;YAAEc,QAAQ;QAAE;;IAC5M,IAAK,IAAIC,IAAI,GAAGA,IAAIb,MAAMF,MAAM,EAAEe,IAAK;QACrC,IAAIA,IAAI,IAAIb,MAAMF,MAAM,EAAEE,KAAK,CAACa,EAAE,CAACF,OAAO,GAAGX,KAAK,CAACa,IAAI,EAAE,CAACH,SAAS,GAAG;QACtE,IAAMF,OAAOb,SAASmB,KAAK,CAACd,KAAK,CAACa,EAAE,CAACH,SAAS,GAAG,GAAGV,KAAK,CAACa,EAAE,CAACF,OAAO,EAAEI,IAAI,CAAC;QAC3Ef,KAAK,CAACa,EAAE,CAACD,MAAM,GAAGI,KAAKC,IAAI,CAACC,IAAAA,uBAAc,EAACV;IAC7C;IACA,OAAOR;AACT;AAEO,IAAMV,WAAoB;IAC/B6B,MAAM;IACAC,QAAN,SAAMA,OAAOC,EAAE;;;;;wBACb;;4BAAMA,GAAGC,IAAI,CAAC;;;wBAAd;;;;;;QACF;;IACAC,SAAAA,SAAAA,QAAQ9B,GAAG,EAAEC,IAAI,EAAE8B,OAAO,EAAEC,KAAK,EAAEC,IAAI,EAAElC,MAAM;QAC7C,OAAOD,mBAAmBC,mBAAAA,oBAAAA,SAAUmC,IAAAA,cAAK,EAACjC,OAAOD,KAAKC;IACxD;IACMkC,QAAN,SAAMA,OAAOP,EAAE,EAAEQ,KAAK;;;;;wBACpB,IAAIA,MAAM/B,MAAM,KAAK,GAAG;;;wBACxB;;4BAAMuB,GAAGS,QAAQ,CACf,yCACAD,MAAMzB,GAAG,CAAC,SAAC2B;uCAAM;oCAACA;iCAAE;;;;wBAFtB;;;;;;QAIF;;IACMC,OAAN,SAAMA,MAAMX,EAAE,EAAEY,IAAI;;gBACZC,MACD,2BAAA,mBAAA,uBAAA,WAAA;;;;wBADCA;wBACD,kCAAA,2BAAA;;;gCAAA,kBAAA,aAAQC,mBAAAA,MAAMC,wBAAAA;gCAAsBA,UAAwBC,OAAO,CAAC,SAACxB,GAAGyB;2CAAQJ,KAAKK,IAAI,CAAC;wCAACJ;wCAAMG;wCAAKzB,EAAER,KAAK;wCAAEQ,EAAEN,OAAO;wCAAEM,EAAEH,SAAS;wCAAEG,EAAEF,OAAO;wCAAEE,EAAED,MAAM;qCAAC;;;4BAAhK,IAAK,YAA6BqB,2BAA7B,6BAAA,QAAA,yBAAA;;4BAAA;4BAAA;;;qCAAA,6BAAA;oCAAA;;;oCAAA;0CAAA;;;;wBACL,IAAIC,KAAKpC,MAAM,KAAK,GAAG;;;wBACvB;;4BAAMuB,GAAGS,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/sections.ts"],"sourcesContent":["import type { Block } from '../chunk/index.ts';\nimport { estimateTokens, parse } from '../chunk/index.ts';\nimport { countLines } from '../scan/frontmatter.ts';\nimport type { Feature } from './types.ts';\n\n// sections(path, idx, level, heading, start_line, end_line, tokens): the heading outline,\n// 1-indexed over the raw file so a row is a direct Read range; tokens is estimateTokens (D5), CJK-aware.\n\nexport interface Section {\n level: number;\n heading: string;\n startLine: number;\n endLine: number;\n tokens: number;\n}\n\n// Heading blocks parse() already found (mdast/CommonMark fences), offset back onto the raw file;\n// a section runs to just before the next heading, or EOF.\nfunction sectionsFromBlocks(blocks: Block[], raw: string, body: string): Section[] {\n const rawLines = raw.split('\\n');\n const offset = rawLines.length - countLines(body);\n const found: Section[] = blocks.filter((b) => b.type === 'heading').map((b) => ({ level: b.depth ?? 1, heading: (b.text ?? '').trim(), startLine: b.startLine + offset, endLine: rawLines.length, tokens: 0 }));\n for (let s = 0; s < found.length; s++) {\n if (s + 1 < found.length) found[s].endLine = found[s + 1].startLine - 1;\n const text = rawLines.slice(found[s].startLine - 1, found[s].endLine).join('\\n');\n found[s].tokens = Math.ceil(estimateTokens(text));\n }\n return found;\n}\n\nexport const sections: Feature = {\n name: 'sections',\n async schema(db) {\n await db.exec(`CREATE TABLE IF NOT EXISTS sections (\"path\" TEXT, idx INTEGER, level INTEGER, heading TEXT, start_line INTEGER, end_line INTEGER, tokens INTEGER, PRIMARY KEY (\"path\", idx))`);\n },\n extract(raw, body, _search, _data, _cfg, blocks) {\n return sectionsFromBlocks(blocks ?? parse(body), raw, body);\n },\n async remove(db, paths) {\n if (paths.length === 0) return;\n await db.runBatch(\n 'DELETE FROM sections WHERE \"path\" = ?',\n paths.map((p) => [p])\n );\n },\n async store(db, docs) {\n const rows: unknown[][] = [];\n for (const { path, extracted } of docs) (extracted as Section[]).forEach((s, idx) => rows.push([path, idx, s.level, s.heading, s.startLine, s.endLine, s.tokens]));\n if (rows.length === 0) return;\n // DO NOTHING, not a bare INSERT: reconcile's added/touched split is decided before this\n // write's lock, so a path called \"added\" here can already have this row from a concurrent\n // reconcile that committed first -- same file, same parse, same row.\n await db.runBatch('INSERT INTO sections (\"path\", idx, level, heading, start_line, end_line, tokens) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(\"path\", idx) DO NOTHING', rows);\n },\n};\n"],"names":["sections","sectionsFromBlocks","blocks","raw","body","rawLines","split","offset","length","countLines","found","filter","b","type","map","level","depth","heading","text","trim","startLine","endLine","tokens","s","slice","join","Math","ceil","estimateTokens","name","schema","db","exec","extract","_search","_data","_cfg","parse","remove","paths","runBatch","p","store","docs","rows","path","extracted","forEach","idx","push"],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Config } from '../config/index.js';
|
|
2
|
+
import type { Feature } from '../features/types.js';
|
|
3
|
+
import type { ParsedDoc } from '../scan/index.js';
|
|
4
|
+
import type { FileStat } from './list.js';
|
|
5
|
+
export type FileResult = {
|
|
6
|
+
doc: ParsedDoc;
|
|
7
|
+
warnings: string[];
|
|
8
|
+
};
|
|
9
|
+
export declare class ParsePool {
|
|
10
|
+
private pool;
|
|
11
|
+
poolsCreated: number;
|
|
12
|
+
private ensure;
|
|
13
|
+
run(files: FileStat[], features: Feature[], cfg: Config, onParsed: ((done: number) => void) | undefined, maxWorkers: number): Promise<FileResult[]>;
|
|
14
|
+
close(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Config } from '../config/index.js';
|
|
2
|
+
import type { Feature } from '../features/types.js';
|
|
3
|
+
import type { ParsedDoc } from '../scan/index.js';
|
|
4
|
+
import type { FileStat } from './list.js';
|
|
5
|
+
export type FileResult = {
|
|
6
|
+
doc: ParsedDoc;
|
|
7
|
+
warnings: string[];
|
|
8
|
+
};
|
|
9
|
+
export declare class ParsePool {
|
|
10
|
+
private pool;
|
|
11
|
+
poolsCreated: number;
|
|
12
|
+
private ensure;
|
|
13
|
+
run(files: FileStat[], features: Feature[], cfg: Config, onParsed: ((done: number) => void) | undefined, maxWorkers: number): Promise<FileResult[]>;
|
|
14
|
+
close(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "ParsePool", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return ParsePool;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
var _nodemodule = require("node:module");
|
|
12
|
+
var _nodepath = require("node:path");
|
|
13
|
+
var _workererrorts = require("./worker-error.js");
|
|
14
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
15
|
+
try {
|
|
16
|
+
var info = gen[key](arg);
|
|
17
|
+
var value = info.value;
|
|
18
|
+
} catch (error) {
|
|
19
|
+
reject(error);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (info.done) resolve(value);
|
|
23
|
+
else Promise.resolve(value).then(_next, _throw);
|
|
24
|
+
}
|
|
25
|
+
function _async_to_generator(fn) {
|
|
26
|
+
return function() {
|
|
27
|
+
var self = this, args = arguments;
|
|
28
|
+
return new Promise(function(resolve, reject) {
|
|
29
|
+
var gen = fn.apply(self, args);
|
|
30
|
+
function _next(value) {
|
|
31
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
|
|
32
|
+
}
|
|
33
|
+
function _throw(err) {
|
|
34
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
|
|
35
|
+
}
|
|
36
|
+
_next(undefined);
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function _class_call_check(instance, Constructor) {
|
|
41
|
+
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
|
|
42
|
+
}
|
|
43
|
+
function _ts_generator(thisArg, body) {
|
|
44
|
+
var f, y, t, _ = {
|
|
45
|
+
label: 0,
|
|
46
|
+
sent: function() {
|
|
47
|
+
if (t[0] & 1) throw t[1];
|
|
48
|
+
return t[1];
|
|
49
|
+
},
|
|
50
|
+
trys: [],
|
|
51
|
+
ops: []
|
|
52
|
+
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
|
|
53
|
+
return d(g, "next", {
|
|
54
|
+
value: verb(0)
|
|
55
|
+
}), d(g, "throw", {
|
|
56
|
+
value: verb(1)
|
|
57
|
+
}), d(g, "return", {
|
|
58
|
+
value: verb(2)
|
|
59
|
+
}), typeof Symbol === "function" && d(g, Symbol.iterator, {
|
|
60
|
+
value: function() {
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
}), g;
|
|
64
|
+
function verb(n) {
|
|
65
|
+
return function(v) {
|
|
66
|
+
return step([
|
|
67
|
+
n,
|
|
68
|
+
v
|
|
69
|
+
]);
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function step(op) {
|
|
73
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
74
|
+
while(g && (g = 0, op[0] && (_ = 0)), _)try {
|
|
75
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
76
|
+
if (y = 0, t) op = [
|
|
77
|
+
op[0] & 2,
|
|
78
|
+
t.value
|
|
79
|
+
];
|
|
80
|
+
switch(op[0]){
|
|
81
|
+
case 0:
|
|
82
|
+
case 1:
|
|
83
|
+
t = op;
|
|
84
|
+
break;
|
|
85
|
+
case 4:
|
|
86
|
+
_.label++;
|
|
87
|
+
return {
|
|
88
|
+
value: op[1],
|
|
89
|
+
done: false
|
|
90
|
+
};
|
|
91
|
+
case 5:
|
|
92
|
+
_.label++;
|
|
93
|
+
y = op[1];
|
|
94
|
+
op = [
|
|
95
|
+
0
|
|
96
|
+
];
|
|
97
|
+
continue;
|
|
98
|
+
case 7:
|
|
99
|
+
op = _.ops.pop();
|
|
100
|
+
_.trys.pop();
|
|
101
|
+
continue;
|
|
102
|
+
default:
|
|
103
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
104
|
+
_ = 0;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
108
|
+
_.label = op[1];
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
if (op[0] === 6 && _.label < t[1]) {
|
|
112
|
+
_.label = t[1];
|
|
113
|
+
t = op;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
if (t && _.label < t[2]) {
|
|
117
|
+
_.label = t[2];
|
|
118
|
+
_.ops.push(op);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
if (t[2]) _.ops.pop();
|
|
122
|
+
_.trys.pop();
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
op = body.call(thisArg, _);
|
|
126
|
+
} catch (e) {
|
|
127
|
+
op = [
|
|
128
|
+
6,
|
|
129
|
+
e
|
|
130
|
+
];
|
|
131
|
+
y = 0;
|
|
132
|
+
} finally{
|
|
133
|
+
f = t = 0;
|
|
134
|
+
}
|
|
135
|
+
if (op[0] & 5) throw op[1];
|
|
136
|
+
return {
|
|
137
|
+
value: op[0] ? op[1] : void 0,
|
|
138
|
+
done: true
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Tinypool is ESM-only; our floor (>=22.20) has native require(esm), so the tier-2 house deferral reaches it.
|
|
143
|
+
var _require = typeof require === 'undefined' ? (0, _nodemodule.createRequire)(require("url").pathToFileURL(__filename).toString()) : require;
|
|
144
|
+
// Worker MUST load from dist/cjs/: a worker_threads thread is a fresh realm with no inherited
|
|
145
|
+
// TS loader hook, so a source path cannot work. Resolved on first pooled dispatch, not at import.
|
|
146
|
+
var workerFile;
|
|
147
|
+
function resolveWorkerFile() {
|
|
148
|
+
if (workerFile) return workerFile;
|
|
149
|
+
var load = (0, _nodemodule.createRequire)(require("url").pathToFileURL(__filename).toString());
|
|
150
|
+
for(var _i = 0, _iter = [
|
|
151
|
+
'..',
|
|
152
|
+
'../..',
|
|
153
|
+
'../../..'
|
|
154
|
+
]; _i < _iter.length; _i++){
|
|
155
|
+
var rel = _iter[_i];
|
|
156
|
+
try {
|
|
157
|
+
if (load("".concat(rel, "/package.json")).name === 'sensemaking') {
|
|
158
|
+
workerFile = (0, _nodepath.join)((0, _nodepath.dirname)(load.resolve("".concat(rel, "/package.json"))), 'dist', 'cjs', 'workers', 'parse.js');
|
|
159
|
+
return workerFile;
|
|
160
|
+
}
|
|
161
|
+
} catch (unused) {}
|
|
162
|
+
}
|
|
163
|
+
throw new Error('cannot locate the sensemaking package root, so the parse worker cannot be found; run npm run build');
|
|
164
|
+
}
|
|
165
|
+
var ParsePool = /*#__PURE__*/ function() {
|
|
166
|
+
"use strict";
|
|
167
|
+
function ParsePool() {
|
|
168
|
+
_class_call_check(this, ParsePool);
|
|
169
|
+
// Tinypools this instance has constructed. One dispatch or a hundred should leave it at 1;
|
|
170
|
+
// it is how a caller, and the specs, observe that a lifetime reuses its pool rather than churning one.
|
|
171
|
+
this.poolsCreated = 0;
|
|
172
|
+
}
|
|
173
|
+
var _proto = ParsePool.prototype;
|
|
174
|
+
_proto.ensure = function ensure(workerData, maxWorkers) {
|
|
175
|
+
if (!this.pool) {
|
|
176
|
+
var Tinypool = _require('tinypool').Tinypool;
|
|
177
|
+
this.pool = new Tinypool({
|
|
178
|
+
filename: resolveWorkerFile(),
|
|
179
|
+
minThreads: maxWorkers,
|
|
180
|
+
maxThreads: maxWorkers,
|
|
181
|
+
workerData: workerData
|
|
182
|
+
});
|
|
183
|
+
this.poolsCreated++;
|
|
184
|
+
}
|
|
185
|
+
return this.pool;
|
|
186
|
+
};
|
|
187
|
+
// Never the tree: a worker task carries one FileStat and returns only what parseFile returns --
|
|
188
|
+
// extracted text and feature values, never the mdast tree.
|
|
189
|
+
_proto.run = function run(files, features, cfg, onParsed, maxWorkers) {
|
|
190
|
+
return _async_to_generator(function() {
|
|
191
|
+
var workerData, pool, done;
|
|
192
|
+
return _ts_generator(this, function(_state) {
|
|
193
|
+
// cfg and the feature selection are constant for the dispatch, so they cross once per worker
|
|
194
|
+
// as workerData. Features carry closures and cannot cross at all; only their names do.
|
|
195
|
+
workerData = {
|
|
196
|
+
cfg: cfg,
|
|
197
|
+
featureNames: features.map(function(feature) {
|
|
198
|
+
return feature.name;
|
|
199
|
+
})
|
|
200
|
+
};
|
|
201
|
+
pool = this.ensure(workerData, maxWorkers);
|
|
202
|
+
done = 0;
|
|
203
|
+
// Promise.all over a mapped array is load-bearing: the resolved array keeps `files` order
|
|
204
|
+
// regardless of task completion order, which first-seen column order depends on downstream.
|
|
205
|
+
return [
|
|
206
|
+
2,
|
|
207
|
+
Promise.all(files.map(function(file) {
|
|
208
|
+
return _async_to_generator(function() {
|
|
209
|
+
var result;
|
|
210
|
+
return _ts_generator(this, function(_state) {
|
|
211
|
+
switch(_state.label){
|
|
212
|
+
case 0:
|
|
213
|
+
return [
|
|
214
|
+
4,
|
|
215
|
+
pool.run(file)
|
|
216
|
+
];
|
|
217
|
+
case 1:
|
|
218
|
+
result = _state.sent();
|
|
219
|
+
if (!result.ok) throw (0, _workererrorts.reviveError)(result.error);
|
|
220
|
+
onParsed === null || onParsed === void 0 ? void 0 : onParsed(++done);
|
|
221
|
+
return [
|
|
222
|
+
2,
|
|
223
|
+
{
|
|
224
|
+
doc: result.doc,
|
|
225
|
+
warnings: result.warnings
|
|
226
|
+
}
|
|
227
|
+
];
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
})();
|
|
231
|
+
}))
|
|
232
|
+
];
|
|
233
|
+
});
|
|
234
|
+
}).call(this);
|
|
235
|
+
};
|
|
236
|
+
// A pool never created costs nothing to destroy.
|
|
237
|
+
_proto.close = function close() {
|
|
238
|
+
return _async_to_generator(function() {
|
|
239
|
+
var pool;
|
|
240
|
+
return _ts_generator(this, function(_state) {
|
|
241
|
+
switch(_state.label){
|
|
242
|
+
case 0:
|
|
243
|
+
if (!this.pool) return [
|
|
244
|
+
2
|
|
245
|
+
];
|
|
246
|
+
pool = this.pool;
|
|
247
|
+
this.pool = undefined;
|
|
248
|
+
return [
|
|
249
|
+
4,
|
|
250
|
+
pool.destroy()
|
|
251
|
+
];
|
|
252
|
+
case 1:
|
|
253
|
+
_state.sent();
|
|
254
|
+
return [
|
|
255
|
+
2
|
|
256
|
+
];
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}).call(this);
|
|
260
|
+
};
|
|
261
|
+
return ParsePool;
|
|
262
|
+
}();
|
|
263
|
+
/* 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/scan/pool.ts"],"sourcesContent":["import { createRequire } from 'node:module';\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 { ParsedDoc } from '../scan/index.ts';\nimport type { ParseTask, ParseTaskResult, ParseWorkerData } from '../workers/parse.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 deferral reaches it.\nconst _require = typeof require === 'undefined' ? createRequire(import.meta.url) : require;\n\n// Worker MUST load from dist/cjs/: a worker_threads thread is a fresh realm with no inherited\n// TS loader hook, so a source path cannot work. Resolved on first pooled dispatch, not at import.\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\nexport type FileResult = { doc: ParsedDoc; warnings: string[] };\n\n// One Tinypool per instance, created lazily on first dispatch and reused by every later call:\n// a builder owns one of these for its whole lifetime instead of paying pool startup per reconcile.\nexport class ParsePool {\n private pool: TinypoolNS.Tinypool | undefined;\n // Tinypools this instance has constructed. One dispatch or a hundred should leave it at 1;\n // it is how a caller, and the specs, observe that a lifetime reuses its pool rather than churning one.\n poolsCreated = 0;\n\n private ensure(workerData: ParseWorkerData, maxWorkers: number): TinypoolNS.Tinypool {\n if (!this.pool) {\n const { Tinypool } = _require('tinypool') as typeof TinypoolNS;\n this.pool = new Tinypool({ filename: resolveWorkerFile(), minThreads: maxWorkers, maxThreads: maxWorkers, workerData });\n this.poolsCreated++;\n }\n return this.pool;\n }\n\n // Never the tree: a worker task carries one FileStat and returns only what parseFile returns --\n // extracted text and feature values, never the mdast tree.\n async run(files: FileStat[], features: Feature[], cfg: Config, onParsed: ((done: number) => void) | undefined, maxWorkers: number): Promise<FileResult[]> {\n // cfg and the feature selection are constant for the dispatch, so they cross once per worker\n // as workerData. Features carry closures and cannot cross at all; only their names do.\n const workerData: ParseWorkerData = { cfg, featureNames: features.map((feature) => feature.name) };\n const pool = this.ensure(workerData, maxWorkers);\n let done = 0;\n // Promise.all over a mapped array is load-bearing: the resolved array keeps `files` order\n // regardless of task completion order, which first-seen column order depends on downstream.\n return 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 }\n\n // A pool never created costs nothing to destroy.\n async close(): Promise<void> {\n if (!this.pool) return;\n const pool = this.pool;\n this.pool = undefined;\n await pool.destroy();\n }\n}\n"],"names":["ParsePool","_require","require","createRequire","workerFile","resolveWorkerFile","load","rel","name","join","dirname","resolve","Error","poolsCreated","ensure","workerData","maxWorkers","pool","Tinypool","filename","minThreads","maxThreads","run","files","features","cfg","onParsed","done","featureNames","map","feature","Promise","all","file","result","ok","reviveError","error","doc","warnings","close","undefined","destroy"],"mappings":";;;;+BAoCaA;;;eAAAA;;;0BApCiB;wBACA;6BASF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE5B,8GAA8G;AAC9G,IAAMC,WAAW,OAAOC,YAAY,cAAcC,IAAAA,yBAAa,EAAC,uDAAmBD;AAEnF,8FAA8F;AAC9F,kGAAkG;AAClG,IAAIE;AACJ,SAASC;IACP,IAAID,YAAY,OAAOA;IACvB,IAAME,OAAOH,IAAAA,yBAAa,EAAC;IAC3B,gBAAkB,QAAA;QAAC;QAAM;QAAS;KAAW,OAA3B,mBAA6B;YAApCI,MAAO;QAChB,IAAI;YACF,IAAI,AAACD,KAAK,AAAC,GAAM,OAAJC,KAAI,kBAAsCC,IAAI,KAAK,eAAe;gBAC7EJ,aAAaK,IAAAA,cAAI,EAACC,IAAAA,iBAAO,EAACJ,KAAKK,OAAO,CAAC,AAAC,GAAM,OAAJJ,KAAI,oBAAkB,QAAQ,OAAO,WAAW;gBAC1F,OAAOH;YACT;QACF,EAAE,eAAM,CAAC;IACX;IACA,MAAM,IAAIQ,MAAM;AAClB;AAMO,IAAA,AAAMZ,0BAAN;;aAAMA;gCAAAA;QAEX,2FAA2F;QAC3F,uGAAuG;aACvGa,eAAe;;iBAJJb;IAMX,OAAQc,MAOP,GAPD,SAAQA,OAAOC,UAA2B,EAAEC,UAAkB;QAC5D,IAAI,CAAC,IAAI,CAACC,IAAI,EAAE;YACd,IAAM,AAAEC,WAAajB,SAAS,YAAtBiB;YACR,IAAI,CAACD,IAAI,GAAG,IAAIC,SAAS;gBAAEC,UAAUd;gBAAqBe,YAAYJ;gBAAYK,YAAYL;gBAAYD,YAAAA;YAAW;YACrH,IAAI,CAACF,YAAY;QACnB;QACA,OAAO,IAAI,CAACI,IAAI;IAClB;IAEA,gGAAgG;IAChG,2DAA2D;IAC3D,OAAMK,GAgBL,GAhBD,SAAMA,IAAIC,KAAiB,EAAEC,QAAmB,EAAEC,GAAW,EAAEC,QAA8C,EAAEV,UAAkB;;gBAGzHD,YACAE,MACFU;;gBAJJ,6FAA6F;gBAC7F,uFAAuF;gBACjFZ,aAA8B;oBAAEU,KAAAA;oBAAKG,cAAcJ,SAASK,GAAG,CAAC,SAACC;+BAAYA,QAAQtB,IAAI;;gBAAE;gBAC3FS,OAAO,IAAI,CAACH,MAAM,CAACC,YAAYC;gBACjCW,OAAO;gBACX,0FAA0F;gBAC1F,4FAA4F;gBAC5F;;oBAAOI,QAAQC,GAAG,CAChBT,MAAMM,GAAG,CAAC,SAAOI;;gCACTC;;;;wCAAU;;4CAAMjB,KAAKK,GAAG,CAACW;;;wCAAzBC,SAAU;wCAChB,IAAI,CAACA,OAAOC,EAAE,EAAE,MAAMC,IAAAA,0BAAW,EAACF,OAAOG,KAAK;wCAC9CX,qBAAAA,+BAAAA,SAAW,EAAEC;wCACb;;4CAAO;gDAAEW,KAAKJ,OAAOI,GAAG;gDAAEC,UAAUL,OAAOK,QAAQ;4CAAC;;;;wBACtD;;;;QAEJ;;IAEA,iDAAiD;IACjD,OAAMC,KAKL,GALD,SAAMA;;gBAEEvB;;;;wBADN,IAAI,CAAC,IAAI,CAACA,IAAI,EAAE;;;wBACVA,OAAO,IAAI,CAACA,IAAI;wBACtB,IAAI,CAACA,IAAI,GAAGwB;wBACZ;;4BAAMxB,KAAKyB,OAAO;;;wBAAlB;;;;;;QACF;;WAzCW1C"}
|
|
@@ -2,6 +2,7 @@ import type { Config } from '../config/index.js';
|
|
|
2
2
|
import type { Feature } from '../features/types.js';
|
|
3
3
|
import type { ParsedDoc } from './index.js';
|
|
4
4
|
import type { FileStat } from './list.js';
|
|
5
|
+
import { ParsePool } from './pool.js';
|
|
5
6
|
export interface ReparseResult {
|
|
6
7
|
docs: ParsedDoc[];
|
|
7
8
|
warnings: string[];
|
|
@@ -10,6 +11,7 @@ export interface ReparseResult {
|
|
|
10
11
|
export interface ReparseOptions {
|
|
11
12
|
threshold?: number;
|
|
12
13
|
maxWorkers?: number;
|
|
14
|
+
pool?: ParsePool;
|
|
13
15
|
}
|
|
14
16
|
export declare function featuresForFile(features: Feature[], cfg: Config, file: FileStat): Feature[];
|
|
15
17
|
export declare function reparseFiles(files: FileStat[], features: Feature[], cfg: Config, knownColumns: ReadonlySet<string>, onParsed?: (done: number) => void, options?: ReparseOptions): Promise<ReparseResult>;
|
|
@@ -2,6 +2,7 @@ import type { Config } from '../config/index.js';
|
|
|
2
2
|
import type { Feature } from '../features/types.js';
|
|
3
3
|
import type { ParsedDoc } from './index.js';
|
|
4
4
|
import type { FileStat } from './list.js';
|
|
5
|
+
import { ParsePool } from './pool.js';
|
|
5
6
|
export interface ReparseResult {
|
|
6
7
|
docs: ParsedDoc[];
|
|
7
8
|
warnings: string[];
|
|
@@ -10,6 +11,7 @@ export interface ReparseResult {
|
|
|
10
11
|
export interface ReparseOptions {
|
|
11
12
|
threshold?: number;
|
|
12
13
|
maxWorkers?: number;
|
|
14
|
+
pool?: ParsePool;
|
|
13
15
|
}
|
|
14
16
|
export declare function featuresForFile(features: Feature[], cfg: Config, file: FileStat): Feature[];
|
|
15
17
|
export declare function reparseFiles(files: FileStat[], features: Feature[], cfg: Config, knownColumns: ReadonlySet<string>, onParsed?: (done: number) => void, options?: ReparseOptions): Promise<ReparseResult>;
|