@crewhaus/tool-text 0.7.0 → 0.8.1
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/LICENSE +201 -0
- package/NOTICE +9 -0
- package/README.md +14 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +94 -35
- package/dist/lib/entities.d.ts +2 -0
- package/dist/lib/entities.js +71 -9
- package/dist/lib/markdown.d.ts +18 -0
- package/dist/lib/markdown.js +35 -4
- package/dist/lib/normalize.d.ts +20 -1
- package/dist/lib/normalize.js +181 -17
- package/dist/lib/regex.d.ts +54 -7
- package/dist/lib/regex.js +116 -27
- package/dist/lib/similarity.d.ts +20 -1
- package/dist/lib/similarity.js +67 -15
- package/dist/lib/template.d.ts +23 -2
- package/dist/lib/template.js +36 -7
- package/package.json +11 -6
package/dist/lib/template.d.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
import type { PatternAnswer } from "./regex";
|
|
1
2
|
export declare class TemplateError extends Error {
|
|
2
3
|
readonly name = "TemplateError";
|
|
3
4
|
}
|
|
4
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Walk a dotted path (`user.name`, `items.0.id`) through plain data. Only
|
|
7
|
+
* the data's own fields are read: `{{constructor}}` on data with no such
|
|
8
|
+
* field is missing, where 0.7.0 rendered Object's constructor as
|
|
9
|
+
* "function Object() { [native code] }" and strict mode let it through.
|
|
10
|
+
*/
|
|
5
11
|
export declare function lookupPath(data: unknown, path: string): unknown;
|
|
6
12
|
/**
|
|
7
13
|
* Render `{{path}}` placeholders against `data`.
|
|
@@ -31,6 +37,16 @@ export type ClassifyResult = {
|
|
|
31
37
|
readonly score: number;
|
|
32
38
|
readonly matched: ReadonlyArray<string>;
|
|
33
39
|
readonly scores: Readonly<Record<string, number>>;
|
|
40
|
+
/**
|
|
41
|
+
* Regex patterns with no answer (the worker's deadline, the engine giving
|
|
42
|
+
* up). When any is listed, `label` is null: a pattern that might have
|
|
43
|
+
* matched could change the winner, so neither the top label nor the
|
|
44
|
+
* default is given.
|
|
45
|
+
*/
|
|
46
|
+
readonly undetermined?: ReadonlyArray<{
|
|
47
|
+
readonly pattern: string;
|
|
48
|
+
readonly reason: string;
|
|
49
|
+
}>;
|
|
34
50
|
};
|
|
35
51
|
/**
|
|
36
52
|
* Score text against operator-written rules and return the best label.
|
|
@@ -38,5 +54,10 @@ export type ClassifyResult = {
|
|
|
38
54
|
* Lexical only — phrase and regex matching with per-rule weights. No model
|
|
39
55
|
* and no embedding, so the same message always gets the same label, which is
|
|
40
56
|
* what makes it safe to route on.
|
|
57
|
+
*
|
|
58
|
+
* A regex rule's pattern is never run here: `regexAnswers` holds its answer
|
|
59
|
+
* on this text, worked out in the regex worker beforehand (the tool does
|
|
60
|
+
* that with `answerPatterns`). A pattern with no definite answer leaves the
|
|
61
|
+
* label undetermined.
|
|
41
62
|
*/
|
|
42
|
-
export declare function classifyByRules(text: string, rules: ReadonlyArray<ClassifyRule>, threshold: number, defaultLabel: string | null): ClassifyResult;
|
|
63
|
+
export declare function classifyByRules(text: string, rules: ReadonlyArray<ClassifyRule>, threshold: number, defaultLabel: string | null, regexAnswers?: ReadonlyMap<string, PatternAnswer>): ClassifyResult;
|
package/dist/lib/template.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
export class TemplateError extends Error {
|
|
2
2
|
name = "TemplateError";
|
|
3
3
|
}
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Walk a dotted path (`user.name`, `items.0.id`) through plain data. Only
|
|
6
|
+
* the data's own fields are read: `{{constructor}}` on data with no such
|
|
7
|
+
* field is missing, where 0.7.0 rendered Object's constructor as
|
|
8
|
+
* "function Object() { [native code] }" and strict mode let it through.
|
|
9
|
+
*/
|
|
5
10
|
export function lookupPath(data, path) {
|
|
6
11
|
if (path === ".")
|
|
7
12
|
return data;
|
|
@@ -16,7 +21,7 @@ export function lookupPath(data, path) {
|
|
|
16
21
|
cur = cur[i];
|
|
17
22
|
continue;
|
|
18
23
|
}
|
|
19
|
-
if (typeof cur !== "object")
|
|
24
|
+
if (typeof cur !== "object" || !Object.hasOwn(cur, part))
|
|
20
25
|
return undefined;
|
|
21
26
|
cur = cur[part];
|
|
22
27
|
}
|
|
@@ -59,18 +64,38 @@ export function renderTemplateString(template, data, strict) {
|
|
|
59
64
|
* Lexical only — phrase and regex matching with per-rule weights. No model
|
|
60
65
|
* and no embedding, so the same message always gets the same label, which is
|
|
61
66
|
* what makes it safe to route on.
|
|
67
|
+
*
|
|
68
|
+
* A regex rule's pattern is never run here: `regexAnswers` holds its answer
|
|
69
|
+
* on this text, worked out in the regex worker beforehand (the tool does
|
|
70
|
+
* that with `answerPatterns`). A pattern with no definite answer leaves the
|
|
71
|
+
* label undetermined.
|
|
62
72
|
*/
|
|
63
|
-
export function classifyByRules(text, rules, threshold, defaultLabel) {
|
|
73
|
+
export function classifyByRules(text, rules, threshold, defaultLabel, regexAnswers = new Map()) {
|
|
64
74
|
const haystack = text.toLowerCase();
|
|
65
|
-
|
|
75
|
+
// Labels are the caller's text: a label named `constructor` or
|
|
76
|
+
// `__proto__` is a label, never an Object.prototype member.
|
|
77
|
+
const scores = Object.create(null);
|
|
66
78
|
const matched = [];
|
|
79
|
+
const undetermined = [];
|
|
67
80
|
for (const rule of rules) {
|
|
68
81
|
const weight = rule.weight ?? 1;
|
|
69
82
|
let hits = 0;
|
|
70
83
|
for (const pattern of rule.patterns) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
84
|
+
let found;
|
|
85
|
+
if (rule.regex === true) {
|
|
86
|
+
const answer = regexAnswers.get(pattern);
|
|
87
|
+
if (typeof answer !== "boolean") {
|
|
88
|
+
undetermined.push({
|
|
89
|
+
pattern,
|
|
90
|
+
reason: answer?.undetermined ?? "the pattern was not answered before classifying",
|
|
91
|
+
});
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
found = answer;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
found = haystack.includes(pattern.toLowerCase());
|
|
98
|
+
}
|
|
74
99
|
if (found) {
|
|
75
100
|
hits += 1;
|
|
76
101
|
matched.push(pattern);
|
|
@@ -79,6 +104,10 @@ export function classifyByRules(text, rules, threshold, defaultLabel) {
|
|
|
79
104
|
if (hits > 0)
|
|
80
105
|
scores[rule.label] = (scores[rule.label] ?? 0) + hits * weight;
|
|
81
106
|
}
|
|
107
|
+
if (undetermined.length > 0) {
|
|
108
|
+
const best = Math.max(0, ...Object.values(scores));
|
|
109
|
+
return { label: null, score: best, matched, scores, undetermined };
|
|
110
|
+
}
|
|
82
111
|
const ranked = Object.entries(scores).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
|
|
83
112
|
const top = ranked[0];
|
|
84
113
|
if (top === undefined || top[1] < threshold) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crewhaus/tool-text",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Deterministic text tools: regex extraction, diffing, unified-diff parsing, budget truncation, log compaction, outlining, templating, rule classification, normalization, similarity, and escaping",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,16 +8,18 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.js"
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
12
13
|
}
|
|
13
14
|
},
|
|
14
15
|
"scripts": {
|
|
15
16
|
"test": "bun test src"
|
|
16
17
|
},
|
|
17
18
|
"dependencies": {
|
|
18
|
-
"@crewhaus/errors": "0.
|
|
19
|
-
"@crewhaus/tool-builder": "0.
|
|
20
|
-
"@crewhaus/tool-catalog": "0.
|
|
19
|
+
"@crewhaus/errors": "0.8.1",
|
|
20
|
+
"@crewhaus/tool-builder": "0.8.1",
|
|
21
|
+
"@crewhaus/tool-catalog": "0.8.1",
|
|
22
|
+
"@crewhaus/tool-safety": "0.8.1",
|
|
21
23
|
"zod": "^3.23.8"
|
|
22
24
|
},
|
|
23
25
|
"devDependencies": {
|
|
@@ -41,5 +43,8 @@
|
|
|
41
43
|
"publishConfig": {
|
|
42
44
|
"access": "public"
|
|
43
45
|
},
|
|
44
|
-
"files": ["dist", "README.md", "LICENSE", "NOTICE"]
|
|
46
|
+
"files": ["dist", "README.md", "LICENSE", "NOTICE"],
|
|
47
|
+
"engines": {
|
|
48
|
+
"bun": ">=1.3.14"
|
|
49
|
+
}
|
|
45
50
|
}
|