sensemaking 0.15.3 → 0.16.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 +33 -5
- package/dist/cjs/cli/download.js +15 -19
- package/dist/cjs/cli/download.js.map +1 -1
- package/dist/cjs/cli/index.d.cts +1 -1
- package/dist/cjs/cli/index.d.ts +1 -1
- package/dist/cjs/cli/index.js +9 -20
- package/dist/cjs/cli/index.js.map +1 -1
- package/dist/cjs/cli/init.js +31 -5
- package/dist/cjs/cli/init.js.map +1 -1
- package/dist/cjs/cli/map.js +1 -3
- package/dist/cjs/cli/map.js.map +1 -1
- package/dist/cjs/cli/named.js +3 -8
- package/dist/cjs/cli/named.js.map +1 -1
- package/dist/cjs/cli/path.js +4 -7
- package/dist/cjs/cli/path.js.map +1 -1
- package/dist/cjs/cli/peek.js +2 -4
- package/dist/cjs/cli/peek.js.map +1 -1
- package/dist/cjs/cli/related.js +14 -20
- package/dist/cjs/cli/related.js.map +1 -1
- package/dist/cjs/cli/search.js +12 -17
- package/dist/cjs/cli/search.js.map +1 -1
- package/dist/cjs/cli/shared.js +17 -21
- package/dist/cjs/cli/shared.js.map +1 -1
- package/dist/cjs/cli/sql.js +7 -8
- package/dist/cjs/cli/sql.js.map +1 -1
- package/dist/cjs/cli/status.js +344 -102
- package/dist/cjs/cli/status.js.map +1 -1
- package/dist/cjs/cli/watch.js +3 -8
- package/dist/cjs/cli/watch.js.map +1 -1
- package/dist/cjs/cli.js +22 -34
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/column-hint.js +4 -2
- package/dist/cjs/column-hint.js.map +1 -1
- package/dist/cjs/commands/peek.js +5 -3
- package/dist/cjs/commands/peek.js.map +1 -1
- package/dist/cjs/commands/related.js +31 -28
- package/dist/cjs/commands/related.js.map +1 -1
- package/dist/cjs/commands/scope.js +4 -2
- package/dist/cjs/commands/scope.js.map +1 -1
- package/dist/cjs/commands/search.js +36 -108
- package/dist/cjs/commands/search.js.map +1 -1
- package/dist/cjs/commands/signals.d.cts +18 -0
- package/dist/cjs/commands/signals.d.ts +18 -0
- package/dist/cjs/commands/signals.js +299 -0
- package/dist/cjs/commands/signals.js.map +1 -0
- package/dist/cjs/commands/status.d.cts +2 -2
- package/dist/cjs/commands/status.d.ts +2 -2
- package/dist/cjs/commands/status.js +1 -1
- package/dist/cjs/commands/status.js.map +1 -1
- package/dist/cjs/config/access.d.cts +5 -2
- package/dist/cjs/config/access.d.ts +5 -2
- package/dist/cjs/config/access.js +34 -14
- package/dist/cjs/config/access.js.map +1 -1
- package/dist/cjs/config/index.d.cts +4 -1
- package/dist/cjs/config/index.d.ts +4 -1
- package/dist/cjs/config/index.js +12 -2
- package/dist/cjs/config/index.js.map +1 -1
- package/dist/cjs/config/load.d.cts +6 -1
- package/dist/cjs/config/load.d.ts +6 -1
- package/dist/cjs/config/load.js +87 -15
- package/dist/cjs/config/load.js.map +1 -1
- package/dist/cjs/config/resolve.js +1 -1
- package/dist/cjs/config/resolve.js.map +1 -1
- package/dist/cjs/config/signals.d.cts +4 -0
- package/dist/cjs/config/signals.d.ts +4 -0
- package/dist/cjs/config/signals.js +30 -0
- package/dist/cjs/config/signals.js.map +1 -0
- package/dist/cjs/config/types.d.cts +6 -4
- package/dist/cjs/config/types.d.ts +6 -4
- package/dist/cjs/config/types.js +1 -1
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/config/validate.js +121 -12
- package/dist/cjs/config/validate.js.map +1 -1
- package/dist/cjs/db/open.d.cts +1 -1
- package/dist/cjs/db/open.d.ts +1 -1
- package/dist/cjs/db/open.js +10 -3
- package/dist/cjs/db/open.js.map +1 -1
- package/dist/cjs/db/reconcile.d.cts +1 -0
- package/dist/cjs/db/reconcile.d.ts +1 -0
- package/dist/cjs/db/reconcile.js +20 -3
- package/dist/cjs/db/reconcile.js.map +1 -1
- package/dist/cjs/embed/cohere.d.cts +2 -0
- package/dist/cjs/embed/cohere.d.ts +2 -0
- package/dist/cjs/embed/cohere.js +238 -0
- package/dist/cjs/embed/cohere.js.map +1 -0
- package/dist/cjs/embed/http.d.cts +2 -0
- package/dist/cjs/embed/http.d.ts +2 -0
- package/dist/cjs/embed/http.js +258 -0
- package/dist/cjs/embed/http.js.map +1 -0
- package/dist/cjs/embed/identity.d.cts +13 -0
- package/dist/cjs/embed/identity.d.ts +13 -0
- package/dist/cjs/embed/identity.js +194 -0
- package/dist/cjs/embed/identity.js.map +1 -0
- package/dist/cjs/embed/langfit.d.cts +6 -0
- package/dist/cjs/embed/langfit.d.ts +6 -0
- package/dist/cjs/embed/langfit.js +326 -0
- package/dist/cjs/embed/langfit.js.map +1 -0
- package/dist/cjs/embed/languages.d.cts +3 -0
- package/dist/cjs/embed/languages.d.ts +3 -0
- package/dist/cjs/embed/languages.js +98 -0
- package/dist/cjs/embed/languages.js.map +1 -0
- package/dist/cjs/embed/openai.d.cts +2 -0
- package/dist/cjs/embed/openai.d.ts +2 -0
- package/dist/cjs/embed/openai.js +268 -0
- package/dist/cjs/embed/openai.js.map +1 -0
- package/dist/cjs/embed/query.d.cts +21 -0
- package/dist/cjs/embed/query.d.ts +21 -0
- package/dist/cjs/embed/query.js +610 -0
- package/dist/cjs/embed/query.js.map +1 -0
- package/dist/cjs/embed/registry.d.cts +3 -0
- package/dist/cjs/embed/registry.d.ts +3 -0
- package/dist/cjs/embed/registry.js +114 -0
- package/dist/cjs/embed/registry.js.map +1 -0
- package/dist/cjs/embed/static.d.cts +2 -0
- package/dist/cjs/embed/static.d.ts +2 -0
- package/dist/cjs/embed/static.js +367 -0
- package/dist/cjs/embed/static.js.map +1 -0
- package/dist/cjs/embed/store.d.cts +8 -0
- package/dist/cjs/embed/store.d.ts +8 -0
- package/dist/cjs/embed/store.js +498 -0
- package/dist/cjs/embed/store.js.map +1 -0
- package/dist/cjs/embed/types.d.cts +8 -0
- package/dist/cjs/embed/types.d.ts +8 -0
- package/dist/cjs/embed/types.js +7 -0
- package/dist/cjs/embed/types.js.map +1 -0
- package/dist/cjs/errors.d.cts +1 -1
- package/dist/cjs/errors.d.ts +1 -1
- package/dist/cjs/errors.js +15 -24
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/features/embed.d.cts +4 -27
- package/dist/cjs/features/embed.d.ts +4 -27
- package/dist/cjs/features/embed.js +6 -978
- package/dist/cjs/features/embed.js.map +1 -1
- package/dist/cjs/features/links.js +4 -2
- package/dist/cjs/features/links.js.map +1 -1
- package/dist/cjs/features/rank.js +1 -1
- package/dist/cjs/features/tags.js +4 -2
- package/dist/cjs/features/tags.js.map +1 -1
- package/dist/cjs/fences.js.map +1 -1
- package/dist/cjs/graph.js +5 -3
- package/dist/cjs/graph.js.map +1 -1
- package/dist/cjs/index.d.cts +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/output.d.cts +2 -2
- package/dist/cjs/output.d.ts +2 -2
- package/dist/cjs/output.js +15 -8
- package/dist/cjs/output.js.map +1 -1
- package/dist/cjs/scan.js +13 -8
- package/dist/cjs/scan.js.map +1 -1
- package/dist/cjs/segment.js +67 -14
- package/dist/cjs/segment.js.map +1 -1
- package/dist/cjs/traverse.js +4 -2
- package/dist/cjs/traverse.js.map +1 -1
- package/dist/cjs/watch.js +2 -5
- package/dist/cjs/watch.js.map +1 -1
- package/dist/esm/cli/download.js +9 -8
- package/dist/esm/cli/download.js.map +1 -1
- package/dist/esm/cli/index.d.ts +1 -1
- package/dist/esm/cli/index.js +1 -1
- package/dist/esm/cli/index.js.map +1 -1
- package/dist/esm/cli/init.js +32 -6
- package/dist/esm/cli/init.js.map +1 -1
- package/dist/esm/cli/status.js +36 -8
- package/dist/esm/cli/status.js.map +1 -1
- package/dist/esm/commands/related.js +14 -10
- package/dist/esm/commands/related.js.map +1 -1
- package/dist/esm/commands/search.js +27 -78
- package/dist/esm/commands/search.js.map +1 -1
- package/dist/esm/commands/signals.d.ts +18 -0
- package/dist/esm/commands/signals.js +78 -0
- package/dist/esm/commands/signals.js.map +1 -0
- package/dist/esm/commands/status.d.ts +2 -2
- package/dist/esm/commands/status.js +2 -2
- package/dist/esm/commands/status.js.map +1 -1
- package/dist/esm/config/access.d.ts +5 -2
- package/dist/esm/config/access.js +31 -12
- package/dist/esm/config/access.js.map +1 -1
- package/dist/esm/config/index.d.ts +4 -1
- package/dist/esm/config/index.js +2 -1
- package/dist/esm/config/index.js.map +1 -1
- package/dist/esm/config/load.d.ts +6 -1
- package/dist/esm/config/load.js +67 -9
- package/dist/esm/config/load.js.map +1 -1
- package/dist/esm/config/resolve.js +2 -2
- package/dist/esm/config/resolve.js.map +1 -1
- package/dist/esm/config/signals.d.ts +4 -0
- package/dist/esm/config/signals.js +14 -0
- package/dist/esm/config/signals.js.map +1 -0
- package/dist/esm/config/types.d.ts +6 -4
- package/dist/esm/config/types.js +1 -1
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/config/validate.js +58 -11
- package/dist/esm/config/validate.js.map +1 -1
- package/dist/esm/db/open.d.ts +1 -1
- package/dist/esm/db/open.js +7 -2
- package/dist/esm/db/open.js.map +1 -1
- package/dist/esm/db/reconcile.d.ts +1 -0
- package/dist/esm/db/reconcile.js +10 -0
- package/dist/esm/db/reconcile.js.map +1 -1
- package/dist/esm/embed/cohere.d.ts +2 -0
- package/dist/esm/embed/cohere.js +46 -0
- package/dist/esm/embed/cohere.js.map +1 -0
- package/dist/esm/embed/http.d.ts +2 -0
- package/dist/esm/embed/http.js +33 -0
- package/dist/esm/embed/http.js.map +1 -0
- package/dist/esm/embed/identity.d.ts +13 -0
- package/dist/esm/embed/identity.js +92 -0
- package/dist/esm/embed/identity.js.map +1 -0
- package/dist/esm/embed/langfit.d.ts +6 -0
- package/dist/esm/embed/langfit.js +44 -0
- package/dist/esm/embed/langfit.js.map +1 -0
- package/dist/esm/embed/languages.d.ts +3 -0
- package/dist/esm/embed/languages.js +80 -0
- package/dist/esm/embed/languages.js.map +1 -0
- package/dist/esm/embed/openai.d.ts +2 -0
- package/dist/esm/embed/openai.js +49 -0
- package/dist/esm/embed/openai.js.map +1 -0
- package/dist/esm/embed/query.d.ts +21 -0
- package/dist/esm/embed/query.js +181 -0
- package/dist/esm/embed/query.js.map +1 -0
- package/dist/esm/embed/registry.d.ts +3 -0
- package/dist/esm/embed/registry.js +45 -0
- package/dist/esm/embed/registry.js.map +1 -0
- package/dist/esm/embed/static.d.ts +2 -0
- package/dist/esm/embed/static.js +72 -0
- package/dist/esm/embed/static.js.map +1 -0
- package/dist/esm/embed/store.d.ts +8 -0
- package/dist/esm/embed/store.js +123 -0
- package/dist/esm/embed/store.js.map +1 -0
- package/dist/esm/embed/types.d.ts +8 -0
- package/dist/esm/embed/types.js +3 -0
- package/dist/esm/embed/types.js.map +1 -0
- package/dist/esm/errors.d.ts +1 -1
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/features/embed.d.ts +4 -27
- package/dist/esm/features/embed.js +0 -346
- package/dist/esm/features/embed.js.map +1 -1
- package/dist/esm/fences.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/output.d.ts +2 -2
- package/dist/esm/output.js +6 -2
- package/dist/esm/output.js.map +1 -1
- package/dist/esm/scan.js +7 -4
- package/dist/esm/scan.js.map +1 -1
- package/dist/esm/segment.js +49 -19
- package/dist/esm/segment.js.map +1 -1
- package/package.json +9 -1
- package/schema.json +14 -7
- package/skills/sense/SKILL.md +21 -5
- package/skills/sense-setup/EXAMPLES.md +6 -6
- package/skills/sense-setup/SKILL.md +6 -4
|
@@ -20,6 +20,7 @@ _export(exports, {
|
|
|
20
20
|
}
|
|
21
21
|
});
|
|
22
22
|
var _errorsts = require("../errors.js");
|
|
23
|
+
var _signalsts = require("./signals.js");
|
|
23
24
|
function _array_like_to_array(arr, len) {
|
|
24
25
|
if (len == null || len > arr.length) len = arr.length;
|
|
25
26
|
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
@@ -53,7 +54,7 @@ function _iterable_to_array_limit(arr, i) {
|
|
|
53
54
|
return _arr;
|
|
54
55
|
}
|
|
55
56
|
function _non_iterable_rest() {
|
|
56
|
-
throw new TypeError("Invalid attempt to destructure non-iterable instance
|
|
57
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
57
58
|
}
|
|
58
59
|
function _sliced_to_array(arr, i) {
|
|
59
60
|
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
|
|
@@ -85,7 +86,7 @@ var KNOWN_PRESET_KEYS = new Set([
|
|
|
85
86
|
'include',
|
|
86
87
|
'exclude',
|
|
87
88
|
'k',
|
|
88
|
-
'
|
|
89
|
+
'signals',
|
|
89
90
|
'where'
|
|
90
91
|
]);
|
|
91
92
|
var KNOWN_FEATURE_KEYS = new Set([
|
|
@@ -96,9 +97,10 @@ var KNOWN_FEATURE_KEYS = new Set([
|
|
|
96
97
|
]);
|
|
97
98
|
var KNOWN_EMBED_KEYS = new Set([
|
|
98
99
|
'model',
|
|
99
|
-
'
|
|
100
|
+
'provider',
|
|
100
101
|
'url',
|
|
101
|
-
'key'
|
|
102
|
+
'key',
|
|
103
|
+
'languages'
|
|
102
104
|
]);
|
|
103
105
|
var KNOWN_CONTENT_KEYS = new Set([
|
|
104
106
|
'tokenize'
|
|
@@ -181,7 +183,7 @@ function validateFeaturesBlock(value, configPath) {
|
|
|
181
183
|
}
|
|
182
184
|
function validateEmbedBlock(value, configPath) {
|
|
183
185
|
if ((typeof value === "undefined" ? "undefined" : _type_of(value)) !== 'object' || value === null || Array.isArray(value)) {
|
|
184
|
-
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ': embed must be an object of { model?,
|
|
186
|
+
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ': embed must be an object of { model?, provider?: "static"|"openai"|"cohere", url?, key? }'));
|
|
185
187
|
}
|
|
186
188
|
var embed = value;
|
|
187
189
|
var unknown = Object.keys(embed).filter(function(k) {
|
|
@@ -198,8 +200,8 @@ function validateEmbedBlock(value, configPath) {
|
|
|
198
200
|
if (embed.model === undefined || embed.model === '') {
|
|
199
201
|
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ': embed.model is required when the "embed" block is present (it is what turns vectors on); remove the block to index without vectors'));
|
|
200
202
|
}
|
|
201
|
-
if (embed.
|
|
202
|
-
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ': embed.
|
|
203
|
+
if (embed.provider !== undefined && embed.provider !== 'static' && embed.provider !== 'openai' && embed.provider !== 'cohere') {
|
|
204
|
+
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ': embed.provider must be "static", "openai", or "cohere"'));
|
|
203
205
|
}
|
|
204
206
|
if (embed.url !== undefined && typeof embed.url !== 'string') {
|
|
205
207
|
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": embed.url must be a string"));
|
|
@@ -207,6 +209,89 @@ function validateEmbedBlock(value, configPath) {
|
|
|
207
209
|
if (embed.key !== undefined && typeof embed.key !== 'string') {
|
|
208
210
|
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": embed.key must be a string"));
|
|
209
211
|
}
|
|
212
|
+
// openai has no default host, so a missing url would otherwise only surface at the first
|
|
213
|
+
// vector search; a config error belongs at config time.
|
|
214
|
+
if (embed.provider === 'openai' && (embed.url === undefined || embed.url === '')) {
|
|
215
|
+
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ': embed.provider "openai" requires embed.url (e.g. "http://localhost:11434/v1" for Ollama)'));
|
|
216
|
+
}
|
|
217
|
+
if (embed.languages !== undefined && (!Array.isArray(embed.languages) || embed.languages.length === 0 || embed.languages.some(function(l) {
|
|
218
|
+
return typeof l !== 'string' || l === '';
|
|
219
|
+
}))) {
|
|
220
|
+
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ': embed.languages must be a non-empty array of language tags (e.g. ["en", "zh"])'));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function isSignalName(value) {
|
|
224
|
+
return typeof value === 'string' && _signalsts.SIGNAL_NAMES.includes(value);
|
|
225
|
+
}
|
|
226
|
+
function isSignalWeight(value) {
|
|
227
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0;
|
|
228
|
+
}
|
|
229
|
+
// Shape (an object of signal name -> weight) plus the one cross-signal prerequisite that
|
|
230
|
+
// doesn't need the top-level embed block: links is seeded by word-match rows, so declaring it
|
|
231
|
+
// without words is always empty.
|
|
232
|
+
function validatePresetSignals(name, value, configPath) {
|
|
233
|
+
if ((typeof value === "undefined" ? "undefined" : _type_of(value)) !== 'object' || value === null || Array.isArray(value) || Object.keys(value).length === 0) {
|
|
234
|
+
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": presets.").concat(name, ".signals must be a non-empty object of signal name -> weight (a finite number > 0), naming ").concat(_signalsts.SIGNAL_NAMES.join(', '), ' -- a preset that searches nothing is a mistake; name at least "words", or omit "signals" to use every signal whose prerequisites hold at weight 1'));
|
|
235
|
+
}
|
|
236
|
+
var entries = Object.entries(value);
|
|
237
|
+
var unknown = entries.filter(function(param) {
|
|
238
|
+
var _param = _sliced_to_array(param, 1), k = _param[0];
|
|
239
|
+
return !isSignalName(k);
|
|
240
|
+
}).map(function(param) {
|
|
241
|
+
var _param = _sliced_to_array(param, 1), k = _param[0];
|
|
242
|
+
return k;
|
|
243
|
+
});
|
|
244
|
+
if (unknown.length > 0) {
|
|
245
|
+
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": presets.").concat(name, ".signals names unknown signal(s) ").concat(unknown.join(', '), "; valid signals are ").concat(_signalsts.SIGNAL_NAMES.join(', ')));
|
|
246
|
+
}
|
|
247
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
248
|
+
try {
|
|
249
|
+
for(var _iterator = entries[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
250
|
+
var _step_value = _sliced_to_array(_step.value, 2), signal = _step_value[0], weight = _step_value[1];
|
|
251
|
+
if (!isSignalWeight(weight)) {
|
|
252
|
+
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": presets.").concat(name, ".signals.").concat(signal, " must be a finite number > 0"));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
} catch (err) {
|
|
256
|
+
_didIteratorError = true;
|
|
257
|
+
_iteratorError = err;
|
|
258
|
+
} finally{
|
|
259
|
+
try {
|
|
260
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
261
|
+
_iterator.return();
|
|
262
|
+
}
|
|
263
|
+
} finally{
|
|
264
|
+
if (_didIteratorError) {
|
|
265
|
+
throw _iteratorError;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
var names = new Set(entries.map(function(param) {
|
|
270
|
+
var _param = _sliced_to_array(param, 1), k = _param[0];
|
|
271
|
+
return k;
|
|
272
|
+
}));
|
|
273
|
+
var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
|
|
274
|
+
try {
|
|
275
|
+
for(var _iterator1 = Object.entries(_signalsts.SIGNAL_PREREQUISITES)[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
|
|
276
|
+
var _step_value1 = _sliced_to_array(_step1.value, 2), signal1 = _step_value1[0], requires = _step_value1[1];
|
|
277
|
+
if (names.has(signal1) && !names.has(requires)) {
|
|
278
|
+
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": presets.").concat(name, '.signals has "').concat(signal1, '" without "').concat(requires, '"; add "').concat(requires, '" to presets.').concat(name, '.signals, or drop "').concat(signal1, '"'));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
} catch (err) {
|
|
282
|
+
_didIteratorError1 = true;
|
|
283
|
+
_iteratorError1 = err;
|
|
284
|
+
} finally{
|
|
285
|
+
try {
|
|
286
|
+
if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
|
|
287
|
+
_iterator1.return();
|
|
288
|
+
}
|
|
289
|
+
} finally{
|
|
290
|
+
if (_didIteratorError1) {
|
|
291
|
+
throw _iteratorError1;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
210
295
|
}
|
|
211
296
|
function validatePreset(name, value, configPath) {
|
|
212
297
|
if ((typeof value === "undefined" ? "undefined" : _type_of(value)) !== 'object' || value === null || Array.isArray(value)) {
|
|
@@ -217,7 +302,7 @@ function validatePreset(name, value, configPath) {
|
|
|
217
302
|
return !KNOWN_PRESET_KEYS.has(k);
|
|
218
303
|
});
|
|
219
304
|
if (unknown.length > 0) {
|
|
220
|
-
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": presets.").concat(name, " has unknown key(s) ").concat(unknown.join(', '), "; a preset takes include, exclude, k,
|
|
305
|
+
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": presets.").concat(name, " has unknown key(s) ").concat(unknown.join(', '), "; a preset takes include, exclude, k, signals, where"));
|
|
221
306
|
}
|
|
222
307
|
if (!isNonEmptyStringArray(preset.include)) {
|
|
223
308
|
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": presets.").concat(name, ".include must be a non-empty array of glob strings"));
|
|
@@ -225,9 +310,7 @@ function validatePreset(name, value, configPath) {
|
|
|
225
310
|
if (preset.exclude !== undefined && !isNonEmptyStringArray(preset.exclude)) {
|
|
226
311
|
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": presets.").concat(name, ".exclude must be a non-empty array of glob strings"));
|
|
227
312
|
}
|
|
228
|
-
if (preset.
|
|
229
|
-
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": presets.").concat(name, ".semantic must be a boolean"));
|
|
230
|
-
}
|
|
313
|
+
if (preset.signals !== undefined) validatePresetSignals(name, preset.signals, configPath);
|
|
231
314
|
if (preset.k !== undefined && (typeof preset.k !== 'number' || !Number.isInteger(preset.k) || preset.k <= 0)) {
|
|
232
315
|
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": presets.").concat(name, ".k must be a positive integer"));
|
|
233
316
|
}
|
|
@@ -299,7 +382,7 @@ function validateConfig(parsed, configPath) {
|
|
|
299
382
|
}
|
|
300
383
|
var presets = cfg.presets;
|
|
301
384
|
if (!presets || (typeof presets === "undefined" ? "undefined" : _type_of(presets)) !== 'object' || Array.isArray(presets) || Object.keys(presets).length === 0) {
|
|
302
|
-
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": presets must be a non-empty object of preset name -> { include, exclude?, k?,
|
|
385
|
+
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": presets must be a non-empty object of preset name -> { include, exclude?, k?, signals?, where? }"));
|
|
303
386
|
}
|
|
304
387
|
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
305
388
|
try {
|
|
@@ -358,6 +441,32 @@ function validateConfig(parsed, configPath) {
|
|
|
358
441
|
if (cfg.content !== undefined) {
|
|
359
442
|
validateContentBlock(cfg.content, configPath);
|
|
360
443
|
}
|
|
444
|
+
// vectors' prerequisite is a named model, checked here once embed is known valid (or absent).
|
|
445
|
+
if (cfg.embed === undefined) {
|
|
446
|
+
var _iteratorNormalCompletion2 = true, _didIteratorError2 = false, _iteratorError2 = undefined;
|
|
447
|
+
try {
|
|
448
|
+
for(var _iterator2 = Object.entries(presets)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true){
|
|
449
|
+
var _step_value2 = _sliced_to_array(_step2.value, 2), name2 = _step_value2[0], value2 = _step_value2[1];
|
|
450
|
+
var signals = value2.signals;
|
|
451
|
+
if (signals && 'vectors' in signals) {
|
|
452
|
+
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": presets.").concat(name2, '.signals includes "vectors", but no "embed" block names a model; add one, or drop "vectors" from presets.').concat(name2, ".signals"));
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
} catch (err) {
|
|
456
|
+
_didIteratorError2 = true;
|
|
457
|
+
_iteratorError2 = err;
|
|
458
|
+
} finally{
|
|
459
|
+
try {
|
|
460
|
+
if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
|
|
461
|
+
_iterator2.return();
|
|
462
|
+
}
|
|
463
|
+
} finally{
|
|
464
|
+
if (_didIteratorError2) {
|
|
465
|
+
throw _iteratorError2;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
361
470
|
return cfg;
|
|
362
471
|
}
|
|
363
472
|
/* 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; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config/validate.ts"],"sourcesContent":["import { SenseError } from '../errors.ts';\nimport type { Config } from './types.ts';\n\n// Shape check for hand-edited files, so a typo names itself instead of surfacing as a\n// TypeError. Unknown top-level keys warn (forward compat); unknown keys inside a block error.\nconst KNOWN_KEYS = new Set(['$schema', 'version', 'presets', 'features', 'embed', 'content', 'queries']);\nconst KNOWN_PRESET_KEYS = new Set(['include', 'exclude', 'k', 'semantic', 'where']);\nconst KNOWN_FEATURE_KEYS = new Set(['links', 'sections', 'tags', 'rank']);\nconst KNOWN_EMBED_KEYS = new Set(['model', 'type', 'url', 'key']);\nconst KNOWN_CONTENT_KEYS = new Set(['tokenize']);\nconst SAVED_SEARCH_KEYS = new Set(['search', 'preset', 'include', 'exclude', 'where', 'k']);\n\nexport function unknownConfigKeys(cfg: Record<string, unknown>): string[] {\n return Object.keys(cfg).filter((k) => !KNOWN_KEYS.has(k));\n}\n\nfunction isNonEmptyStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.length > 0 && value.every((g) => typeof g === 'string' && g.length > 0);\n}\n\n// Pre-v3 shape check, just enough for migrateConfig to run safely (it reads scan.include\n// directly). The full v3 shape is checked by validateConfig once migration has produced it.\nexport function validateLegacyScan(parsed: unknown, configPath: string): void {\n const cfg = typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed) ? (parsed as Record<string, unknown>) : null;\n const scan = cfg?.scan as { include?: unknown } | undefined;\n if (!cfg || !scan || !isNonEmptyStringArray(scan.include)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: scan.include must be a non-empty array of glob strings`);\n }\n}\n\n// Only the shape is checked here. Whether the linked SQLite accepts the tokenizer is settled\n// by probing it in db.ts, so this never has to carry a table of which version added what.\nfunction validateContentBlock(value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: content must be an object of { tokenize?: string }`);\n }\n const block = value as Record<string, unknown>;\n const unknown = Object.keys(block).filter((k) => !KNOWN_CONTENT_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: content has unknown key(s) ${unknown.join(', ')}; content takes tokenize`);\n }\n if (block.tokenize !== undefined && (typeof block.tokenize !== 'string' || block.tokenize.trim() === '')) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: content.tokenize must be a non-empty string, e.g. \"trigram\" or \"unicode61 tokenchars '-_'\"`);\n }\n}\n\nfunction validateFeaturesBlock(value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: features must be an object of name -> boolean`);\n }\n const block = value as Record<string, unknown>;\n const unknown = Object.keys(block).filter((k) => !KNOWN_FEATURE_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: features has unknown key(s) ${unknown.join(', ')}; embed is not a features key -- see the top-level \"embed\" block`);\n }\n for (const [name, v] of Object.entries(block)) {\n if (typeof v !== 'boolean') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: features.${name} must be a boolean`);\n }\n }\n}\n\nfunction validateEmbedBlock(value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed must be an object of { model?, type?: \"static\"|\"api\", url?, key? }`);\n }\n const embed = value as Record<string, unknown>;\n const unknown = Object.keys(embed).filter((k) => !KNOWN_EMBED_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed has unknown key(s) ${unknown.join(', ')}`);\n }\n if (embed.model !== undefined && typeof embed.model !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.model must be a string`);\n }\n // The block exists to name a model; provider settings without one would read as \"vectors\n // configured\" while embedding stays off, which is the ambiguity v4 removed.\n if (embed.model === undefined || embed.model === '') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.model is required when the \"embed\" block is present (it is what turns vectors on); remove the block to index without vectors`);\n }\n if (embed.type !== undefined && embed.type !== 'static' && embed.type !== 'api') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.type must be \"static\" or \"api\"`);\n }\n if (embed.url !== undefined && typeof embed.url !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.url must be a string`);\n }\n if (embed.key !== undefined && typeof embed.key !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.key must be a string`);\n }\n}\n\nfunction validatePreset(name: string, value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name} must be an object`);\n }\n const preset = value as Record<string, unknown>;\n const unknown = Object.keys(preset).filter((k) => !KNOWN_PRESET_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name} has unknown key(s) ${unknown.join(', ')}; a preset takes include, exclude, k, semantic, where`);\n }\n if (!isNonEmptyStringArray(preset.include)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.include must be a non-empty array of glob strings`);\n }\n if (preset.exclude !== undefined && !isNonEmptyStringArray(preset.exclude)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.exclude must be a non-empty array of glob strings`);\n }\n if (preset.semantic !== undefined && typeof preset.semantic !== 'boolean') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.semantic must be a boolean`);\n }\n if (preset.k !== undefined && (typeof preset.k !== 'number' || !Number.isInteger(preset.k) || preset.k <= 0)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.k must be a positive integer`);\n }\n if (preset.where !== undefined && typeof preset.where !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.where must be a SQL condition string`);\n }\n}\n\n// A queries.<name> entry: { sql } or a saved search { search, preset?, include?, exclude?, where?, k? }.\nfunction validateSavedQuery(name: string, value: unknown, configPath: string): void {\n // A bare string used to mean SQL. It now fails rather than being inferred: an entry says\n // which of the two verbs it runs, the same choice the CLI makes explicit.\n if (typeof value === 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} must say which verb it runs: { \"sql\": ${JSON.stringify(value.length > 40 ? `${value.slice(0, 37)}...` : value)} } to run it as SQL, or { \"search\": \"...\" } for a ranked search`);\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} must be { sql } or { search, preset?, include?, exclude?, where?, k? }`);\n }\n const entry = value as Record<string, unknown>;\n if ('sql' in entry) {\n const unknown = Object.keys(entry).filter((k) => k !== 'sql');\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} has unknown key(s) ${unknown.join(', ')}; a { sql } query takes only sql`);\n }\n if (typeof entry.sql !== 'string' || entry.sql.trim() === '') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.sql must be a non-empty string`);\n }\n return;\n }\n if ('search' in entry) {\n const unknown = Object.keys(entry).filter((k) => !SAVED_SEARCH_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} has unknown key(s) ${unknown.join(', ')}; a saved search takes search, preset, include, exclude, where, k`);\n }\n // A saved entry saves a question; a scope without a question is just flags.\n if (typeof entry.search !== 'string' || entry.search.trim() === '') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.search must be non-empty text`);\n }\n if (entry.preset !== undefined && (typeof entry.preset !== 'string' || entry.preset.length === 0)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.preset must be a preset name`);\n }\n if (entry.include !== undefined && !isNonEmptyStringArray(entry.include)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.include must be a non-empty array of glob strings`);\n }\n if (entry.exclude !== undefined && !isNonEmptyStringArray(entry.exclude)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.exclude must be a non-empty array of glob strings`);\n }\n if (entry.where !== undefined && typeof entry.where !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.where must be a SQL condition string`);\n }\n if (entry.k !== undefined && (typeof entry.k !== 'number' || !Number.isInteger(entry.k) || entry.k <= 0)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.k must be a positive integer`);\n }\n return;\n }\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} must be { sql } or { search, preset?, include?, exclude?, where?, k? }`);\n}\n\nexport function validateConfig(parsed: unknown, configPath: string): Config {\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: config must be a JSON object`);\n }\n const cfg = parsed as Record<string, unknown>;\n\n // `checks` is rejected by name, not warned: silence would hide that assertions are gone.\n if (cfg.checks !== undefined) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: checks was removed in v3 -- sense check no longer asserts on saved queries; a returned row set is the reader's judgment`);\n }\n\n const presets = cfg.presets as Record<string, unknown> | undefined;\n if (!presets || typeof presets !== 'object' || Array.isArray(presets) || Object.keys(presets).length === 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets must be a non-empty object of preset name -> { include, exclude?, k?, semantic?, where? }`);\n }\n for (const [name, value] of Object.entries(presets)) {\n validatePreset(name, value, configPath);\n }\n if (presets.default === undefined) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets must include a \"default\" preset`);\n }\n\n if (cfg.queries === undefined) cfg.queries = {};\n if (typeof cfg.queries !== 'object' || cfg.queries === null || Array.isArray(cfg.queries)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries must be an object of name -> { sql } or { search }`);\n }\n const queries = cfg.queries as Record<string, unknown>;\n for (const [name, value] of Object.entries(queries)) {\n validateSavedQuery(name, value, configPath);\n }\n\n if (cfg.features !== undefined) {\n validateFeaturesBlock(cfg.features, configPath);\n }\n if (cfg.embed !== undefined) {\n validateEmbedBlock(cfg.embed, configPath);\n }\n if (cfg.content !== undefined) {\n validateContentBlock(cfg.content, configPath);\n }\n\n return cfg as unknown as Config;\n}\n"],"names":["unknownConfigKeys","validateConfig","validateLegacyScan","KNOWN_KEYS","Set","KNOWN_PRESET_KEYS","KNOWN_FEATURE_KEYS","KNOWN_EMBED_KEYS","KNOWN_CONTENT_KEYS","SAVED_SEARCH_KEYS","cfg","Object","keys","filter","k","has","isNonEmptyStringArray","value","Array","isArray","length","every","g","parsed","configPath","scan","include","SenseError","validateContentBlock","block","unknown","join","tokenize","undefined","trim","validateFeaturesBlock","entries","name","v","validateEmbedBlock","embed","model","type","url","key","validatePreset","preset","exclude","semantic","Number","isInteger","where","validateSavedQuery","JSON","stringify","slice","entry","sql","search","checks","presets","default","queries","features","content"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config/validate.ts"],"sourcesContent":["import { SenseError } from '../errors.ts';\nimport { SIGNAL_NAMES, SIGNAL_PREREQUISITES, type SignalName } from './signals.ts';\nimport type { Config } from './types.ts';\n\n// Shape check for hand-edited files, so a typo names itself instead of surfacing as a\n// TypeError. Unknown top-level keys warn (forward compat); unknown keys inside a block error.\nconst KNOWN_KEYS = new Set(['$schema', 'version', 'presets', 'features', 'embed', 'content', 'queries']);\nconst KNOWN_PRESET_KEYS = new Set(['include', 'exclude', 'k', 'signals', 'where']);\nconst KNOWN_FEATURE_KEYS = new Set(['links', 'sections', 'tags', 'rank']);\nconst KNOWN_EMBED_KEYS = new Set(['model', 'provider', 'url', 'key', 'languages']);\nconst KNOWN_CONTENT_KEYS = new Set(['tokenize']);\nconst SAVED_SEARCH_KEYS = new Set(['search', 'preset', 'include', 'exclude', 'where', 'k']);\n\nexport function unknownConfigKeys(cfg: Record<string, unknown>): string[] {\n return Object.keys(cfg).filter((k) => !KNOWN_KEYS.has(k));\n}\n\nfunction isNonEmptyStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.length > 0 && value.every((g) => typeof g === 'string' && g.length > 0);\n}\n\n// Pre-v3 shape check, just enough for migrateConfig to run safely (it reads scan.include\n// directly). The full v3 shape is checked by validateConfig once migration has produced it.\nexport function validateLegacyScan(parsed: unknown, configPath: string): void {\n const cfg = typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed) ? (parsed as Record<string, unknown>) : null;\n const scan = cfg?.scan as { include?: unknown } | undefined;\n if (!cfg || !scan || !isNonEmptyStringArray(scan.include)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: scan.include must be a non-empty array of glob strings`);\n }\n}\n\n// Only the shape is checked here. Whether the linked SQLite accepts the tokenizer is settled\n// by probing it in db.ts, so this never has to carry a table of which version added what.\nfunction validateContentBlock(value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: content must be an object of { tokenize?: string }`);\n }\n const block = value as Record<string, unknown>;\n const unknown = Object.keys(block).filter((k) => !KNOWN_CONTENT_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: content has unknown key(s) ${unknown.join(', ')}; content takes tokenize`);\n }\n if (block.tokenize !== undefined && (typeof block.tokenize !== 'string' || block.tokenize.trim() === '')) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: content.tokenize must be a non-empty string, e.g. \"trigram\" or \"unicode61 tokenchars '-_'\"`);\n }\n}\n\nfunction validateFeaturesBlock(value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: features must be an object of name -> boolean`);\n }\n const block = value as Record<string, unknown>;\n const unknown = Object.keys(block).filter((k) => !KNOWN_FEATURE_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: features has unknown key(s) ${unknown.join(', ')}; embed is not a features key -- see the top-level \"embed\" block`);\n }\n for (const [name, v] of Object.entries(block)) {\n if (typeof v !== 'boolean') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: features.${name} must be a boolean`);\n }\n }\n}\n\nfunction validateEmbedBlock(value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed must be an object of { model?, provider?: \"static\"|\"openai\"|\"cohere\", url?, key? }`);\n }\n const embed = value as Record<string, unknown>;\n const unknown = Object.keys(embed).filter((k) => !KNOWN_EMBED_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed has unknown key(s) ${unknown.join(', ')}`);\n }\n if (embed.model !== undefined && typeof embed.model !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.model must be a string`);\n }\n // The block exists to name a model; provider settings without one would read as \"vectors\n // configured\" while embedding stays off, which is the ambiguity v4 removed.\n if (embed.model === undefined || embed.model === '') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.model is required when the \"embed\" block is present (it is what turns vectors on); remove the block to index without vectors`);\n }\n if (embed.provider !== undefined && embed.provider !== 'static' && embed.provider !== 'openai' && embed.provider !== 'cohere') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.provider must be \"static\", \"openai\", or \"cohere\"`);\n }\n if (embed.url !== undefined && typeof embed.url !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.url must be a string`);\n }\n if (embed.key !== undefined && typeof embed.key !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.key must be a string`);\n }\n // openai has no default host, so a missing url would otherwise only surface at the first\n // vector search; a config error belongs at config time.\n if (embed.provider === 'openai' && (embed.url === undefined || embed.url === '')) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.provider \"openai\" requires embed.url (e.g. \"http://localhost:11434/v1\" for Ollama)`);\n }\n if (embed.languages !== undefined && (!Array.isArray(embed.languages) || embed.languages.length === 0 || embed.languages.some((l) => typeof l !== 'string' || l === ''))) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.languages must be a non-empty array of language tags (e.g. [\"en\", \"zh\"])`);\n }\n}\n\nfunction isSignalName(value: unknown): value is SignalName {\n return typeof value === 'string' && (SIGNAL_NAMES as readonly string[]).includes(value);\n}\n\nfunction isSignalWeight(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value > 0;\n}\n\n// Shape (an object of signal name -> weight) plus the one cross-signal prerequisite that\n// doesn't need the top-level embed block: links is seeded by word-match rows, so declaring it\n// without words is always empty.\nfunction validatePresetSignals(name: string, value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value) || Object.keys(value).length === 0) {\n throw new SenseError(\n 'CONFIG_INVALID',\n `${configPath}: presets.${name}.signals must be a non-empty object of signal name -> weight (a finite number > 0), naming ${SIGNAL_NAMES.join(', ')} -- a preset that searches nothing is a mistake; name at least \"words\", or omit \"signals\" to use every signal whose prerequisites hold at weight 1`\n );\n }\n const entries = Object.entries(value as Record<string, unknown>);\n const unknown = entries.filter(([k]) => !isSignalName(k)).map(([k]) => k);\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.signals names unknown signal(s) ${unknown.join(', ')}; valid signals are ${SIGNAL_NAMES.join(', ')}`);\n }\n for (const [signal, weight] of entries) {\n if (!isSignalWeight(weight)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.signals.${signal} must be a finite number > 0`);\n }\n }\n const names = new Set(entries.map(([k]) => k));\n for (const [signal, requires] of Object.entries(SIGNAL_PREREQUISITES) as Array<[SignalName, SignalName]>) {\n if (names.has(signal) && !names.has(requires)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.signals has \"${signal}\" without \"${requires}\"; add \"${requires}\" to presets.${name}.signals, or drop \"${signal}\"`);\n }\n }\n}\n\nfunction validatePreset(name: string, value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name} must be an object`);\n }\n const preset = value as Record<string, unknown>;\n const unknown = Object.keys(preset).filter((k) => !KNOWN_PRESET_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name} has unknown key(s) ${unknown.join(', ')}; a preset takes include, exclude, k, signals, where`);\n }\n if (!isNonEmptyStringArray(preset.include)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.include must be a non-empty array of glob strings`);\n }\n if (preset.exclude !== undefined && !isNonEmptyStringArray(preset.exclude)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.exclude must be a non-empty array of glob strings`);\n }\n if (preset.signals !== undefined) validatePresetSignals(name, preset.signals, configPath);\n if (preset.k !== undefined && (typeof preset.k !== 'number' || !Number.isInteger(preset.k) || preset.k <= 0)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.k must be a positive integer`);\n }\n if (preset.where !== undefined && typeof preset.where !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.where must be a SQL condition string`);\n }\n}\n\n// A queries.<name> entry: { sql } or a saved search { search, preset?, include?, exclude?, where?, k? }.\nfunction validateSavedQuery(name: string, value: unknown, configPath: string): void {\n // A bare string used to mean SQL. It now fails rather than being inferred: an entry says\n // which of the two verbs it runs, the same choice the CLI makes explicit.\n if (typeof value === 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} must say which verb it runs: { \"sql\": ${JSON.stringify(value.length > 40 ? `${value.slice(0, 37)}...` : value)} } to run it as SQL, or { \"search\": \"...\" } for a ranked search`);\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} must be { sql } or { search, preset?, include?, exclude?, where?, k? }`);\n }\n const entry = value as Record<string, unknown>;\n if ('sql' in entry) {\n const unknown = Object.keys(entry).filter((k) => k !== 'sql');\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} has unknown key(s) ${unknown.join(', ')}; a { sql } query takes only sql`);\n }\n if (typeof entry.sql !== 'string' || entry.sql.trim() === '') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.sql must be a non-empty string`);\n }\n return;\n }\n if ('search' in entry) {\n const unknown = Object.keys(entry).filter((k) => !SAVED_SEARCH_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} has unknown key(s) ${unknown.join(', ')}; a saved search takes search, preset, include, exclude, where, k`);\n }\n // A saved entry saves a question; a scope without a question is just flags.\n if (typeof entry.search !== 'string' || entry.search.trim() === '') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.search must be non-empty text`);\n }\n if (entry.preset !== undefined && (typeof entry.preset !== 'string' || entry.preset.length === 0)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.preset must be a preset name`);\n }\n if (entry.include !== undefined && !isNonEmptyStringArray(entry.include)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.include must be a non-empty array of glob strings`);\n }\n if (entry.exclude !== undefined && !isNonEmptyStringArray(entry.exclude)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.exclude must be a non-empty array of glob strings`);\n }\n if (entry.where !== undefined && typeof entry.where !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.where must be a SQL condition string`);\n }\n if (entry.k !== undefined && (typeof entry.k !== 'number' || !Number.isInteger(entry.k) || entry.k <= 0)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.k must be a positive integer`);\n }\n return;\n }\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} must be { sql } or { search, preset?, include?, exclude?, where?, k? }`);\n}\n\nexport function validateConfig(parsed: unknown, configPath: string): Config {\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: config must be a JSON object`);\n }\n const cfg = parsed as Record<string, unknown>;\n\n // `checks` is rejected by name, not warned: silence would hide that assertions are gone.\n if (cfg.checks !== undefined) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: checks was removed in v3 -- sense check no longer asserts on saved queries; a returned row set is the reader's judgment`);\n }\n\n const presets = cfg.presets as Record<string, unknown> | undefined;\n if (!presets || typeof presets !== 'object' || Array.isArray(presets) || Object.keys(presets).length === 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets must be a non-empty object of preset name -> { include, exclude?, k?, signals?, where? }`);\n }\n for (const [name, value] of Object.entries(presets)) {\n validatePreset(name, value, configPath);\n }\n if (presets.default === undefined) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets must include a \"default\" preset`);\n }\n\n if (cfg.queries === undefined) cfg.queries = {};\n if (typeof cfg.queries !== 'object' || cfg.queries === null || Array.isArray(cfg.queries)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries must be an object of name -> { sql } or { search }`);\n }\n const queries = cfg.queries as Record<string, unknown>;\n for (const [name, value] of Object.entries(queries)) {\n validateSavedQuery(name, value, configPath);\n }\n\n if (cfg.features !== undefined) {\n validateFeaturesBlock(cfg.features, configPath);\n }\n if (cfg.embed !== undefined) {\n validateEmbedBlock(cfg.embed, configPath);\n }\n if (cfg.content !== undefined) {\n validateContentBlock(cfg.content, configPath);\n }\n\n // vectors' prerequisite is a named model, checked here once embed is known valid (or absent).\n if (cfg.embed === undefined) {\n for (const [name, value] of Object.entries(presets)) {\n const signals = (value as Record<string, unknown>).signals as Record<string, unknown> | undefined;\n if (signals && 'vectors' in signals) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.signals includes \"vectors\", but no \"embed\" block names a model; add one, or drop \"vectors\" from presets.${name}.signals`);\n }\n }\n }\n\n return cfg as unknown as Config;\n}\n"],"names":["unknownConfigKeys","validateConfig","validateLegacyScan","KNOWN_KEYS","Set","KNOWN_PRESET_KEYS","KNOWN_FEATURE_KEYS","KNOWN_EMBED_KEYS","KNOWN_CONTENT_KEYS","SAVED_SEARCH_KEYS","cfg","Object","keys","filter","k","has","isNonEmptyStringArray","value","Array","isArray","length","every","g","parsed","configPath","scan","include","SenseError","validateContentBlock","block","unknown","join","tokenize","undefined","trim","validateFeaturesBlock","entries","name","v","validateEmbedBlock","embed","model","provider","url","key","languages","some","l","isSignalName","SIGNAL_NAMES","includes","isSignalWeight","Number","isFinite","validatePresetSignals","map","signal","weight","names","SIGNAL_PREREQUISITES","requires","validatePreset","preset","exclude","signals","isInteger","where","validateSavedQuery","JSON","stringify","slice","entry","sql","search","checks","presets","default","queries","features","content"],"mappings":"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"}
|
package/dist/cjs/db/open.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DatabaseSync } from 'node:sqlite';
|
|
2
2
|
import type { ResolvedConfig } from '../config/index.js';
|
|
3
3
|
export declare const DB_FILENAME = "cache.db";
|
|
4
|
-
export declare const SCHEMA_VERSION = "
|
|
4
|
+
export declare const SCHEMA_VERSION = "17";
|
|
5
5
|
export interface OpenResult {
|
|
6
6
|
db: DatabaseSync;
|
|
7
7
|
cfg: ResolvedConfig;
|
package/dist/cjs/db/open.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DatabaseSync } from 'node:sqlite';
|
|
2
2
|
import type { ResolvedConfig } from '../config/index.js';
|
|
3
3
|
export declare const DB_FILENAME = "cache.db";
|
|
4
|
-
export declare const SCHEMA_VERSION = "
|
|
4
|
+
export declare const SCHEMA_VERSION = "17";
|
|
5
5
|
export interface OpenResult {
|
|
6
6
|
db: DatabaseSync;
|
|
7
7
|
cfg: ResolvedConfig;
|
package/dist/cjs/db/open.js
CHANGED
|
@@ -45,10 +45,12 @@ function _array_without_holes(arr) {
|
|
|
45
45
|
if (Array.isArray(arr)) return _array_like_to_array(arr);
|
|
46
46
|
}
|
|
47
47
|
function _iterable_to_array(iter) {
|
|
48
|
-
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null)
|
|
48
|
+
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) {
|
|
49
|
+
return Array.from(iter);
|
|
50
|
+
}
|
|
49
51
|
}
|
|
50
52
|
function _non_iterable_spread() {
|
|
51
|
-
throw new TypeError("Invalid attempt to spread non-iterable instance
|
|
53
|
+
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
52
54
|
}
|
|
53
55
|
function _to_consumable_array(arr) {
|
|
54
56
|
return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
|
|
@@ -62,7 +64,7 @@ function _unsupported_iterable_to_array(o, minLen) {
|
|
|
62
64
|
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
63
65
|
}
|
|
64
66
|
var DB_FILENAME = 'cache.db';
|
|
65
|
-
var SCHEMA_VERSION = '
|
|
67
|
+
var SCHEMA_VERSION = '17';
|
|
66
68
|
// Stemming is English-only, but the segmentation underneath it is what decides coverage:
|
|
67
69
|
// unicode61 splits on spaces, so a language written without them (Chinese, Japanese, Thai)
|
|
68
70
|
// indexes a whole run as one token and word search finds nothing. `content.tokenize` is how
|
|
@@ -176,6 +178,11 @@ function open(cfg) {
|
|
|
176
178
|
console.error('sense: config change (content tokenizer) rebuilds the text index; vectors, links, and sections are kept');
|
|
177
179
|
db.exec('DROP TABLE content');
|
|
178
180
|
tokenizeOnlyRebuild = true;
|
|
181
|
+
} else if (changedKeys.size === 1 && changedKeys.has('embed') && (0, _reconcilets.embedIdentityAdopted)(features !== null && features !== void 0 ? features : '', wantFeatures)) {
|
|
182
|
+
// First sight of a resolved weight identity: the model itself hasn't changed, so
|
|
183
|
+
// adopt it into meta with no rebuild and no re-embed, mirroring the tokenize precedent.
|
|
184
|
+
console.error("sense: recorded the embedding model's resolved identity; vectors are unaffected");
|
|
185
|
+
(0, _sharedts.setMeta)(db, 'features', wantFeatures);
|
|
179
186
|
} else {
|
|
180
187
|
var changed = (0, _reconcilets.signatureDiff)(features !== null && features !== void 0 ? features : '', wantFeatures);
|
|
181
188
|
console.error("sense: config change (".concat(changed, ") rebuilds the index"));
|
package/dist/cjs/db/open.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/db/open.ts"],"sourcesContent":["// The Node floor (>=22.20) is explained here and nowhere else: 22.20 is the first release with\n// both FTS5 and row-returning INSERT ... RETURNING. Raise it only for a load-bearing capability.\nimport { mkdirSync, rmSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { DatabaseSync } from 'node:sqlite';\nimport type { Config, ResolvedConfig } from '../config/index.ts';\nimport { contentTokenize, featureSignature, STATE_DIR } from '../config/index.ts';\nimport { SenseError } from '../errors.ts';\nimport { activeFeatures } from '../features/index.ts';\nimport { registerFunctions } from '../sql-functions.ts';\nimport { changedSignatureKeys, rebuildContentTable, reconcile, signatureDiff } from './reconcile.ts';\nimport { getMeta, setMeta } from './shared.ts';\n\nexport const DB_FILENAME = 'cache.db';\n// Cache shape version, independent of the config's own `version`. Bumping it rebuilds\n// existing trees on first query.\nexport const SCHEMA_VERSION = '16';\n\nexport interface OpenResult {\n db: DatabaseSync;\n cfg: ResolvedConfig;\n dbPath: string;\n parsed: number;\n warnings: string[];\n}\n\n// Stemming is English-only, but the segmentation underneath it is what decides coverage:\n// unicode61 splits on spaces, so a language written without them (Chinese, Japanese, Thai)\n// indexes a whole run as one token and word search finds nothing. `content.tokenize` is how\n// such a tree picks trigram instead.\nconst DEFAULT_TOKENIZE = 'porter unicode61';\n\n// FTS5 takes its tokenizer as a string literal inside DDL, where nothing can bind, so a\n// configured value has to be concatenated. Probing a throwaway table is what makes that safe\n// and is also the whole validation: anything the linked SQLite accepts passes, anything else\n// fails here with SQLite's own message rather than against the real table. It means no table\n// of which version added which tokenizer has to be maintained.\nfunction resolveTokenize(db: DatabaseSync, cfg: Config): string {\n const configured = contentTokenize(cfg);\n if (configured === undefined) return DEFAULT_TOKENIZE;\n const literal = configured.replace(/'/g, \"''\");\n try {\n db.exec('DROP TABLE IF EXISTS temp.sense_tokenize_probe');\n db.exec(`CREATE VIRTUAL TABLE temp.sense_tokenize_probe USING fts5(x, tokenize = '${literal}')`);\n db.exec('DROP TABLE IF EXISTS temp.sense_tokenize_probe');\n } catch (err) {\n throw new SenseError('CONFIG_INVALID', `content.tokenize \"${configured}\" is not a tokenizer this SQLite accepts (${(err as Error).message}); the built-in choices are unicode61, ascii, porter, and trigram, each with their own options`);\n }\n return literal;\n}\n\n// The tokenizer the content table was actually built with, from its own DDL -- the one\n// record that cannot desynchronize from the table. NULL when the table does not exist yet.\nfunction storedTokenize(db: DatabaseSync): string | null {\n const row = db.prepare(`SELECT sql FROM sqlite_master WHERE name = 'content'`).get() as { sql: string } | undefined;\n if (!row) return null;\n const m = row.sql.match(/tokenize = '((?:[^']|'')*)'/);\n return m ? m[1] : null;\n}\n\n// Content is a separate table (not a column on frontmatter) so `SELECT * FROM frontmatter`\n// can't dump file text into context. Features add their own tables after the core ones.\nfunction ensureSchema(db: DatabaseSync, cfg: Config, tokenize: string): void {\n db.exec(`CREATE TABLE IF NOT EXISTS frontmatter (\"path\" TEXT PRIMARY KEY, \"_mtime\" REAL, \"_ctime\" REAL, \"_size\" INTEGER, \"_parse_error\" TEXT)`);\n // IF NOT EXISTS is safe against a tokenizer change: open() compares the table's own DDL\n // against the resolved tokenizer before this runs, so a stale table is already gone by now.\n // The three `_seg` sidecars are appended after path, never inserted: bm25(content, ...) and\n // snippet(content, 2, ...) are documented against the first three columns and keep working\n // (FTS5 defaults the weights it was not given). Each carries its field's exploded unspaced\n // runs for text that needs it, and an empty string for text that does not.\n db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS content USING fts5(title, summary, text, path UNINDEXED, title_seg, summary_seg, text_seg, tokenize = '${tokenize}')`);\n // Coverage, not ownership: a path can appear under several presets. path leads the PK so the\n // per-doc delete is an index hit -- keyed the other way, cold builds went quadratic.\n db.exec(`CREATE TABLE IF NOT EXISTS preset_files (\"path\" TEXT, preset TEXT, PRIMARY KEY (\"path\", preset))`);\n db.exec('CREATE INDEX IF NOT EXISTS preset_files_preset ON preset_files(preset)');\n for (const feature of activeFeatures(cfg)) feature.schema(db);\n if (getMeta(db, 'schema_version') === null) setMeta(db, 'schema_version', SCHEMA_VERSION);\n if (getMeta(db, 'features') === null) setMeta(db, 'features', featureSignature(cfg));\n}\n\nexport function docCount(db: DatabaseSync): number {\n const row = db.prepare('SELECT COUNT(*) AS n FROM frontmatter').get() as { n: number };\n return row.n;\n}\n\nexport function open(cfg: ResolvedConfig): OpenResult {\n const stateDir = join(cfg.baseDir, STATE_DIR);\n mkdirSync(stateDir, { recursive: true });\n const dbPath = join(stateDir, DB_FILENAME);\n\n const db = new DatabaseSync(dbPath);\n db.exec('PRAGMA journal_mode = WAL');\n // Covers a concurrent watcher's bulk reconcile (~5s for 500 files at 26k notes). A query\n // that outwaits it still fails loudly.\n db.exec('PRAGMA busy_timeout = 30000');\n registerFunctions(db, contentTokenize(cfg) === undefined);\n\n db.exec('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)');\n\n // Before the rebuild branch below, never after: that branch deletes the cache, so a typo'd\n // tokenizer validated later would cost a full re-index (and re-embed) to reach its own error.\n const tokenize = resolveTokenize(db, cfg);\n\n // Schema-version or feature-set mismatch: reconcile only reparses changed files, so an\n // old cache can't be patched incrementally -- rebuild instead (cheap: nothing expensive lives here).\n const version = getMeta(db, 'schema_version');\n const features = getMeta(db, 'features');\n const wantFeatures = featureSignature(cfg);\n let tokenizeOnlyRebuild = false;\n if ((version !== null && version !== SCHEMA_VERSION) || (features !== null && features !== wantFeatures)) {\n // Indexing derives from presets, so a config edit rebuilding the cache must say so and\n // name what changed -- silent rebuilds make derived indexing look like a hang or a bug.\n if (version !== null && version !== SCHEMA_VERSION) {\n console.error('sense: cache format changed (new sensemaking version); rebuilding the index');\n db.close();\n clearCache(cfg);\n return open(cfg);\n }\n const changedKeys = changedSignatureKeys(features ?? '', wantFeatures);\n // Only the tokenizer moved: frontmatter, links, sections, and embeddings are file-derived\n // and tokenizer-independent, so they don't need re-deriving. Everything else -- a preset\n // edit, an embed model change -- still takes the full clear/reopen below.\n if (changedKeys.size === 1 && changedKeys.has('tokenize')) {\n console.error('sense: config change (content tokenizer) rebuilds the text index; vectors, links, and sections are kept');\n db.exec('DROP TABLE content');\n tokenizeOnlyRebuild = true;\n } else {\n const changed = signatureDiff(features ?? '', wantFeatures);\n console.error(`sense: config change (${changed}) rebuilds the index`);\n db.close();\n clearCache(cfg);\n return open(cfg);\n }\n }\n\n // Meta can lie after a crash between table creation and the signature write; the table's\n // own DDL cannot. A mismatch here rebuilds no matter what meta says. A tokenize-only rebuild\n // just dropped content above, so storedTokenize sees no table and this guard is skipped\n // naturally rather than needing its own case.\n const stored = storedTokenize(db);\n if (stored !== null && stored !== tokenize) {\n console.error('sense: cache was built with a different content tokenizer; rebuilding the index');\n db.close();\n clearCache(cfg);\n return open(cfg);\n }\n\n ensureSchema(db, cfg, tokenize);\n\n let rebuildWarnings: string[] = [];\n if (tokenizeOnlyRebuild) {\n rebuildWarnings = rebuildContentTable(db, cfg, cfg.baseDir);\n setMeta(db, 'features', wantFeatures);\n }\n\n // 3x the largest reconcile this cache has recorded, floored at 30s and capped at 10min.\n // Installed before reconcile() -- that call is the one that races a watcher's transaction.\n const recordedMaxMs = Number(getMeta(db, 'reconcile_max_ms') ?? '0');\n db.exec(`PRAGMA busy_timeout = ${Math.min(Math.max(30000, 3 * recordedMaxMs), 600_000)}`);\n\n const { parsed, warnings } = reconcile(db, cfg, cfg.baseDir);\n\n return { db, cfg, dbPath, parsed, warnings: [...rebuildWarnings, ...warnings] };\n}\n\n// Deletes the cache directory, and only that. The rebuild is not this function's job: the next\n// open() reconciles, which is what running any command already does, so a verb that bundled the\n// two described the half that was not its own. Manual reset for a doubted cache; the schema and\n// config-signature mismatches below reset themselves.\nexport function clearCache(cfg: ResolvedConfig): void {\n rmSync(join(cfg.baseDir, STATE_DIR), { recursive: true, force: true });\n}\n"],"names":["DB_FILENAME","SCHEMA_VERSION","clearCache","docCount","open","DEFAULT_TOKENIZE","resolveTokenize","db","cfg","configured","contentTokenize","undefined","literal","replace","exec","err","SenseError","message","storedTokenize","row","prepare","get","m","sql","match","ensureSchema","tokenize","activeFeatures","feature","schema","getMeta","setMeta","featureSignature","n","stateDir","join","baseDir","STATE_DIR","mkdirSync","recursive","dbPath","DatabaseSync","registerFunctions","version","features","wantFeatures","tokenizeOnlyRebuild","console","error","close","changedKeys","changedSignatureKeys","size","has","changed","signatureDiff","stored","rebuildWarnings","rebuildContentTable","recordedMaxMs","Number","Math","min","max","reconcile","parsed","warnings","rmSync","force"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/db/open.ts"],"sourcesContent":["// The Node floor (>=22.20) is explained here and nowhere else: 22.20 is the first release with\n// both FTS5 and row-returning INSERT ... RETURNING. Raise it only for a load-bearing capability.\nimport { mkdirSync, rmSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { DatabaseSync } from 'node:sqlite';\nimport type { Config, ResolvedConfig } from '../config/index.ts';\nimport { contentTokenize, featureSignature, STATE_DIR } from '../config/index.ts';\nimport { SenseError } from '../errors.ts';\nimport { activeFeatures } from '../features/index.ts';\nimport { registerFunctions } from '../sql-functions.ts';\nimport { changedSignatureKeys, embedIdentityAdopted, rebuildContentTable, reconcile, signatureDiff } from './reconcile.ts';\nimport { getMeta, setMeta } from './shared.ts';\n\nexport const DB_FILENAME = 'cache.db';\n// Cache shape version, independent of the config's own `version`. Bumping it rebuilds\n// existing trees on first query.\nexport const SCHEMA_VERSION = '17';\n\nexport interface OpenResult {\n db: DatabaseSync;\n cfg: ResolvedConfig;\n dbPath: string;\n parsed: number;\n warnings: string[];\n}\n\n// Stemming is English-only, but the segmentation underneath it is what decides coverage:\n// unicode61 splits on spaces, so a language written without them (Chinese, Japanese, Thai)\n// indexes a whole run as one token and word search finds nothing. `content.tokenize` is how\n// such a tree picks trigram instead.\nconst DEFAULT_TOKENIZE = 'porter unicode61';\n\n// FTS5 takes its tokenizer as a string literal inside DDL, where nothing can bind, so a\n// configured value has to be concatenated. Probing a throwaway table is what makes that safe\n// and is also the whole validation: anything the linked SQLite accepts passes, anything else\n// fails here with SQLite's own message rather than against the real table. It means no table\n// of which version added which tokenizer has to be maintained.\nfunction resolveTokenize(db: DatabaseSync, cfg: Config): string {\n const configured = contentTokenize(cfg);\n if (configured === undefined) return DEFAULT_TOKENIZE;\n const literal = configured.replace(/'/g, \"''\");\n try {\n db.exec('DROP TABLE IF EXISTS temp.sense_tokenize_probe');\n db.exec(`CREATE VIRTUAL TABLE temp.sense_tokenize_probe USING fts5(x, tokenize = '${literal}')`);\n db.exec('DROP TABLE IF EXISTS temp.sense_tokenize_probe');\n } catch (err) {\n throw new SenseError('CONFIG_INVALID', `content.tokenize \"${configured}\" is not a tokenizer this SQLite accepts (${(err as Error).message}); the built-in choices are unicode61, ascii, porter, and trigram, each with their own options`);\n }\n return literal;\n}\n\n// The tokenizer the content table was actually built with, from its own DDL -- the one\n// record that cannot desynchronize from the table. NULL when the table does not exist yet.\nfunction storedTokenize(db: DatabaseSync): string | null {\n const row = db.prepare(`SELECT sql FROM sqlite_master WHERE name = 'content'`).get() as { sql: string } | undefined;\n if (!row) return null;\n const m = row.sql.match(/tokenize = '((?:[^']|'')*)'/);\n return m ? m[1] : null;\n}\n\n// Content is a separate table (not a column on frontmatter) so `SELECT * FROM frontmatter`\n// can't dump file text into context. Features add their own tables after the core ones.\nfunction ensureSchema(db: DatabaseSync, cfg: Config, tokenize: string): void {\n db.exec(`CREATE TABLE IF NOT EXISTS frontmatter (\"path\" TEXT PRIMARY KEY, \"_mtime\" REAL, \"_ctime\" REAL, \"_size\" INTEGER, \"_parse_error\" TEXT)`);\n // IF NOT EXISTS is safe against a tokenizer change: open() compares the table's own DDL\n // against the resolved tokenizer before this runs, so a stale table is already gone by now.\n // The three `_seg` sidecars are appended after path, never inserted: bm25(content, ...) and\n // snippet(content, 2, ...) are documented against the first three columns and keep working\n // (FTS5 defaults the weights it was not given). Each carries its field's exploded unspaced\n // runs for text that needs it, and an empty string for text that does not.\n db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS content USING fts5(title, summary, text, path UNINDEXED, title_seg, summary_seg, text_seg, tokenize = '${tokenize}')`);\n // Coverage, not ownership: a path can appear under several presets. path leads the PK so the\n // per-doc delete is an index hit -- keyed the other way, cold builds went quadratic.\n db.exec(`CREATE TABLE IF NOT EXISTS preset_files (\"path\" TEXT, preset TEXT, PRIMARY KEY (\"path\", preset))`);\n db.exec('CREATE INDEX IF NOT EXISTS preset_files_preset ON preset_files(preset)');\n for (const feature of activeFeatures(cfg)) feature.schema(db);\n if (getMeta(db, 'schema_version') === null) setMeta(db, 'schema_version', SCHEMA_VERSION);\n if (getMeta(db, 'features') === null) setMeta(db, 'features', featureSignature(cfg));\n}\n\nexport function docCount(db: DatabaseSync): number {\n const row = db.prepare('SELECT COUNT(*) AS n FROM frontmatter').get() as { n: number };\n return row.n;\n}\n\nexport function open(cfg: ResolvedConfig): OpenResult {\n const stateDir = join(cfg.baseDir, STATE_DIR);\n mkdirSync(stateDir, { recursive: true });\n const dbPath = join(stateDir, DB_FILENAME);\n\n const db = new DatabaseSync(dbPath);\n db.exec('PRAGMA journal_mode = WAL');\n // Covers a concurrent watcher's bulk reconcile (~5s for 500 files at 26k notes). A query\n // that outwaits it still fails loudly.\n db.exec('PRAGMA busy_timeout = 30000');\n registerFunctions(db, contentTokenize(cfg) === undefined);\n\n db.exec('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)');\n\n // Before the rebuild branch below, never after: that branch deletes the cache, so a typo'd\n // tokenizer validated later would cost a full re-index (and re-embed) to reach its own error.\n const tokenize = resolveTokenize(db, cfg);\n\n // Schema-version or feature-set mismatch: reconcile only reparses changed files, so an\n // old cache can't be patched incrementally -- rebuild instead (cheap: nothing expensive lives here).\n const version = getMeta(db, 'schema_version');\n const features = getMeta(db, 'features');\n const wantFeatures = featureSignature(cfg);\n let tokenizeOnlyRebuild = false;\n if ((version !== null && version !== SCHEMA_VERSION) || (features !== null && features !== wantFeatures)) {\n // Indexing derives from presets, so a config edit rebuilding the cache must say so and\n // name what changed -- silent rebuilds make derived indexing look like a hang or a bug.\n if (version !== null && version !== SCHEMA_VERSION) {\n console.error('sense: cache format changed (new sensemaking version); rebuilding the index');\n db.close();\n clearCache(cfg);\n return open(cfg);\n }\n const changedKeys = changedSignatureKeys(features ?? '', wantFeatures);\n // Only the tokenizer moved: frontmatter, links, sections, and embeddings are file-derived\n // and tokenizer-independent, so they don't need re-deriving. Everything else -- a preset\n // edit, an embed model change -- still takes the full clear/reopen below.\n if (changedKeys.size === 1 && changedKeys.has('tokenize')) {\n console.error('sense: config change (content tokenizer) rebuilds the text index; vectors, links, and sections are kept');\n db.exec('DROP TABLE content');\n tokenizeOnlyRebuild = true;\n } else if (changedKeys.size === 1 && changedKeys.has('embed') && embedIdentityAdopted(features ?? '', wantFeatures)) {\n // First sight of a resolved weight identity: the model itself hasn't changed, so\n // adopt it into meta with no rebuild and no re-embed, mirroring the tokenize precedent.\n console.error(\"sense: recorded the embedding model's resolved identity; vectors are unaffected\");\n setMeta(db, 'features', wantFeatures);\n } else {\n const changed = signatureDiff(features ?? '', wantFeatures);\n console.error(`sense: config change (${changed}) rebuilds the index`);\n db.close();\n clearCache(cfg);\n return open(cfg);\n }\n }\n\n // Meta can lie after a crash between table creation and the signature write; the table's\n // own DDL cannot. A mismatch here rebuilds no matter what meta says. A tokenize-only rebuild\n // just dropped content above, so storedTokenize sees no table and this guard is skipped\n // naturally rather than needing its own case.\n const stored = storedTokenize(db);\n if (stored !== null && stored !== tokenize) {\n console.error('sense: cache was built with a different content tokenizer; rebuilding the index');\n db.close();\n clearCache(cfg);\n return open(cfg);\n }\n\n ensureSchema(db, cfg, tokenize);\n\n let rebuildWarnings: string[] = [];\n if (tokenizeOnlyRebuild) {\n rebuildWarnings = rebuildContentTable(db, cfg, cfg.baseDir);\n setMeta(db, 'features', wantFeatures);\n }\n\n // 3x the largest reconcile this cache has recorded, floored at 30s and capped at 10min.\n // Installed before reconcile() -- that call is the one that races a watcher's transaction.\n const recordedMaxMs = Number(getMeta(db, 'reconcile_max_ms') ?? '0');\n db.exec(`PRAGMA busy_timeout = ${Math.min(Math.max(30000, 3 * recordedMaxMs), 600_000)}`);\n\n const { parsed, warnings } = reconcile(db, cfg, cfg.baseDir);\n\n return { db, cfg, dbPath, parsed, warnings: [...rebuildWarnings, ...warnings] };\n}\n\n// Deletes the cache directory, and only that. The rebuild is not this function's job: the next\n// open() reconciles, which is what running any command already does, so a verb that bundled the\n// two described the half that was not its own. Manual reset for a doubted cache; the schema and\n// config-signature mismatches below reset themselves.\nexport function clearCache(cfg: ResolvedConfig): void {\n rmSync(join(cfg.baseDir, STATE_DIR), { recursive: true, force: true });\n}\n"],"names":["DB_FILENAME","SCHEMA_VERSION","clearCache","docCount","open","DEFAULT_TOKENIZE","resolveTokenize","db","cfg","configured","contentTokenize","undefined","literal","replace","exec","err","SenseError","message","storedTokenize","row","prepare","get","m","sql","match","ensureSchema","tokenize","activeFeatures","feature","schema","getMeta","setMeta","featureSignature","n","stateDir","join","baseDir","STATE_DIR","mkdirSync","recursive","dbPath","DatabaseSync","registerFunctions","version","features","wantFeatures","tokenizeOnlyRebuild","console","error","close","changedKeys","changedSignatureKeys","size","has","embedIdentityAdopted","changed","signatureDiff","stored","rebuildWarnings","rebuildContentTable","recordedMaxMs","Number","Math","min","max","reconcile","parsed","warnings","rmSync","force"],"mappings":"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"}
|
|
@@ -5,5 +5,6 @@ export declare function reconcile(db: DatabaseSync, cfg: Config, baseDir: string
|
|
|
5
5
|
warnings: string[];
|
|
6
6
|
};
|
|
7
7
|
export declare function changedSignatureKeys(before: string, after: string): Set<string>;
|
|
8
|
+
export declare function embedIdentityAdopted(before: string, after: string): boolean;
|
|
8
9
|
export declare function signatureDiff(before: string, after: string): string;
|
|
9
10
|
export declare function rebuildContentTable(db: DatabaseSync, cfg: Config, baseDir: string): string[];
|
|
@@ -5,5 +5,6 @@ export declare function reconcile(db: DatabaseSync, cfg: Config, baseDir: string
|
|
|
5
5
|
warnings: string[];
|
|
6
6
|
};
|
|
7
7
|
export declare function changedSignatureKeys(before: string, after: string): Set<string>;
|
|
8
|
+
export declare function embedIdentityAdopted(before: string, after: string): boolean;
|
|
8
9
|
export declare function signatureDiff(before: string, after: string): string;
|
|
9
10
|
export declare function rebuildContentTable(db: DatabaseSync, cfg: Config, baseDir: string): string[];
|
package/dist/cjs/db/reconcile.js
CHANGED
|
@@ -12,6 +12,9 @@ _export(exports, {
|
|
|
12
12
|
get changedSignatureKeys () {
|
|
13
13
|
return changedSignatureKeys;
|
|
14
14
|
},
|
|
15
|
+
get embedIdentityAdopted () {
|
|
16
|
+
return embedIdentityAdopted;
|
|
17
|
+
},
|
|
15
18
|
get rebuildContentTable () {
|
|
16
19
|
return rebuildContentTable;
|
|
17
20
|
},
|
|
@@ -41,7 +44,9 @@ function _array_without_holes(arr) {
|
|
|
41
44
|
if (Array.isArray(arr)) return _array_like_to_array(arr);
|
|
42
45
|
}
|
|
43
46
|
function _iterable_to_array(iter) {
|
|
44
|
-
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null)
|
|
47
|
+
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) {
|
|
48
|
+
return Array.from(iter);
|
|
49
|
+
}
|
|
45
50
|
}
|
|
46
51
|
function _iterable_to_array_limit(arr, i) {
|
|
47
52
|
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
@@ -68,10 +73,10 @@ function _iterable_to_array_limit(arr, i) {
|
|
|
68
73
|
return _arr;
|
|
69
74
|
}
|
|
70
75
|
function _non_iterable_rest() {
|
|
71
|
-
throw new TypeError("Invalid attempt to destructure non-iterable instance
|
|
76
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
72
77
|
}
|
|
73
78
|
function _non_iterable_spread() {
|
|
74
|
-
throw new TypeError("Invalid attempt to spread non-iterable instance
|
|
79
|
+
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
75
80
|
}
|
|
76
81
|
function _sliced_to_array(arr, i) {
|
|
77
82
|
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
|
|
@@ -510,6 +515,18 @@ function changedSignatureKeys(before, after) {
|
|
|
510
515
|
}
|
|
511
516
|
return changed;
|
|
512
517
|
}
|
|
518
|
+
function embedIdentityAdopted(before, after) {
|
|
519
|
+
var embedPart = function embedPart(sig) {
|
|
520
|
+
return sig.split('|').find(function(p) {
|
|
521
|
+
return p.startsWith('embed:');
|
|
522
|
+
});
|
|
523
|
+
};
|
|
524
|
+
var b = embedPart(before);
|
|
525
|
+
var a = embedPart(after);
|
|
526
|
+
if (b === undefined || a === undefined) return false;
|
|
527
|
+
var at = a.indexOf('@');
|
|
528
|
+
return b.indexOf('@') === -1 && at !== -1 && a.slice(0, at) === b;
|
|
529
|
+
}
|
|
513
530
|
function signatureDiff(before, after) {
|
|
514
531
|
var changed = changedSignatureKeys(before, after);
|
|
515
532
|
var label = function label(key) {
|