@karmaniverous/jeeves-watcher 0.8.3 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/jeeves-watcher/index.js +52 -15
- package/dist/index.js +52 -15
- package/package.json +6 -6
|
@@ -621,18 +621,23 @@ class ValuesManager extends JsonFileStore {
|
|
|
621
621
|
index[ruleName] ??= {};
|
|
622
622
|
const ruleValues = index[ruleName];
|
|
623
623
|
for (const [key, value] of Object.entries(metadata)) {
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
const
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
624
|
+
// Decompose arrays into individual trackable elements so that
|
|
625
|
+
// array-typed fields (e.g. domains: ["email"]) are indexed.
|
|
626
|
+
const items = Array.isArray(value) ? value : [value];
|
|
627
|
+
for (const item of items) {
|
|
628
|
+
if (!this.isTrackable(item))
|
|
629
|
+
continue;
|
|
630
|
+
ruleValues[key] ??= [];
|
|
631
|
+
const arr = ruleValues[key];
|
|
632
|
+
if (!arr.includes(item)) {
|
|
633
|
+
arr.push(item);
|
|
634
|
+
arr.sort((a, b) => {
|
|
635
|
+
if (typeof a === typeof b) {
|
|
636
|
+
return String(a).localeCompare(String(b));
|
|
637
|
+
}
|
|
638
|
+
return typeof a < typeof b ? -1 : 1;
|
|
639
|
+
});
|
|
640
|
+
}
|
|
636
641
|
}
|
|
637
642
|
}
|
|
638
643
|
this.save();
|
|
@@ -1491,6 +1496,31 @@ function validateSchemaCompleteness(schema, ruleName) {
|
|
|
1491
1496
|
}
|
|
1492
1497
|
}
|
|
1493
1498
|
}
|
|
1499
|
+
/** Types that can produce trackable facet values. */
|
|
1500
|
+
const FACETABLE_TYPES = new Set([
|
|
1501
|
+
'string',
|
|
1502
|
+
'number',
|
|
1503
|
+
'boolean',
|
|
1504
|
+
'integer',
|
|
1505
|
+
'array',
|
|
1506
|
+
]);
|
|
1507
|
+
/**
|
|
1508
|
+
* Validate that uiHint and enum are only applied to facetable property types.
|
|
1509
|
+
* Throws if a non-facetable type (e.g. object) declares uiHint or enum.
|
|
1510
|
+
*
|
|
1511
|
+
* @param schema - Resolved schema to validate.
|
|
1512
|
+
* @param ruleName - Name of the rule (for error messages).
|
|
1513
|
+
*/
|
|
1514
|
+
function validateFacetTypes(schema, ruleName) {
|
|
1515
|
+
for (const [propName, propDef] of Object.entries(schema.properties)) {
|
|
1516
|
+
if (!propDef.type)
|
|
1517
|
+
continue;
|
|
1518
|
+
if (!FACETABLE_TYPES.has(propDef.type) &&
|
|
1519
|
+
(propDef.uiHint !== undefined || propDef.enum !== undefined)) {
|
|
1520
|
+
throw new Error(`Property "${propName}" in rule "${ruleName}" has type "${propDef.type}" with uiHint/enum. Facet hints are only valid on string, number, boolean, integer, or array types.`);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1494
1524
|
|
|
1495
1525
|
/**
|
|
1496
1526
|
* @module rules/apply
|
|
@@ -1557,6 +1587,7 @@ async function applyRules(compiledRules, attributes, options = {}) {
|
|
|
1557
1587
|
});
|
|
1558
1588
|
// Validate schema completeness
|
|
1559
1589
|
validateSchemaCompleteness(mergedSchema, rule.name);
|
|
1590
|
+
validateFacetTypes(mergedSchema, rule.name);
|
|
1560
1591
|
// Resolve and coerce metadata
|
|
1561
1592
|
const schemaOutput = resolveAndCoerce(mergedSchema, attributes, hbs);
|
|
1562
1593
|
merged = { ...merged, ...schemaOutput };
|
|
@@ -2668,6 +2699,7 @@ function validateInferenceRuleSchemas(parsed) {
|
|
|
2668
2699
|
globalSchemas: parsed.schemas,
|
|
2669
2700
|
});
|
|
2670
2701
|
validateSchemaCompleteness(merged, rule.name);
|
|
2702
|
+
validateFacetTypes(merged, rule.name);
|
|
2671
2703
|
}
|
|
2672
2704
|
catch (error) {
|
|
2673
2705
|
return [
|
|
@@ -2748,6 +2780,9 @@ function computeRulesHash(rules) {
|
|
|
2748
2780
|
* A property is facetable if it declares `uiHint` or `enum`.
|
|
2749
2781
|
*/
|
|
2750
2782
|
function isFacetable(prop) {
|
|
2783
|
+
// Reject non-primitive types that can never produce trackable values.
|
|
2784
|
+
if (prop.type === 'object')
|
|
2785
|
+
return false;
|
|
2751
2786
|
return prop.uiHint !== undefined || prop.enum !== undefined;
|
|
2752
2787
|
}
|
|
2753
2788
|
/**
|
|
@@ -5987,6 +6022,9 @@ class JeevesWatcher {
|
|
|
5987
6022
|
const { templateEngine, customMapLib } = await buildTemplateEngineAndCustomMapLib(this.config, configDir);
|
|
5988
6023
|
this.helperIntrospection = await introspectHelpers(this.config, configDir);
|
|
5989
6024
|
const processorConfig = createProcessorConfig(this.config, configDir, customMapLib);
|
|
6025
|
+
const stateDir = this.config.stateDir ?? this.config.metadataDir ?? '.jeeves-metadata';
|
|
6026
|
+
this.issuesManager = new IssuesManager(stateDir, logger);
|
|
6027
|
+
this.valuesManager = new ValuesManager(stateDir, logger);
|
|
5990
6028
|
const processor = this.factories.createDocumentProcessor({
|
|
5991
6029
|
config: processorConfig,
|
|
5992
6030
|
embeddingProvider,
|
|
@@ -5994,6 +6032,8 @@ class JeevesWatcher {
|
|
|
5994
6032
|
compiledRules,
|
|
5995
6033
|
logger,
|
|
5996
6034
|
templateEngine,
|
|
6035
|
+
issuesManager: this.issuesManager,
|
|
6036
|
+
valuesManager: this.valuesManager,
|
|
5997
6037
|
});
|
|
5998
6038
|
this.processor = processor;
|
|
5999
6039
|
this.queue = this.factories.createEventQueue({
|
|
@@ -6002,9 +6042,6 @@ class JeevesWatcher {
|
|
|
6002
6042
|
rateLimitPerMinute: this.config.embedding.rateLimitPerMinute,
|
|
6003
6043
|
});
|
|
6004
6044
|
this.watcher = createWatcher(this.config, this.factories, this.queue, processor, logger, this.runtimeOptions);
|
|
6005
|
-
const stateDir = this.config.stateDir ?? this.config.metadataDir ?? '.jeeves-metadata';
|
|
6006
|
-
this.issuesManager = new IssuesManager(stateDir, logger);
|
|
6007
|
-
this.valuesManager = new ValuesManager(stateDir, logger);
|
|
6008
6045
|
this.server = await this.startApiServer();
|
|
6009
6046
|
this.watcher.start();
|
|
6010
6047
|
this.startConfigWatch();
|
package/dist/index.js
CHANGED
|
@@ -846,6 +846,31 @@ function validateSchemaCompleteness(schema, ruleName) {
|
|
|
846
846
|
}
|
|
847
847
|
}
|
|
848
848
|
}
|
|
849
|
+
/** Types that can produce trackable facet values. */
|
|
850
|
+
const FACETABLE_TYPES = new Set([
|
|
851
|
+
'string',
|
|
852
|
+
'number',
|
|
853
|
+
'boolean',
|
|
854
|
+
'integer',
|
|
855
|
+
'array',
|
|
856
|
+
]);
|
|
857
|
+
/**
|
|
858
|
+
* Validate that uiHint and enum are only applied to facetable property types.
|
|
859
|
+
* Throws if a non-facetable type (e.g. object) declares uiHint or enum.
|
|
860
|
+
*
|
|
861
|
+
* @param schema - Resolved schema to validate.
|
|
862
|
+
* @param ruleName - Name of the rule (for error messages).
|
|
863
|
+
*/
|
|
864
|
+
function validateFacetTypes(schema, ruleName) {
|
|
865
|
+
for (const [propName, propDef] of Object.entries(schema.properties)) {
|
|
866
|
+
if (!propDef.type)
|
|
867
|
+
continue;
|
|
868
|
+
if (!FACETABLE_TYPES.has(propDef.type) &&
|
|
869
|
+
(propDef.uiHint !== undefined || propDef.enum !== undefined)) {
|
|
870
|
+
throw new Error(`Property "${propName}" in rule "${ruleName}" has type "${propDef.type}" with uiHint/enum. Facet hints are only valid on string, number, boolean, integer, or array types.`);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
}
|
|
849
874
|
|
|
850
875
|
/**
|
|
851
876
|
* @module rules/apply
|
|
@@ -912,6 +937,7 @@ async function applyRules(compiledRules, attributes, options = {}) {
|
|
|
912
937
|
});
|
|
913
938
|
// Validate schema completeness
|
|
914
939
|
validateSchemaCompleteness(mergedSchema, rule.name);
|
|
940
|
+
validateFacetTypes(mergedSchema, rule.name);
|
|
915
941
|
// Resolve and coerce metadata
|
|
916
942
|
const schemaOutput = resolveAndCoerce(mergedSchema, attributes, hbs);
|
|
917
943
|
merged = { ...merged, ...schemaOutput };
|
|
@@ -2359,6 +2385,7 @@ function validateInferenceRuleSchemas(parsed) {
|
|
|
2359
2385
|
globalSchemas: parsed.schemas,
|
|
2360
2386
|
});
|
|
2361
2387
|
validateSchemaCompleteness(merged, rule.name);
|
|
2388
|
+
validateFacetTypes(merged, rule.name);
|
|
2362
2389
|
}
|
|
2363
2390
|
catch (error) {
|
|
2364
2391
|
return [
|
|
@@ -2439,6 +2466,9 @@ function computeRulesHash(rules) {
|
|
|
2439
2466
|
* A property is facetable if it declares `uiHint` or `enum`.
|
|
2440
2467
|
*/
|
|
2441
2468
|
function isFacetable(prop) {
|
|
2469
|
+
// Reject non-primitive types that can never produce trackable values.
|
|
2470
|
+
if (prop.type === 'object')
|
|
2471
|
+
return false;
|
|
2442
2472
|
return prop.uiHint !== undefined || prop.enum !== undefined;
|
|
2443
2473
|
}
|
|
2444
2474
|
/**
|
|
@@ -3509,18 +3539,23 @@ class ValuesManager extends JsonFileStore {
|
|
|
3509
3539
|
index[ruleName] ??= {};
|
|
3510
3540
|
const ruleValues = index[ruleName];
|
|
3511
3541
|
for (const [key, value] of Object.entries(metadata)) {
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
const
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3542
|
+
// Decompose arrays into individual trackable elements so that
|
|
3543
|
+
// array-typed fields (e.g. domains: ["email"]) are indexed.
|
|
3544
|
+
const items = Array.isArray(value) ? value : [value];
|
|
3545
|
+
for (const item of items) {
|
|
3546
|
+
if (!this.isTrackable(item))
|
|
3547
|
+
continue;
|
|
3548
|
+
ruleValues[key] ??= [];
|
|
3549
|
+
const arr = ruleValues[key];
|
|
3550
|
+
if (!arr.includes(item)) {
|
|
3551
|
+
arr.push(item);
|
|
3552
|
+
arr.sort((a, b) => {
|
|
3553
|
+
if (typeof a === typeof b) {
|
|
3554
|
+
return String(a).localeCompare(String(b));
|
|
3555
|
+
}
|
|
3556
|
+
return typeof a < typeof b ? -1 : 1;
|
|
3557
|
+
});
|
|
3558
|
+
}
|
|
3524
3559
|
}
|
|
3525
3560
|
}
|
|
3526
3561
|
this.save();
|
|
@@ -5965,6 +6000,9 @@ class JeevesWatcher {
|
|
|
5965
6000
|
const { templateEngine, customMapLib } = await buildTemplateEngineAndCustomMapLib(this.config, configDir);
|
|
5966
6001
|
this.helperIntrospection = await introspectHelpers(this.config, configDir);
|
|
5967
6002
|
const processorConfig = createProcessorConfig(this.config, configDir, customMapLib);
|
|
6003
|
+
const stateDir = this.config.stateDir ?? this.config.metadataDir ?? '.jeeves-metadata';
|
|
6004
|
+
this.issuesManager = new IssuesManager(stateDir, logger);
|
|
6005
|
+
this.valuesManager = new ValuesManager(stateDir, logger);
|
|
5968
6006
|
const processor = this.factories.createDocumentProcessor({
|
|
5969
6007
|
config: processorConfig,
|
|
5970
6008
|
embeddingProvider,
|
|
@@ -5972,6 +6010,8 @@ class JeevesWatcher {
|
|
|
5972
6010
|
compiledRules,
|
|
5973
6011
|
logger,
|
|
5974
6012
|
templateEngine,
|
|
6013
|
+
issuesManager: this.issuesManager,
|
|
6014
|
+
valuesManager: this.valuesManager,
|
|
5975
6015
|
});
|
|
5976
6016
|
this.processor = processor;
|
|
5977
6017
|
this.queue = this.factories.createEventQueue({
|
|
@@ -5980,9 +6020,6 @@ class JeevesWatcher {
|
|
|
5980
6020
|
rateLimitPerMinute: this.config.embedding.rateLimitPerMinute,
|
|
5981
6021
|
});
|
|
5982
6022
|
this.watcher = createWatcher(this.config, this.factories, this.queue, processor, logger, this.runtimeOptions);
|
|
5983
|
-
const stateDir = this.config.stateDir ?? this.config.metadataDir ?? '.jeeves-metadata';
|
|
5984
|
-
this.issuesManager = new IssuesManager(stateDir, logger);
|
|
5985
|
-
this.valuesManager = new ValuesManager(stateDir, logger);
|
|
5986
6023
|
this.server = await this.startApiServer();
|
|
5987
6024
|
this.watcher.start();
|
|
5988
6025
|
this.startConfigWatch();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@karmaniverous/jeeves-watcher",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"author": "Jason Williscroft",
|
|
5
5
|
"description": "Filesystem watcher that keeps a Qdrant vector store in sync with document changes",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
@@ -84,22 +84,22 @@
|
|
|
84
84
|
"zod": "^4.3.6"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
|
-
"@dotenvx/dotenvx": "^1.
|
|
87
|
+
"@dotenvx/dotenvx": "^1.54.1",
|
|
88
88
|
"@rollup/plugin-alias": "^6.0.0",
|
|
89
|
-
"@rollup/plugin-commonjs": "^29.0.
|
|
89
|
+
"@rollup/plugin-commonjs": "^29.0.2",
|
|
90
90
|
"@rollup/plugin-json": "^6.1.0",
|
|
91
91
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
92
92
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
93
93
|
"@types/fs-extra": "^11.0.4",
|
|
94
94
|
"@types/js-yaml": "*",
|
|
95
|
-
"@types/node": "^25.3.
|
|
95
|
+
"@types/node": "^25.3.5",
|
|
96
96
|
"@types/picomatch": "^4.0.2",
|
|
97
97
|
"@types/uuid": "*",
|
|
98
98
|
"@vitest/coverage-v8": "^4.0.18",
|
|
99
99
|
"auto-changelog": "^2.5.0",
|
|
100
100
|
"cross-env": "^10.1.0",
|
|
101
|
-
"fs-extra": "^11.3.
|
|
102
|
-
"happy-dom": "^20.
|
|
101
|
+
"fs-extra": "^11.3.4",
|
|
102
|
+
"happy-dom": "^20.8.3",
|
|
103
103
|
"knip": "^5.85.0",
|
|
104
104
|
"release-it": "^19.2.4",
|
|
105
105
|
"rollup": "^4.59.0",
|