@karmaniverous/jeeves-watcher 0.4.2 → 0.4.4
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/config.schema.json +38 -11
- package/dist/cjs/index.js +278 -168
- package/dist/cli/jeeves-watcher/index.js +279 -169
- package/dist/index.d.ts +10 -9
- package/dist/index.iife.js +278 -169
- package/dist/index.iife.min.js +1 -1
- package/dist/mjs/index.js +279 -169
- package/package.json +1 -1
package/dist/index.iife.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function (exports, Fastify, promises, node_path, picomatch, radash, node_crypto, node_fs, ignore, Handlebars, dayjs, hastUtilToMdast, mdastUtilFromAdf, mdastUtilToMarkdown, rehypeParse, unified, chokidar, cosmiconfig, zod,
|
|
1
|
+
(function (exports, Fastify, promises, node_path, picomatch, radash, node_crypto, node_fs, ignore, node_url, jsonmap, Handlebars, dayjs, hastUtilToMdast, mdastUtilFromAdf, mdastUtilToMarkdown, rehypeParse, unified, chokidar, cosmiconfig, zod, googleGenai, pino, uuid, cheerio, yaml, mammoth, Ajv, addFormats, textsplitters, jsClientRest) {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
function _interopNamespaceDefault(e) {
|
|
@@ -628,6 +628,251 @@
|
|
|
628
628
|
}
|
|
629
629
|
}
|
|
630
630
|
|
|
631
|
+
/**
|
|
632
|
+
* @module rules/templates
|
|
633
|
+
* Resolves template variables (`${path.to.value}`) in rule `set` objects against file attributes.
|
|
634
|
+
*/
|
|
635
|
+
/**
|
|
636
|
+
* Resolve `${template.vars}` in a value against the given attributes.
|
|
637
|
+
*
|
|
638
|
+
* @param value - The value to resolve.
|
|
639
|
+
* @param attributes - The file attributes for variable lookup.
|
|
640
|
+
* @returns The resolved value.
|
|
641
|
+
*/
|
|
642
|
+
function resolveTemplateVars(value, attributes) {
|
|
643
|
+
if (typeof value !== 'string')
|
|
644
|
+
return value;
|
|
645
|
+
return value.replace(/\$\{([^}]+)\}/g, (_match, varPath) => {
|
|
646
|
+
const current = radash.get(attributes, varPath);
|
|
647
|
+
if (current === null || current === undefined)
|
|
648
|
+
return '';
|
|
649
|
+
return typeof current === 'string' ? current : JSON.stringify(current);
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Resolve all template variables in a `set` object.
|
|
654
|
+
*
|
|
655
|
+
* @param setObj - The key-value pairs to resolve.
|
|
656
|
+
* @param attributes - The file attributes for variable lookup.
|
|
657
|
+
* @returns The resolved key-value pairs.
|
|
658
|
+
*/
|
|
659
|
+
function resolveSet(setObj, attributes) {
|
|
660
|
+
const result = {};
|
|
661
|
+
for (const [key, value] of Object.entries(setObj)) {
|
|
662
|
+
result[key] = resolveTemplateVars(value, attributes);
|
|
663
|
+
}
|
|
664
|
+
return result;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* @module rules/apply
|
|
669
|
+
* Applies compiled inference rules to file attributes, producing merged metadata via template resolution and JsonMap transforms.
|
|
670
|
+
*/
|
|
671
|
+
/**
|
|
672
|
+
* Create the lib object for JsonMap transformations.
|
|
673
|
+
*
|
|
674
|
+
* @param configDir - Optional config directory for resolving relative file paths in lookups.
|
|
675
|
+
* @returns The lib object.
|
|
676
|
+
*/
|
|
677
|
+
function createJsonMapLib(configDir, customLib) {
|
|
678
|
+
// Cache loaded JSON files within a single applyRules invocation.
|
|
679
|
+
const jsonCache = new Map();
|
|
680
|
+
const loadJson = (filePath) => {
|
|
681
|
+
const resolvedPath = configDir ? node_path.resolve(configDir, filePath) : filePath;
|
|
682
|
+
if (!jsonCache.has(resolvedPath)) {
|
|
683
|
+
const raw = node_fs.readFileSync(resolvedPath, 'utf-8');
|
|
684
|
+
jsonCache.set(resolvedPath, JSON.parse(raw));
|
|
685
|
+
}
|
|
686
|
+
return jsonCache.get(resolvedPath);
|
|
687
|
+
};
|
|
688
|
+
return {
|
|
689
|
+
split: (str, separator) => str.split(separator),
|
|
690
|
+
slice: (arr, start, end) => arr.slice(start, end),
|
|
691
|
+
join: (arr, separator) => arr.join(separator),
|
|
692
|
+
toLowerCase: (str) => str.toLowerCase(),
|
|
693
|
+
replace: (str, search, replacement) => str.replace(search, replacement),
|
|
694
|
+
get: (obj, path) => radash.get(obj, path),
|
|
695
|
+
/**
|
|
696
|
+
* Load a JSON file (relative to configDir) and look up a value by key,
|
|
697
|
+
* optionally drilling into a sub-path.
|
|
698
|
+
*
|
|
699
|
+
* @param filePath - Path to a JSON file (resolved relative to configDir).
|
|
700
|
+
* @param key - Top-level key to look up.
|
|
701
|
+
* @param field - Optional dot-path into the looked-up entry.
|
|
702
|
+
* @returns The resolved value, or null if not found.
|
|
703
|
+
*/
|
|
704
|
+
lookupJson: (filePath, key, field) => {
|
|
705
|
+
const data = loadJson(filePath);
|
|
706
|
+
const entry = data[key];
|
|
707
|
+
if (entry === undefined || entry === null)
|
|
708
|
+
return null;
|
|
709
|
+
if (field)
|
|
710
|
+
return radash.get(entry, field) ?? null;
|
|
711
|
+
return entry;
|
|
712
|
+
},
|
|
713
|
+
/**
|
|
714
|
+
* Map an array of keys through a JSON lookup file, collecting a specific
|
|
715
|
+
* field from each matching entry. Non-matching keys are silently skipped.
|
|
716
|
+
* Array-valued fields are flattened into the result.
|
|
717
|
+
*
|
|
718
|
+
* @param filePath - Path to a JSON file (resolved relative to configDir).
|
|
719
|
+
* @param keys - Array of top-level keys to look up.
|
|
720
|
+
* @param field - Dot-path into each looked-up entry.
|
|
721
|
+
* @returns Flat array of resolved values.
|
|
722
|
+
*/
|
|
723
|
+
mapLookup: (filePath, keys, field) => {
|
|
724
|
+
if (!Array.isArray(keys))
|
|
725
|
+
return [];
|
|
726
|
+
const data = loadJson(filePath);
|
|
727
|
+
const results = [];
|
|
728
|
+
for (const k of keys) {
|
|
729
|
+
if (typeof k !== 'string')
|
|
730
|
+
continue;
|
|
731
|
+
const entry = data[k];
|
|
732
|
+
if (entry === undefined || entry === null)
|
|
733
|
+
continue;
|
|
734
|
+
const val = radash.get(entry, field);
|
|
735
|
+
if (val === undefined || val === null)
|
|
736
|
+
continue;
|
|
737
|
+
if (Array.isArray(val)) {
|
|
738
|
+
for (const item of val) {
|
|
739
|
+
results.push(item);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
else {
|
|
743
|
+
results.push(val);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
return results;
|
|
747
|
+
},
|
|
748
|
+
...customLib,
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Load custom JsonMap lib functions from file paths.
|
|
753
|
+
*
|
|
754
|
+
* Each module should default-export an object of functions,
|
|
755
|
+
* or use named exports. Only function-valued exports are merged.
|
|
756
|
+
*
|
|
757
|
+
* @param paths - File paths to custom lib modules.
|
|
758
|
+
* @param configDir - Directory to resolve relative paths against.
|
|
759
|
+
* @returns The merged custom lib functions.
|
|
760
|
+
*/
|
|
761
|
+
async function loadCustomMapHelpers(paths, configDir) {
|
|
762
|
+
const merged = {};
|
|
763
|
+
for (const p of paths) {
|
|
764
|
+
const resolved = node_path.resolve(configDir, p);
|
|
765
|
+
const mod = (await import(node_url.pathToFileURL(resolved).href));
|
|
766
|
+
const fns = typeof mod.default === 'object' && mod.default !== null
|
|
767
|
+
? mod.default
|
|
768
|
+
: mod;
|
|
769
|
+
for (const [key, val] of Object.entries(fns)) {
|
|
770
|
+
if (typeof val === 'function') {
|
|
771
|
+
merged[key] = val;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
return merged;
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Apply compiled inference rules to file attributes, returning merged metadata and optional rendered content.
|
|
779
|
+
*
|
|
780
|
+
* Rules are evaluated in order; later rules override earlier ones.
|
|
781
|
+
* If a rule has a `map`, the JsonMap transformation is applied after `set` resolution,
|
|
782
|
+
* and map output overrides set output on conflict.
|
|
783
|
+
*
|
|
784
|
+
* @param compiledRules - The compiled rules to evaluate.
|
|
785
|
+
* @param attributes - The file attributes to match against.
|
|
786
|
+
* @param namedMaps - Optional record of named JsonMap definitions.
|
|
787
|
+
* @param logger - Optional logger for warnings (falls back to console.warn).
|
|
788
|
+
* @param templateEngine - Optional template engine for rendering content templates.
|
|
789
|
+
* @param configDir - Optional config directory for resolving .json map file paths.
|
|
790
|
+
* @returns The merged metadata and optional rendered content.
|
|
791
|
+
*/
|
|
792
|
+
async function applyRules(compiledRules, attributes, namedMaps, logger, templateEngine, configDir, customMapLib) {
|
|
793
|
+
// JsonMap's type definitions expect a generic JsonMapLib shape with unary functions.
|
|
794
|
+
// Our helper functions accept multiple args, which JsonMap supports at runtime.
|
|
795
|
+
const lib = createJsonMapLib(configDir, customMapLib);
|
|
796
|
+
let merged = {};
|
|
797
|
+
let renderedContent = null;
|
|
798
|
+
const log = logger ?? console;
|
|
799
|
+
for (const [ruleIndex, { rule, validate }] of compiledRules.entries()) {
|
|
800
|
+
if (validate(attributes)) {
|
|
801
|
+
// Apply set resolution
|
|
802
|
+
const setOutput = resolveSet(rule.set, attributes);
|
|
803
|
+
merged = { ...merged, ...setOutput };
|
|
804
|
+
// Apply map transformation if present
|
|
805
|
+
if (rule.map) {
|
|
806
|
+
let mapDef;
|
|
807
|
+
// Resolve map reference
|
|
808
|
+
if (typeof rule.map === 'string') {
|
|
809
|
+
if (rule.map.endsWith('.json') && configDir) {
|
|
810
|
+
// File path: load from .json file
|
|
811
|
+
try {
|
|
812
|
+
const mapPath = node_path.resolve(configDir, rule.map);
|
|
813
|
+
const raw = node_fs.readFileSync(mapPath, 'utf-8');
|
|
814
|
+
mapDef = JSON.parse(raw);
|
|
815
|
+
}
|
|
816
|
+
catch (error) {
|
|
817
|
+
log.warn(`Failed to load map file "${rule.map}": ${error instanceof Error ? error.message : String(error)}`);
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
else {
|
|
822
|
+
mapDef = namedMaps?.[rule.map];
|
|
823
|
+
if (!mapDef) {
|
|
824
|
+
log.warn(`Map reference "${rule.map}" not found in named maps. Skipping map transformation.`);
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
else {
|
|
830
|
+
mapDef = rule.map;
|
|
831
|
+
}
|
|
832
|
+
// Execute JsonMap transformation
|
|
833
|
+
try {
|
|
834
|
+
const jsonMap = new jsonmap.JsonMap(mapDef, lib);
|
|
835
|
+
const mapOutput = await jsonMap.transform(attributes);
|
|
836
|
+
if (mapOutput &&
|
|
837
|
+
typeof mapOutput === 'object' &&
|
|
838
|
+
!Array.isArray(mapOutput)) {
|
|
839
|
+
merged = { ...merged, ...mapOutput };
|
|
840
|
+
}
|
|
841
|
+
else {
|
|
842
|
+
log.warn(`JsonMap transformation did not return an object; skipping merge.`);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
catch (error) {
|
|
846
|
+
log.warn(`JsonMap transformation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
// Render template if present
|
|
850
|
+
if (rule.template && templateEngine) {
|
|
851
|
+
const templateKey = `rule-${String(ruleIndex)}`;
|
|
852
|
+
// Build template context: attributes (with json spread at top) + map output
|
|
853
|
+
const context = {
|
|
854
|
+
...(attributes.json ?? {}),
|
|
855
|
+
...attributes,
|
|
856
|
+
...merged,
|
|
857
|
+
};
|
|
858
|
+
try {
|
|
859
|
+
const result = templateEngine.render(templateKey, context);
|
|
860
|
+
if (result && result.trim()) {
|
|
861
|
+
renderedContent = result;
|
|
862
|
+
}
|
|
863
|
+
else {
|
|
864
|
+
log.warn(`Template for rule ${String(ruleIndex)} rendered empty output. Falling back to raw content.`);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
catch (error) {
|
|
868
|
+
log.warn(`Template render failed for rule ${String(ruleIndex)}: ${error instanceof Error ? error.message : String(error)}. Falling back to raw content.`);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
return { metadata: merged, renderedContent };
|
|
874
|
+
}
|
|
875
|
+
|
|
631
876
|
/**
|
|
632
877
|
* @module templates/helpers
|
|
633
878
|
* Registers built-in Handlebars helpers for content templates.
|
|
@@ -765,7 +1010,7 @@
|
|
|
765
1010
|
async function loadCustomHelpers(hbs, paths, configDir) {
|
|
766
1011
|
for (const p of paths) {
|
|
767
1012
|
const resolved = node_path.resolve(configDir, p);
|
|
768
|
-
const mod = (await import(resolved));
|
|
1013
|
+
const mod = (await import(node_url.pathToFileURL(resolved).href));
|
|
769
1014
|
if (typeof mod.default === 'function') {
|
|
770
1015
|
mod.default(hbs);
|
|
771
1016
|
}
|
|
@@ -1156,6 +1401,17 @@
|
|
|
1156
1401
|
})
|
|
1157
1402
|
.optional()
|
|
1158
1403
|
.describe('Custom Handlebars helper registration.'),
|
|
1404
|
+
/** Custom JsonMap lib function registration. */
|
|
1405
|
+
mapHelpers: zod.z
|
|
1406
|
+
.object({
|
|
1407
|
+
/** File paths to custom lib modules (each exports functions to merge into the JsonMap lib). */
|
|
1408
|
+
paths: zod.z
|
|
1409
|
+
.array(zod.z.string())
|
|
1410
|
+
.optional()
|
|
1411
|
+
.describe('File paths to JS modules exporting functions to merge into the JsonMap lib.'),
|
|
1412
|
+
})
|
|
1413
|
+
.optional()
|
|
1414
|
+
.describe('Custom JsonMap lib function registration.'),
|
|
1159
1415
|
/** Logging configuration. */
|
|
1160
1416
|
logging: loggingConfigSchema.optional().describe('Logging configuration.'),
|
|
1161
1417
|
/** Timeout in milliseconds for graceful shutdown. */
|
|
@@ -1646,160 +1902,6 @@
|
|
|
1646
1902
|
return extractPlaintext(filePath);
|
|
1647
1903
|
}
|
|
1648
1904
|
|
|
1649
|
-
/**
|
|
1650
|
-
* @module rules/templates
|
|
1651
|
-
* Resolves template variables (`${path.to.value}`) in rule `set` objects against file attributes.
|
|
1652
|
-
*/
|
|
1653
|
-
/**
|
|
1654
|
-
* Resolve `${template.vars}` in a value against the given attributes.
|
|
1655
|
-
*
|
|
1656
|
-
* @param value - The value to resolve.
|
|
1657
|
-
* @param attributes - The file attributes for variable lookup.
|
|
1658
|
-
* @returns The resolved value.
|
|
1659
|
-
*/
|
|
1660
|
-
function resolveTemplateVars(value, attributes) {
|
|
1661
|
-
if (typeof value !== 'string')
|
|
1662
|
-
return value;
|
|
1663
|
-
return value.replace(/\$\{([^}]+)\}/g, (_match, varPath) => {
|
|
1664
|
-
const current = radash.get(attributes, varPath);
|
|
1665
|
-
if (current === null || current === undefined)
|
|
1666
|
-
return '';
|
|
1667
|
-
return typeof current === 'string' ? current : JSON.stringify(current);
|
|
1668
|
-
});
|
|
1669
|
-
}
|
|
1670
|
-
/**
|
|
1671
|
-
* Resolve all template variables in a `set` object.
|
|
1672
|
-
*
|
|
1673
|
-
* @param setObj - The key-value pairs to resolve.
|
|
1674
|
-
* @param attributes - The file attributes for variable lookup.
|
|
1675
|
-
* @returns The resolved key-value pairs.
|
|
1676
|
-
*/
|
|
1677
|
-
function resolveSet(setObj, attributes) {
|
|
1678
|
-
const result = {};
|
|
1679
|
-
for (const [key, value] of Object.entries(setObj)) {
|
|
1680
|
-
result[key] = resolveTemplateVars(value, attributes);
|
|
1681
|
-
}
|
|
1682
|
-
return result;
|
|
1683
|
-
}
|
|
1684
|
-
|
|
1685
|
-
/**
|
|
1686
|
-
* @module rules/apply
|
|
1687
|
-
* Applies compiled inference rules to file attributes, producing merged metadata via template resolution and JsonMap transforms.
|
|
1688
|
-
*/
|
|
1689
|
-
/**
|
|
1690
|
-
* Create the lib object for JsonMap transformations.
|
|
1691
|
-
*
|
|
1692
|
-
* @returns The lib object.
|
|
1693
|
-
*/
|
|
1694
|
-
function createJsonMapLib() {
|
|
1695
|
-
return {
|
|
1696
|
-
split: (str, separator) => str.split(separator),
|
|
1697
|
-
slice: (arr, start, end) => arr.slice(start, end),
|
|
1698
|
-
join: (arr, separator) => arr.join(separator),
|
|
1699
|
-
toLowerCase: (str) => str.toLowerCase(),
|
|
1700
|
-
replace: (str, search, replacement) => str.replace(search, replacement),
|
|
1701
|
-
get: (obj, path) => radash.get(obj, path),
|
|
1702
|
-
};
|
|
1703
|
-
}
|
|
1704
|
-
/**
|
|
1705
|
-
* Apply compiled inference rules to file attributes, returning merged metadata and optional rendered content.
|
|
1706
|
-
*
|
|
1707
|
-
* Rules are evaluated in order; later rules override earlier ones.
|
|
1708
|
-
* If a rule has a `map`, the JsonMap transformation is applied after `set` resolution,
|
|
1709
|
-
* and map output overrides set output on conflict.
|
|
1710
|
-
*
|
|
1711
|
-
* @param compiledRules - The compiled rules to evaluate.
|
|
1712
|
-
* @param attributes - The file attributes to match against.
|
|
1713
|
-
* @param namedMaps - Optional record of named JsonMap definitions.
|
|
1714
|
-
* @param logger - Optional logger for warnings (falls back to console.warn).
|
|
1715
|
-
* @param templateEngine - Optional template engine for rendering content templates.
|
|
1716
|
-
* @param configDir - Optional config directory for resolving .json map file paths.
|
|
1717
|
-
* @returns The merged metadata and optional rendered content.
|
|
1718
|
-
*/
|
|
1719
|
-
async function applyRules(compiledRules, attributes, namedMaps, logger, templateEngine, configDir) {
|
|
1720
|
-
// JsonMap's type definitions expect a generic JsonMapLib shape with unary functions.
|
|
1721
|
-
// Our helper functions accept multiple args, which JsonMap supports at runtime.
|
|
1722
|
-
const lib = createJsonMapLib();
|
|
1723
|
-
let merged = {};
|
|
1724
|
-
let renderedContent = null;
|
|
1725
|
-
const log = logger ?? console;
|
|
1726
|
-
for (const [ruleIndex, { rule, validate }] of compiledRules.entries()) {
|
|
1727
|
-
if (validate(attributes)) {
|
|
1728
|
-
// Apply set resolution
|
|
1729
|
-
const setOutput = resolveSet(rule.set, attributes);
|
|
1730
|
-
merged = { ...merged, ...setOutput };
|
|
1731
|
-
// Apply map transformation if present
|
|
1732
|
-
if (rule.map) {
|
|
1733
|
-
let mapDef;
|
|
1734
|
-
// Resolve map reference
|
|
1735
|
-
if (typeof rule.map === 'string') {
|
|
1736
|
-
if (rule.map.endsWith('.json') && configDir) {
|
|
1737
|
-
// File path: load from .json file
|
|
1738
|
-
try {
|
|
1739
|
-
const mapPath = node_path.resolve(configDir, rule.map);
|
|
1740
|
-
const raw = node_fs.readFileSync(mapPath, 'utf-8');
|
|
1741
|
-
mapDef = JSON.parse(raw);
|
|
1742
|
-
}
|
|
1743
|
-
catch (error) {
|
|
1744
|
-
log.warn(`Failed to load map file "${rule.map}": ${error instanceof Error ? error.message : String(error)}`);
|
|
1745
|
-
continue;
|
|
1746
|
-
}
|
|
1747
|
-
}
|
|
1748
|
-
else {
|
|
1749
|
-
mapDef = namedMaps?.[rule.map];
|
|
1750
|
-
if (!mapDef) {
|
|
1751
|
-
log.warn(`Map reference "${rule.map}" not found in named maps. Skipping map transformation.`);
|
|
1752
|
-
continue;
|
|
1753
|
-
}
|
|
1754
|
-
}
|
|
1755
|
-
}
|
|
1756
|
-
else {
|
|
1757
|
-
mapDef = rule.map;
|
|
1758
|
-
}
|
|
1759
|
-
// Execute JsonMap transformation
|
|
1760
|
-
try {
|
|
1761
|
-
const jsonMap = new jsonmap.JsonMap(mapDef, lib);
|
|
1762
|
-
const mapOutput = await jsonMap.transform(attributes);
|
|
1763
|
-
if (mapOutput &&
|
|
1764
|
-
typeof mapOutput === 'object' &&
|
|
1765
|
-
!Array.isArray(mapOutput)) {
|
|
1766
|
-
merged = { ...merged, ...mapOutput };
|
|
1767
|
-
}
|
|
1768
|
-
else {
|
|
1769
|
-
log.warn(`JsonMap transformation did not return an object; skipping merge.`);
|
|
1770
|
-
}
|
|
1771
|
-
}
|
|
1772
|
-
catch (error) {
|
|
1773
|
-
log.warn(`JsonMap transformation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
|
-
// Render template if present
|
|
1777
|
-
if (rule.template && templateEngine) {
|
|
1778
|
-
const templateKey = `rule-${String(ruleIndex)}`;
|
|
1779
|
-
// Build template context: attributes (with json spread at top) + map output
|
|
1780
|
-
const context = {
|
|
1781
|
-
...(attributes.json ?? {}),
|
|
1782
|
-
...attributes,
|
|
1783
|
-
...merged,
|
|
1784
|
-
};
|
|
1785
|
-
try {
|
|
1786
|
-
const result = templateEngine.render(templateKey, context);
|
|
1787
|
-
if (result && result.trim()) {
|
|
1788
|
-
renderedContent = result;
|
|
1789
|
-
}
|
|
1790
|
-
else {
|
|
1791
|
-
log.warn(`Template for rule ${String(ruleIndex)} rendered empty output. Falling back to raw content.`);
|
|
1792
|
-
}
|
|
1793
|
-
}
|
|
1794
|
-
catch (error) {
|
|
1795
|
-
log.warn(`Template render failed for rule ${String(ruleIndex)}: ${error instanceof Error ? error.message : String(error)}. Falling back to raw content.`);
|
|
1796
|
-
}
|
|
1797
|
-
}
|
|
1798
|
-
}
|
|
1799
|
-
}
|
|
1800
|
-
return { metadata: merged, renderedContent };
|
|
1801
|
-
}
|
|
1802
|
-
|
|
1803
1905
|
/**
|
|
1804
1906
|
* @module rules/attributes
|
|
1805
1907
|
* Builds file attribute objects for rule matching. Pure function: derives attributes from path, stats, and extracted data.
|
|
@@ -1890,14 +1992,14 @@
|
|
|
1890
1992
|
* @param configDir - Optional config directory for resolving file paths.
|
|
1891
1993
|
* @returns The merged metadata and intermediate data.
|
|
1892
1994
|
*/
|
|
1893
|
-
async function buildMergedMetadata(filePath, compiledRules, metadataDir, maps, logger, templateEngine, configDir) {
|
|
1995
|
+
async function buildMergedMetadata(filePath, compiledRules, metadataDir, maps, logger, templateEngine, configDir, customMapLib) {
|
|
1894
1996
|
const ext = node_path.extname(filePath);
|
|
1895
1997
|
const stats = await promises.stat(filePath);
|
|
1896
1998
|
// 1. Extract text and structured data
|
|
1897
1999
|
const extracted = await extractText(filePath, ext);
|
|
1898
2000
|
// 2. Build attributes + apply rules
|
|
1899
2001
|
const attributes = buildAttributes(filePath, stats, extracted.frontmatter, extracted.json);
|
|
1900
|
-
const { metadata: inferred, renderedContent } = await applyRules(compiledRules, attributes, maps, logger, templateEngine, configDir);
|
|
2002
|
+
const { metadata: inferred, renderedContent } = await applyRules(compiledRules, attributes, maps, logger, templateEngine, configDir, customMapLib);
|
|
1901
2003
|
// 3. Read enrichment metadata (merge, enrichment wins)
|
|
1902
2004
|
const enrichment = await readMetadata(filePath, metadataDir);
|
|
1903
2005
|
const metadata = {
|
|
@@ -2010,7 +2112,7 @@
|
|
|
2010
2112
|
try {
|
|
2011
2113
|
const ext = node_path.extname(filePath);
|
|
2012
2114
|
// 1. Build merged metadata + extract text
|
|
2013
|
-
const { metadata, extracted, renderedContent } = await buildMergedMetadata(filePath, this.compiledRules, this.config.metadataDir, this.config.maps, this.logger, this.templateEngine, this.config.configDir);
|
|
2115
|
+
const { metadata, extracted, renderedContent } = await buildMergedMetadata(filePath, this.compiledRules, this.config.metadataDir, this.config.maps, this.logger, this.templateEngine, this.config.configDir, this.config.customMapLib);
|
|
2014
2116
|
// Use rendered template content if available, otherwise raw extracted text
|
|
2015
2117
|
const textToEmbed = renderedContent ?? extracted.text;
|
|
2016
2118
|
if (!textToEmbed.trim()) {
|
|
@@ -2124,7 +2226,7 @@
|
|
|
2124
2226
|
return null;
|
|
2125
2227
|
}
|
|
2126
2228
|
// Build merged metadata (lightweight — no embedding)
|
|
2127
|
-
const { metadata } = await buildMergedMetadata(filePath, this.compiledRules, this.config.metadataDir, this.config.maps, this.logger, this.templateEngine, this.config.configDir);
|
|
2229
|
+
const { metadata } = await buildMergedMetadata(filePath, this.compiledRules, this.config.metadataDir, this.config.maps, this.logger, this.templateEngine, this.config.configDir, this.config.customMapLib);
|
|
2128
2230
|
// Update all chunk payloads
|
|
2129
2231
|
const totalChunks = getChunkCount(existingPayload);
|
|
2130
2232
|
const ids = chunkIds(filePath, totalChunks);
|
|
@@ -2138,21 +2240,20 @@
|
|
|
2138
2240
|
}
|
|
2139
2241
|
}
|
|
2140
2242
|
/**
|
|
2141
|
-
* Update compiled inference rules
|
|
2142
|
-
*
|
|
2143
|
-
* @param compiledRules - The newly compiled rules.
|
|
2144
|
-
*/
|
|
2145
|
-
/**
|
|
2146
|
-
* Update compiled inference rules and optionally the template engine.
|
|
2243
|
+
* Update compiled inference rules, template engine, and custom map lib.
|
|
2147
2244
|
*
|
|
2148
2245
|
* @param compiledRules - The newly compiled rules.
|
|
2149
2246
|
* @param templateEngine - Optional updated template engine.
|
|
2247
|
+
* @param customMapLib - Optional updated custom JsonMap lib functions.
|
|
2150
2248
|
*/
|
|
2151
|
-
updateRules(compiledRules, templateEngine) {
|
|
2249
|
+
updateRules(compiledRules, templateEngine, customMapLib) {
|
|
2152
2250
|
this.compiledRules = compiledRules;
|
|
2153
2251
|
if (templateEngine) {
|
|
2154
2252
|
this.templateEngine = templateEngine;
|
|
2155
2253
|
}
|
|
2254
|
+
if (customMapLib !== undefined) {
|
|
2255
|
+
this.config = { ...this.config, customMapLib };
|
|
2256
|
+
}
|
|
2156
2257
|
this.logger.info({ rules: compiledRules.length }, 'Inference rules updated');
|
|
2157
2258
|
}
|
|
2158
2259
|
}
|
|
@@ -3032,12 +3133,17 @@
|
|
|
3032
3133
|
const compiledRules = this.factories.compileRules(this.config.inferenceRules ?? []);
|
|
3033
3134
|
const configDir = this.configPath ? node_path.dirname(this.configPath) : '.';
|
|
3034
3135
|
const templateEngine = await buildTemplateEngine(this.config.inferenceRules ?? [], this.config.templates, this.config.templateHelpers?.paths, configDir);
|
|
3136
|
+
// Load custom JsonMap lib functions
|
|
3137
|
+
const customMapLib = this.config.mapHelpers?.paths?.length && configDir
|
|
3138
|
+
? await loadCustomMapHelpers(this.config.mapHelpers.paths, configDir)
|
|
3139
|
+
: undefined;
|
|
3035
3140
|
const processor = this.factories.createDocumentProcessor({
|
|
3036
3141
|
metadataDir: this.config.metadataDir ?? '.jeeves-metadata',
|
|
3037
3142
|
chunkSize: this.config.embedding.chunkSize,
|
|
3038
3143
|
chunkOverlap: this.config.embedding.chunkOverlap,
|
|
3039
3144
|
maps: this.config.maps,
|
|
3040
3145
|
configDir,
|
|
3146
|
+
customMapLib,
|
|
3041
3147
|
}, embeddingProvider, vectorStore, compiledRules, logger, templateEngine);
|
|
3042
3148
|
this.processor = processor;
|
|
3043
3149
|
this.queue = this.factories.createEventQueue({
|
|
@@ -3156,7 +3262,10 @@
|
|
|
3156
3262
|
const compiledRules = this.factories.compileRules(newConfig.inferenceRules ?? []);
|
|
3157
3263
|
const reloadConfigDir = node_path.dirname(this.configPath);
|
|
3158
3264
|
const newTemplateEngine = await buildTemplateEngine(newConfig.inferenceRules ?? [], newConfig.templates, newConfig.templateHelpers?.paths, reloadConfigDir);
|
|
3159
|
-
|
|
3265
|
+
const newCustomMapLib = newConfig.mapHelpers?.paths?.length && reloadConfigDir
|
|
3266
|
+
? await loadCustomMapHelpers(newConfig.mapHelpers.paths, reloadConfigDir)
|
|
3267
|
+
: undefined;
|
|
3268
|
+
processor.updateRules(compiledRules, newTemplateEngine, newCustomMapLib);
|
|
3160
3269
|
logger.info({ configPath: this.configPath, rules: compiledRules.length }, 'Config reloaded');
|
|
3161
3270
|
}
|
|
3162
3271
|
catch (error) {
|
|
@@ -3203,4 +3312,4 @@
|
|
|
3203
3312
|
exports.watchConfigSchema = watchConfigSchema;
|
|
3204
3313
|
exports.writeMetadata = writeMetadata;
|
|
3205
3314
|
|
|
3206
|
-
})(this["jeeves-watcher"] = this["jeeves-watcher"] || {}, Fastify, promises, node_path, picomatch, radash, node_crypto, node_fs, ignore, Handlebars, dayjs, hastUtilToMdast, mdastUtilFromAdf, mdastUtilToMarkdown, rehypeParse, unified, chokidar, cosmiconfig, zod,
|
|
3315
|
+
})(this["jeeves-watcher"] = this["jeeves-watcher"] || {}, Fastify, promises, node_path, picomatch, radash, node_crypto, node_fs, ignore, node_url, jsonmap, Handlebars, dayjs, hastUtilToMdast, mdastUtilFromAdf, mdastUtilToMarkdown, rehypeParse, unified, chokidar, cosmiconfig, zod, googleGenai, pino, uuid, cheerio, yaml, mammoth, Ajv, addFormats, textsplitters, jsClientRest);
|