@karmaniverous/jeeves-watcher 0.4.1 → 0.4.3
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 +47 -13
- package/dist/cjs/index.js +291 -170
- package/dist/cli/jeeves-watcher/index.js +292 -171
- package/dist/index.d.ts +11 -10
- package/dist/index.iife.js +292 -171
- package/dist/index.iife.min.js +1 -1
- package/dist/mjs/index.js +292 -171
- 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, 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(resolved));
|
|
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.
|
|
@@ -1135,11 +1380,11 @@
|
|
|
1135
1380
|
.array(inferenceRuleSchema)
|
|
1136
1381
|
.optional()
|
|
1137
1382
|
.describe('Rules for inferring metadata from file attributes.'),
|
|
1138
|
-
/** Reusable named JsonMap transformations. */
|
|
1383
|
+
/** Reusable named JsonMap transformations (inline objects or .json file paths). */
|
|
1139
1384
|
maps: zod.z
|
|
1140
|
-
.record(zod.z.string(), jsonmap.jsonMapMapSchema)
|
|
1385
|
+
.record(zod.z.string(), jsonmap.jsonMapMapSchema.or(zod.z.string()))
|
|
1141
1386
|
.optional()
|
|
1142
|
-
.describe('Reusable named JsonMap transformations.'),
|
|
1387
|
+
.describe('Reusable named JsonMap transformations (inline definition or .json file path resolved relative to config directory).'),
|
|
1143
1388
|
/** Reusable named Handlebars templates (inline strings or .hbs/.handlebars file paths). */
|
|
1144
1389
|
templates: zod.z
|
|
1145
1390
|
.record(zod.z.string(), zod.z.string())
|
|
@@ -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. */
|
|
@@ -1253,6 +1509,18 @@
|
|
|
1253
1509
|
}
|
|
1254
1510
|
try {
|
|
1255
1511
|
const validated = jeevesWatcherConfigSchema.parse(result.config);
|
|
1512
|
+
// Resolve file-path map references relative to config directory.
|
|
1513
|
+
// After this block, all map values are inline JsonMapMap objects.
|
|
1514
|
+
if (validated.maps) {
|
|
1515
|
+
const configDir = node_path.dirname(result.filepath);
|
|
1516
|
+
for (const [name, value] of Object.entries(validated.maps)) {
|
|
1517
|
+
if (typeof value === 'string') {
|
|
1518
|
+
const mapPath = node_path.resolve(configDir, value);
|
|
1519
|
+
const raw = node_fs.readFileSync(mapPath, 'utf-8');
|
|
1520
|
+
validated.maps[name] = JSON.parse(raw);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1256
1524
|
const withDefaults = applyDefaults(validated);
|
|
1257
1525
|
return substituteEnvVars(withDefaults);
|
|
1258
1526
|
}
|
|
@@ -1634,160 +1902,6 @@
|
|
|
1634
1902
|
return extractPlaintext(filePath);
|
|
1635
1903
|
}
|
|
1636
1904
|
|
|
1637
|
-
/**
|
|
1638
|
-
* @module rules/templates
|
|
1639
|
-
* Resolves template variables (`${path.to.value}`) in rule `set` objects against file attributes.
|
|
1640
|
-
*/
|
|
1641
|
-
/**
|
|
1642
|
-
* Resolve `${template.vars}` in a value against the given attributes.
|
|
1643
|
-
*
|
|
1644
|
-
* @param value - The value to resolve.
|
|
1645
|
-
* @param attributes - The file attributes for variable lookup.
|
|
1646
|
-
* @returns The resolved value.
|
|
1647
|
-
*/
|
|
1648
|
-
function resolveTemplateVars(value, attributes) {
|
|
1649
|
-
if (typeof value !== 'string')
|
|
1650
|
-
return value;
|
|
1651
|
-
return value.replace(/\$\{([^}]+)\}/g, (_match, varPath) => {
|
|
1652
|
-
const current = radash.get(attributes, varPath);
|
|
1653
|
-
if (current === null || current === undefined)
|
|
1654
|
-
return '';
|
|
1655
|
-
return typeof current === 'string' ? current : JSON.stringify(current);
|
|
1656
|
-
});
|
|
1657
|
-
}
|
|
1658
|
-
/**
|
|
1659
|
-
* Resolve all template variables in a `set` object.
|
|
1660
|
-
*
|
|
1661
|
-
* @param setObj - The key-value pairs to resolve.
|
|
1662
|
-
* @param attributes - The file attributes for variable lookup.
|
|
1663
|
-
* @returns The resolved key-value pairs.
|
|
1664
|
-
*/
|
|
1665
|
-
function resolveSet(setObj, attributes) {
|
|
1666
|
-
const result = {};
|
|
1667
|
-
for (const [key, value] of Object.entries(setObj)) {
|
|
1668
|
-
result[key] = resolveTemplateVars(value, attributes);
|
|
1669
|
-
}
|
|
1670
|
-
return result;
|
|
1671
|
-
}
|
|
1672
|
-
|
|
1673
|
-
/**
|
|
1674
|
-
* @module rules/apply
|
|
1675
|
-
* Applies compiled inference rules to file attributes, producing merged metadata via template resolution and JsonMap transforms.
|
|
1676
|
-
*/
|
|
1677
|
-
/**
|
|
1678
|
-
* Create the lib object for JsonMap transformations.
|
|
1679
|
-
*
|
|
1680
|
-
* @returns The lib object.
|
|
1681
|
-
*/
|
|
1682
|
-
function createJsonMapLib() {
|
|
1683
|
-
return {
|
|
1684
|
-
split: (str, separator) => str.split(separator),
|
|
1685
|
-
slice: (arr, start, end) => arr.slice(start, end),
|
|
1686
|
-
join: (arr, separator) => arr.join(separator),
|
|
1687
|
-
toLowerCase: (str) => str.toLowerCase(),
|
|
1688
|
-
replace: (str, search, replacement) => str.replace(search, replacement),
|
|
1689
|
-
get: (obj, path) => radash.get(obj, path),
|
|
1690
|
-
};
|
|
1691
|
-
}
|
|
1692
|
-
/**
|
|
1693
|
-
* Apply compiled inference rules to file attributes, returning merged metadata and optional rendered content.
|
|
1694
|
-
*
|
|
1695
|
-
* Rules are evaluated in order; later rules override earlier ones.
|
|
1696
|
-
* If a rule has a `map`, the JsonMap transformation is applied after `set` resolution,
|
|
1697
|
-
* and map output overrides set output on conflict.
|
|
1698
|
-
*
|
|
1699
|
-
* @param compiledRules - The compiled rules to evaluate.
|
|
1700
|
-
* @param attributes - The file attributes to match against.
|
|
1701
|
-
* @param namedMaps - Optional record of named JsonMap definitions.
|
|
1702
|
-
* @param logger - Optional logger for warnings (falls back to console.warn).
|
|
1703
|
-
* @param templateEngine - Optional template engine for rendering content templates.
|
|
1704
|
-
* @param configDir - Optional config directory for resolving .json map file paths.
|
|
1705
|
-
* @returns The merged metadata and optional rendered content.
|
|
1706
|
-
*/
|
|
1707
|
-
async function applyRules(compiledRules, attributes, namedMaps, logger, templateEngine, configDir) {
|
|
1708
|
-
// JsonMap's type definitions expect a generic JsonMapLib shape with unary functions.
|
|
1709
|
-
// Our helper functions accept multiple args, which JsonMap supports at runtime.
|
|
1710
|
-
const lib = createJsonMapLib();
|
|
1711
|
-
let merged = {};
|
|
1712
|
-
let renderedContent = null;
|
|
1713
|
-
const log = logger ?? console;
|
|
1714
|
-
for (const [ruleIndex, { rule, validate }] of compiledRules.entries()) {
|
|
1715
|
-
if (validate(attributes)) {
|
|
1716
|
-
// Apply set resolution
|
|
1717
|
-
const setOutput = resolveSet(rule.set, attributes);
|
|
1718
|
-
merged = { ...merged, ...setOutput };
|
|
1719
|
-
// Apply map transformation if present
|
|
1720
|
-
if (rule.map) {
|
|
1721
|
-
let mapDef;
|
|
1722
|
-
// Resolve map reference
|
|
1723
|
-
if (typeof rule.map === 'string') {
|
|
1724
|
-
if (rule.map.endsWith('.json') && configDir) {
|
|
1725
|
-
// File path: load from .json file
|
|
1726
|
-
try {
|
|
1727
|
-
const mapPath = node_path.resolve(configDir, rule.map);
|
|
1728
|
-
const raw = node_fs.readFileSync(mapPath, 'utf-8');
|
|
1729
|
-
mapDef = JSON.parse(raw);
|
|
1730
|
-
}
|
|
1731
|
-
catch (error) {
|
|
1732
|
-
log.warn(`Failed to load map file "${rule.map}": ${error instanceof Error ? error.message : String(error)}`);
|
|
1733
|
-
continue;
|
|
1734
|
-
}
|
|
1735
|
-
}
|
|
1736
|
-
else {
|
|
1737
|
-
mapDef = namedMaps?.[rule.map];
|
|
1738
|
-
if (!mapDef) {
|
|
1739
|
-
log.warn(`Map reference "${rule.map}" not found in named maps. Skipping map transformation.`);
|
|
1740
|
-
continue;
|
|
1741
|
-
}
|
|
1742
|
-
}
|
|
1743
|
-
}
|
|
1744
|
-
else {
|
|
1745
|
-
mapDef = rule.map;
|
|
1746
|
-
}
|
|
1747
|
-
// Execute JsonMap transformation
|
|
1748
|
-
try {
|
|
1749
|
-
const jsonMap = new jsonmap.JsonMap(mapDef, lib);
|
|
1750
|
-
const mapOutput = await jsonMap.transform(attributes);
|
|
1751
|
-
if (mapOutput &&
|
|
1752
|
-
typeof mapOutput === 'object' &&
|
|
1753
|
-
!Array.isArray(mapOutput)) {
|
|
1754
|
-
merged = { ...merged, ...mapOutput };
|
|
1755
|
-
}
|
|
1756
|
-
else {
|
|
1757
|
-
log.warn(`JsonMap transformation did not return an object; skipping merge.`);
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
catch (error) {
|
|
1761
|
-
log.warn(`JsonMap transformation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1762
|
-
}
|
|
1763
|
-
}
|
|
1764
|
-
// Render template if present
|
|
1765
|
-
if (rule.template && templateEngine) {
|
|
1766
|
-
const templateKey = `rule-${String(ruleIndex)}`;
|
|
1767
|
-
// Build template context: attributes (with json spread at top) + map output
|
|
1768
|
-
const context = {
|
|
1769
|
-
...(attributes.json ?? {}),
|
|
1770
|
-
...attributes,
|
|
1771
|
-
...merged,
|
|
1772
|
-
};
|
|
1773
|
-
try {
|
|
1774
|
-
const result = templateEngine.render(templateKey, context);
|
|
1775
|
-
if (result && result.trim()) {
|
|
1776
|
-
renderedContent = result;
|
|
1777
|
-
}
|
|
1778
|
-
else {
|
|
1779
|
-
log.warn(`Template for rule ${String(ruleIndex)} rendered empty output. Falling back to raw content.`);
|
|
1780
|
-
}
|
|
1781
|
-
}
|
|
1782
|
-
catch (error) {
|
|
1783
|
-
log.warn(`Template render failed for rule ${String(ruleIndex)}: ${error instanceof Error ? error.message : String(error)}. Falling back to raw content.`);
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1787
|
-
}
|
|
1788
|
-
return { metadata: merged, renderedContent };
|
|
1789
|
-
}
|
|
1790
|
-
|
|
1791
1905
|
/**
|
|
1792
1906
|
* @module rules/attributes
|
|
1793
1907
|
* Builds file attribute objects for rule matching. Pure function: derives attributes from path, stats, and extracted data.
|
|
@@ -1878,14 +1992,14 @@
|
|
|
1878
1992
|
* @param configDir - Optional config directory for resolving file paths.
|
|
1879
1993
|
* @returns The merged metadata and intermediate data.
|
|
1880
1994
|
*/
|
|
1881
|
-
async function buildMergedMetadata(filePath, compiledRules, metadataDir, maps, logger, templateEngine, configDir) {
|
|
1995
|
+
async function buildMergedMetadata(filePath, compiledRules, metadataDir, maps, logger, templateEngine, configDir, customMapLib) {
|
|
1882
1996
|
const ext = node_path.extname(filePath);
|
|
1883
1997
|
const stats = await promises.stat(filePath);
|
|
1884
1998
|
// 1. Extract text and structured data
|
|
1885
1999
|
const extracted = await extractText(filePath, ext);
|
|
1886
2000
|
// 2. Build attributes + apply rules
|
|
1887
2001
|
const attributes = buildAttributes(filePath, stats, extracted.frontmatter, extracted.json);
|
|
1888
|
-
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);
|
|
1889
2003
|
// 3. Read enrichment metadata (merge, enrichment wins)
|
|
1890
2004
|
const enrichment = await readMetadata(filePath, metadataDir);
|
|
1891
2005
|
const metadata = {
|
|
@@ -1998,7 +2112,7 @@
|
|
|
1998
2112
|
try {
|
|
1999
2113
|
const ext = node_path.extname(filePath);
|
|
2000
2114
|
// 1. Build merged metadata + extract text
|
|
2001
|
-
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);
|
|
2002
2116
|
// Use rendered template content if available, otherwise raw extracted text
|
|
2003
2117
|
const textToEmbed = renderedContent ?? extracted.text;
|
|
2004
2118
|
if (!textToEmbed.trim()) {
|
|
@@ -2112,7 +2226,7 @@
|
|
|
2112
2226
|
return null;
|
|
2113
2227
|
}
|
|
2114
2228
|
// Build merged metadata (lightweight — no embedding)
|
|
2115
|
-
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);
|
|
2116
2230
|
// Update all chunk payloads
|
|
2117
2231
|
const totalChunks = getChunkCount(existingPayload);
|
|
2118
2232
|
const ids = chunkIds(filePath, totalChunks);
|
|
@@ -2126,21 +2240,20 @@
|
|
|
2126
2240
|
}
|
|
2127
2241
|
}
|
|
2128
2242
|
/**
|
|
2129
|
-
* Update compiled inference rules
|
|
2130
|
-
*
|
|
2131
|
-
* @param compiledRules - The newly compiled rules.
|
|
2132
|
-
*/
|
|
2133
|
-
/**
|
|
2134
|
-
* Update compiled inference rules and optionally the template engine.
|
|
2243
|
+
* Update compiled inference rules, template engine, and custom map lib.
|
|
2135
2244
|
*
|
|
2136
2245
|
* @param compiledRules - The newly compiled rules.
|
|
2137
2246
|
* @param templateEngine - Optional updated template engine.
|
|
2247
|
+
* @param customMapLib - Optional updated custom JsonMap lib functions.
|
|
2138
2248
|
*/
|
|
2139
|
-
updateRules(compiledRules, templateEngine) {
|
|
2249
|
+
updateRules(compiledRules, templateEngine, customMapLib) {
|
|
2140
2250
|
this.compiledRules = compiledRules;
|
|
2141
2251
|
if (templateEngine) {
|
|
2142
2252
|
this.templateEngine = templateEngine;
|
|
2143
2253
|
}
|
|
2254
|
+
if (customMapLib !== undefined) {
|
|
2255
|
+
this.config = { ...this.config, customMapLib };
|
|
2256
|
+
}
|
|
2144
2257
|
this.logger.info({ rules: compiledRules.length }, 'Inference rules updated');
|
|
2145
2258
|
}
|
|
2146
2259
|
}
|
|
@@ -3020,12 +3133,17 @@
|
|
|
3020
3133
|
const compiledRules = this.factories.compileRules(this.config.inferenceRules ?? []);
|
|
3021
3134
|
const configDir = this.configPath ? node_path.dirname(this.configPath) : '.';
|
|
3022
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;
|
|
3023
3140
|
const processor = this.factories.createDocumentProcessor({
|
|
3024
3141
|
metadataDir: this.config.metadataDir ?? '.jeeves-metadata',
|
|
3025
3142
|
chunkSize: this.config.embedding.chunkSize,
|
|
3026
3143
|
chunkOverlap: this.config.embedding.chunkOverlap,
|
|
3027
3144
|
maps: this.config.maps,
|
|
3028
3145
|
configDir,
|
|
3146
|
+
customMapLib,
|
|
3029
3147
|
}, embeddingProvider, vectorStore, compiledRules, logger, templateEngine);
|
|
3030
3148
|
this.processor = processor;
|
|
3031
3149
|
this.queue = this.factories.createEventQueue({
|
|
@@ -3144,7 +3262,10 @@
|
|
|
3144
3262
|
const compiledRules = this.factories.compileRules(newConfig.inferenceRules ?? []);
|
|
3145
3263
|
const reloadConfigDir = node_path.dirname(this.configPath);
|
|
3146
3264
|
const newTemplateEngine = await buildTemplateEngine(newConfig.inferenceRules ?? [], newConfig.templates, newConfig.templateHelpers?.paths, reloadConfigDir);
|
|
3147
|
-
|
|
3265
|
+
const newCustomMapLib = newConfig.mapHelpers?.paths?.length && reloadConfigDir
|
|
3266
|
+
? await loadCustomMapHelpers(newConfig.mapHelpers.paths, reloadConfigDir)
|
|
3267
|
+
: undefined;
|
|
3268
|
+
processor.updateRules(compiledRules, newTemplateEngine, newCustomMapLib);
|
|
3148
3269
|
logger.info({ configPath: this.configPath, rules: compiledRules.length }, 'Config reloaded');
|
|
3149
3270
|
}
|
|
3150
3271
|
catch (error) {
|
|
@@ -3191,4 +3312,4 @@
|
|
|
3191
3312
|
exports.watchConfigSchema = watchConfigSchema;
|
|
3192
3313
|
exports.writeMetadata = writeMetadata;
|
|
3193
3314
|
|
|
3194
|
-
})(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, jsonmap, Handlebars, dayjs, hastUtilToMdast, mdastUtilFromAdf, mdastUtilToMarkdown, rehypeParse, unified, chokidar, cosmiconfig, zod, googleGenai, pino, uuid, cheerio, yaml, mammoth, Ajv, addFormats, textsplitters, jsClientRest);
|