@intentic/iq-engine 1.326.0 → 1.327.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 +21 -5
- package/dist/engines/catalog.d.ts +40 -0
- package/dist/engines/catalog.d.ts.map +1 -0
- package/dist/engines/catalog.js +293 -0
- package/dist/engines/catalog.js.map +1 -0
- package/dist/engines/routes.d.ts +32 -0
- package/dist/engines/routes.d.ts.map +1 -0
- package/dist/engines/routes.js +387 -0
- package/dist/engines/routes.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/render/text.d.ts +1 -0
- package/dist/render/text.d.ts.map +1 -1
- package/dist/render/text.js +2 -1
- package/dist/render/text.js.map +1 -1
- package/dist/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/verbs/dispatch.d.ts +1 -9
- package/dist/verbs/dispatch.d.ts.map +1 -1
- package/dist/verbs/dispatch.js +103 -37
- package/dist/verbs/dispatch.js.map +1 -1
- package/dist/verbs/literal.d.ts +29 -0
- package/dist/verbs/literal.d.ts.map +1 -0
- package/dist/verbs/literal.js +246 -0
- package/dist/verbs/literal.js.map +1 -0
- package/dist/verbs/route.d.ts +19 -0
- package/dist/verbs/route.d.ts.map +1 -0
- package/dist/verbs/route.js +227 -0
- package/dist/verbs/route.js.map +1 -0
- package/dist/verbs/siblings.d.ts +4 -0
- package/dist/verbs/siblings.d.ts.map +1 -0
- package/dist/verbs/siblings.js +31 -0
- package/dist/verbs/siblings.js.map +1 -0
- package/dist/verbs/verdict.d.ts +31 -0
- package/dist/verbs/verdict.d.ts.map +1 -0
- package/dist/verbs/verdict.js +34 -0
- package/dist/verbs/verdict.js.map +1 -0
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -4,7 +4,9 @@ The search engine behind `iq`: it indexes a workspace into SQLite and answers ea
|
|
|
4
4
|
|
|
5
5
|
```mermaid
|
|
6
6
|
flowchart LR
|
|
7
|
-
query["query<br/>iq CLI · daemon"] --> classify["classify<br/>path · symbol · regex · prose"]
|
|
7
|
+
query["query<br/>iq CLI · daemon"] --> classify["classify<br/>route · path · symbol · regex · prose"]
|
|
8
|
+
classify -- "/address" --> routes["routes<br/>router tables → view"]
|
|
9
|
+
classify -- "prose" --> literal["literal first<br/>ripgrep · translation catalogs"]
|
|
8
10
|
classify --> lexical["lexical<br/>ripgrep · BM25"]
|
|
9
11
|
classify --> structural["structural<br/>symbols · ast-grep"]
|
|
10
12
|
classify --> semantic["semantic<br/>embeddings"]
|
|
@@ -12,15 +14,25 @@ flowchart LR
|
|
|
12
14
|
structural --> fuse
|
|
13
15
|
semantic --> fuse
|
|
14
16
|
query -- "recent · who · hotspots · impact" --> git["git<br/>log · blame · churn"]
|
|
15
|
-
|
|
17
|
+
literal -- "leads" --> anchors["anchors<br/>capsule within budget"]
|
|
18
|
+
routes --> anchors
|
|
19
|
+
fuse --> anchors
|
|
16
20
|
git --> anchors
|
|
17
21
|
```
|
|
18
22
|
|
|
19
23
|
- The index is one SQLite database in `.intentic/local/cache/iq`: files, symbols, imports, complexity, chunks with FTS5, and quantized vectors in sqlite-vec. A worker thread writes it; exact verbs (`find`, `refs`) run ripgrep live so they are never stale.
|
|
20
|
-
- Prose queries run BM25 plus an RM3-expanded second pass, semantic vectors, reciprocal-rank fusion, then a cross-encoder rerank
|
|
24
|
+
- Prose queries run BM25 plus an RM3-expanded second pass, semantic vectors, reciprocal-rank fusion, then a cross-encoder rerank. A query that matches nothing exactly escalates to this path. The answer line anchors the passage that put its file first in the blended order, so the `[rerank]` it prints is that file's own score.
|
|
25
|
+
- Literal first (`src/verbs/literal.ts`): beside the semantic run, a prose query of three words or more (or two written as copy, sentence-cased or closed like a sentence, or any quoted span) is matched literally, and its hits lead with the semantic answer following. Agents open a third of their sessions on a screenshot and search its text, and a bare query used to read that text as a question (2026-10-06: 0 of 6 random `en.json` strings found). rg looks for the words in order, any whitespace and either apostrophe between them, case-insensitive. Hits in source always lead; hits only in tests or docs lead only for text written as copy, so search words a doc happens to quote do not; more than eight files is common wording and does not lead. A question ("where is…", "how does…") is matched only against whole translation strings: a doc restating it is not its answer.
|
|
26
|
+
- Translation catalogs (`src/engines/catalog.ts`) are any JSON under an `i18n`, `locales`, `lang`, `translations` or `messages` folder. Each is parsed with the line and full dotted key of every string, and UI text is matched against them with case, whitespace and punctuation ignored, interpolation slots (`{name}`, `{{name}}`, `%{name}`, `%s`) standing for whatever the screenshot shows, and vue-i18n's `a | b` plural forms one by one. Part of a string matches when the query is most of it or five words of it. A template vouches only through its own words (two at least, and more than half the query's), or `{count} chat` would fit any sentence ending in "chat".
|
|
27
|
+
- The second hop: a matched key is looked up in code as a quoted string (`t('a.b.c')`, `$t("…")`, `` t(`…`) ``, `keypath="…"`), and its call sites lead the answer, the component before the English catalog line before the other locales. A key with no whole use points at the template it is built from (`` t(`a.b.${kind}`) ``). One `key:` line per matched key says where it sits, how many locales hold it, and where it is used; `iq find` adds the same line when its matches land in a catalog. Equally exact matches (one sentence under three keys) are ordered by where the semantic run ranked their files or folders.
|
|
28
|
+
- Verdict: `confident` for a single key, or a single file writing the text, that the query occurs in as typed; `ambiguous` for several, or for a catalog string the query only contains.
|
|
29
|
+
- Routes (`src/verbs/route.ts`, `src/engines/routes.ts`): a query that is an address (`/agents`, `/sandbox/agent?section=tools`) is resolved against the route tables in files that build a router (vue-router, react-router's data routers and flat `<Route>` elements, Angular's `Routes`), found by one rg pass and read by a small scanner that joins nested child paths. The most specific match wins (most literal segments, a catch-all never counts) and loads a view; each parameter and query value is then followed into it: the line that tests for it (`slug === 'agent'`, `case "tools":`, a lookup table) and the component that branch mounts, descending when it mounts one. The answer is the screen the address ends on, then the declaration, the branches and the nav table entry naming the value; `route:` and one `name=value:` line per step say how. An address no route declares falls back to a path search. A test's stub router (`{ path, component: page }`) does not count.
|
|
30
|
+
- Siblings (`src/verbs/siblings.ts`): for `q` and `find`, a `siblings:` line names the other source and test files in the answer's folder, those iq ranked first, then those whose name holds a query word, six at most. Mined 2026-10-06: the first query of a session named the file the session went on to edit 20% of the time, but its folder 58%. Replayed over 361 transcript queries, when a session edited a neighbour of iq's answer the line named it 56 times in 76.
|
|
31
|
+
- The verdict (`src/verbs/verdict.ts`) is `confident` only when the top file's best passage scores at least `CONFIDENT_FLOOR` (0.9) and leads the runner-up file's by `CONFIDENCE_MARGIN` (0.1): a lead alone says which file is ahead, not whether it answers, and the old rule (any lead of 0.05) called 37% of mined transcript answers confident, 11% of them under a rerank of 0.2. On iq-bench's `full` run it picks 15 of 67 reranked answerable cases and is right on 14 (93%), where the old rule picked 33 at 76%; [iq-bench](../iq-bench)'s "Verdicts" section has the sweep. A query naming an identifier the index defines is never weak. An identifier query answered by its one definition is `confident`, by several `ambiguous`.
|
|
21
32
|
- Natural-language answers prefer implementation. A class prior (src 1, config 0.8, tests 0.6, docs 0.5, and 0.3 for generated files: lockfiles, minified bundles, snapshots, `.svg`, `.env` templates) weights fusion and, again, the cross-encoder blend. The cross-encoder is trained on web passages and scores prose that restates a question (docs, changelogs, test names) above the code that answers it, so a prior applied only before it was undone. The `spread` stage lets at most four of one file's best hits into the 32-passage rerank pool, so one file matching on many lines cannot crowd out the files after it.
|
|
22
33
|
- 2026-10-01: tuned on iq-bench's click, hono and intentic queries, then checked on SigMap's corpora (264 queries over 12 repos, from `refs/sigmap`) held out from tuning. Over all 369 answerable queries, MRR@10 rose from 0.520 to 0.605 and hit@1 from 42.5% to 51.2% (90 queries better, 10 worse, sign p≈3e-17); every corpus improved, the held-out ones included. Symbol and lexical verbs are unchanged. Filtering with `--only src` reached a slightly higher hit@5 (75.3% against 72.6%) but a lower hit@1 and MRR, and it hides tests and docs outright, which is why this is a prior and not a filter. On the no-answer slice, `weak` caught 4 of 12 instead of 5, with the same 2 false alarms in 70 answerable queries.
|
|
23
|
-
- `weak` overrides both when no reranked passage reaches `WEAK_FLOOR
|
|
34
|
+
- `weak` overrides both when no reranked passage reaches `WEAK_FLOOR` (0.05), a cross-encoder probability, and adds a hint to stop or rephrase once. Every other score in the pipeline is relative to the best hit, so something always leads; this is the one absolute test, calibrated on [iq-bench](../iq-bench)'s no-answer slice. Without a reranker no answer is ever called weak.
|
|
35
|
+
- 2026-10-06: raised from 0.005. It catches 8 of 12 no-answer cases instead of 5 and calls 3 of 76 answerable ones weak instead of 2. Over 393 replayed transcript queries it fires on 9 instead of none, and none of those sessions went on to open the file iq answered with.
|
|
24
36
|
- The embedder and reranker are ONNX models baked at image build time by `scripts/fetch-model.mjs`. Without them search degrades to BM25 and ripgrep; it never downloads at runtime.
|
|
25
37
|
- Embeddings are bge-small-en-v1.5 (q8): an attention-masked mean over the token states, L2-normalised. Queries carry bge's retrieval instruction ("Represent this sentence for searching relevant passages: "); passages never do. `VECTOR_SPACE` names what a stored vector depends on (model, pooling, normalisation), and both the index and the vector-cache sidecar are keyed by it: changing it empties both stores once and re-embeds every chunk, so vectors from two spaces never mix. The mean config keeps the bare model id as its value, the key existing caches already carry.
|
|
26
38
|
- 2026-09-29: kept mean pooling over bge's reference [CLS] pooling. On iq-bench's 58 natural-language cases (hono, click, intentic), nDCG@10 for mean vs CLS was 0.716 vs 0.699 in the full pipeline (CLS better on 6 cases, worse on 4, sign p 0.75) and 0.610 vs 0.600 for the vector leg alone (12 better, 14 worse). That is a tie, and switching would force every index through a full re-embed (about 80 minutes for this repository's 138k chunks) for no measured gain. Revisit if a larger bench shows a real difference. Dropping the query instruction lowered nDCG under both poolings (vector leg with mean pooling 0.610 to 0.582).
|
|
@@ -33,10 +45,14 @@ flowchart LR
|
|
|
33
45
|
|
|
34
46
|
- [src/index.ts](src/index.ts) — `createEngine` and `createResidentEngine`.
|
|
35
47
|
- [src/verbs/dispatch.ts](src/verbs/dispatch.ts) — the plan each verb runs, including the prose pipeline.
|
|
48
|
+
- [src/verbs/verdict.ts](src/verbs/verdict.ts) — the confident/ambiguous/weak rule and its calibration.
|
|
49
|
+
- [src/verbs/literal.ts](src/verbs/literal.ts) and [src/engines/catalog.ts](src/engines/catalog.ts) — literal first, and UI text to translation key to call site.
|
|
50
|
+
- [src/verbs/route.ts](src/verbs/route.ts) and [src/engines/routes.ts](src/engines/routes.ts) — an address to its route, view and section.
|
|
51
|
+
- [src/verbs/siblings.ts](src/verbs/siblings.ts) — the answer's neighbours.
|
|
36
52
|
- [src/plan/fuse.ts](src/plan/fuse.ts) — reciprocal-rank fusion, boosts and grouping by file.
|
|
37
53
|
- [src/plan/prior.ts](src/plan/prior.ts) — the class prior natural-language answers rank by.
|
|
38
54
|
- [src/verify/check.ts](src/verify/check.ts) — what `iq verify` judges, and how it renders.
|
|
39
|
-
- [src/render/text.ts](src/render/text.ts) — the capsule and the body under a hard token budget.
|
|
55
|
+
- [src/render/text.ts](src/render/text.ts) — the capsule (`answer:`, the `key:`/`route:`/`siblings:` lines under it, `candidates:`, `more:`) and the body under a hard token budget.
|
|
40
56
|
- [src/store/db.ts](src/store/db.ts) — the index schema and its rebuild-on-mismatch version.
|
|
41
57
|
- [src/workspace/floor.ts](src/workspace/floor.ts) — what no query can ever surface.
|
|
42
58
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { FileEntry } from "../types.js";
|
|
2
|
+
export interface CatalogEntry {
|
|
3
|
+
readonly key: string;
|
|
4
|
+
readonly line: number;
|
|
5
|
+
readonly value: string;
|
|
6
|
+
}
|
|
7
|
+
export interface Catalog {
|
|
8
|
+
readonly path: string;
|
|
9
|
+
readonly locale: string | undefined;
|
|
10
|
+
readonly entries: readonly CatalogEntry[];
|
|
11
|
+
}
|
|
12
|
+
export declare const isCatalogPath: (path: string) => boolean;
|
|
13
|
+
export declare const catalogHome: (path: string) => string;
|
|
14
|
+
export declare const localeOf: (path: string) => string | undefined;
|
|
15
|
+
export declare const isEnglish: (locale: string | undefined) => boolean;
|
|
16
|
+
export declare const parseCatalog: (text: string) => CatalogEntry[];
|
|
17
|
+
export declare const loadCatalogs: (entries: readonly FileEntry[]) => Promise<Catalog[]>;
|
|
18
|
+
export declare const normalizeText: (text: string) => string;
|
|
19
|
+
export declare const MIN_PARTIAL_WORDS = 3;
|
|
20
|
+
export interface MatchOptions {
|
|
21
|
+
readonly wholeOnly?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface EntryMatch {
|
|
24
|
+
readonly score: number;
|
|
25
|
+
readonly verbatim: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare const entryMatch: (entry: CatalogEntry, normalizedQuery: string, options?: MatchOptions) => EntryMatch | undefined;
|
|
28
|
+
export declare const matchEntry: (entry: CatalogEntry, normalizedQuery: string, options?: MatchOptions) => number | undefined;
|
|
29
|
+
export interface CatalogMatch {
|
|
30
|
+
readonly catalog: Catalog;
|
|
31
|
+
readonly entry: CatalogEntry;
|
|
32
|
+
readonly score: number;
|
|
33
|
+
readonly verbatim: boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare const matchCatalogs: (catalogs: readonly Catalog[], text: string, options?: MatchOptions) => CatalogMatch[];
|
|
36
|
+
export declare const lineOfKey: (catalog: Catalog, key: string) => number | undefined;
|
|
37
|
+
export declare const keyUsePattern: (keys: readonly string[]) => string;
|
|
38
|
+
export declare const dynamicKeyPattern: (key: string) => string | undefined;
|
|
39
|
+
export declare const parentKey: (key: string) => string | undefined;
|
|
40
|
+
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/engines/catalog.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAM7C,MAAM,WAAW,YAAY;IAEzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,OAAO;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;CAC7C;AAQD,eAAO,MAAM,aAAa,SAAU,MAAM,KAAG,OAK5C,CAAC;AAIF,eAAO,MAAM,WAAW,SAAU,MAAM,KAAG,MAM1C,CAAC;AAEF,eAAO,MAAM,QAAQ,SAAU,MAAM,KAAG,MAAM,GAAG,SAQhD,CAAC;AAGF,eAAO,MAAM,SAAS,WAAY,MAAM,GAAG,SAAS,KAAG,OAAsG,CAAC;AAU9J,eAAO,MAAM,YAAY,SAAU,MAAM,KAAG,YAAY,EA0HvD,CAAC;AAOF,eAAO,MAAM,YAAY,YAAmB,SAAS,SAAS,EAAE,KAAG,OAAO,CAAC,OAAO,EAAE,CAwBpC,CAAC;AAOjD,eAAO,MAAM,aAAa,SAAU,MAAM,KAAG,MAO9B,CAAC;AA4DhB,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAUnC,MAAM,WAAW,YAAY;IAGzB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IAEvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAGvB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC9B;AAED,eAAO,MAAM,UAAU,UAAW,YAAY,mBAAmB,MAAM,YAAW,YAAY,KAAQ,UAAU,GAAG,SAoClH,CAAC;AAGF,eAAO,MAAM,UAAU,UAAW,YAAY,mBAAmB,MAAM,YAAW,YAAY,KAAQ,MAAM,GAAG,SACzD,CAAC;AAEvD,MAAM,WAAW,YAAY;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC9B;AAmBD,eAAO,MAAM,aAAa,aAAc,SAAS,OAAO,EAAE,QAAQ,MAAM,YAAW,YAAY,KAAQ,YAAY,EAyBlH,CAAC;AAIF,eAAO,MAAM,SAAS,YAAa,OAAO,OAAO,MAAM,KAAG,MAAM,GAAG,SAAqE,CAAC;AAIzI,eAAO,MAAM,aAAa,SAAU,SAAS,MAAM,EAAE,KAAG,MAA+D,CAAC;AAGxH,eAAO,MAAM,iBAAiB,QAAS,MAAM,KAAG,MAAM,GAAG,SAGxD,CAAC;AAGF,eAAO,MAAM,SAAS,QAAS,MAAM,KAAG,MAAM,GAAG,SAGhD,CAAC"}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
const CATALOG_DIRS = new Set(["i18n", "l10n", "locales", "locale", "lang", "langs", "languages", "translations", "messages"]);
|
|
3
|
+
const LOCALE_CODE = /^[a-z]{2,3}(?:[-_][A-Za-z]{2,4})?$/;
|
|
4
|
+
export const isCatalogPath = (path) => {
|
|
5
|
+
if (!path.endsWith(".json")) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
return path.split("/").slice(0, -1).some((segment) => CATALOG_DIRS.has(segment.toLowerCase()));
|
|
9
|
+
};
|
|
10
|
+
export const catalogHome = (path) => {
|
|
11
|
+
const parts = path.split("/");
|
|
12
|
+
const catalogDir = parts.findIndex((segment, index) => index < parts.length - 1 && CATALOG_DIRS.has(segment.toLowerCase()));
|
|
13
|
+
const src = parts.slice(0, Math.max(catalogDir, 0)).lastIndexOf("src");
|
|
14
|
+
const end = src !== -1 ? src + 1 : Math.max(catalogDir, 0);
|
|
15
|
+
return parts.slice(0, end).map((segment) => `${segment}/`).join("");
|
|
16
|
+
};
|
|
17
|
+
export const localeOf = (path) => {
|
|
18
|
+
const parts = path.split("/");
|
|
19
|
+
const base = (parts.at(-1) ?? "").replace(/\.json$/, "");
|
|
20
|
+
if (LOCALE_CODE.test(base)) {
|
|
21
|
+
return base;
|
|
22
|
+
}
|
|
23
|
+
const parent = parts.at(-2) ?? "";
|
|
24
|
+
return LOCALE_CODE.test(parent) ? parent : undefined;
|
|
25
|
+
};
|
|
26
|
+
export const isEnglish = (locale) => locale === "en" || locale?.startsWith("en-") === true || locale?.startsWith("en_") === true;
|
|
27
|
+
const ESCAPES = { '"': '"', "\\": "\\", "/": "/", b: "\b", f: "\f", n: "\n", r: "\r", t: "\t" };
|
|
28
|
+
class Malformed extends Error {
|
|
29
|
+
}
|
|
30
|
+
export const parseCatalog = (text) => {
|
|
31
|
+
const entries = [];
|
|
32
|
+
let at = 0;
|
|
33
|
+
let line = 1;
|
|
34
|
+
const length = text.length;
|
|
35
|
+
const space = () => {
|
|
36
|
+
while (at < length) {
|
|
37
|
+
const char = text[at];
|
|
38
|
+
if (char === "\n") {
|
|
39
|
+
line += 1;
|
|
40
|
+
}
|
|
41
|
+
else if (char !== " " && char !== "\t" && char !== "\r") {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
at += 1;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const string = () => {
|
|
48
|
+
at += 1;
|
|
49
|
+
let out = "";
|
|
50
|
+
let from = at;
|
|
51
|
+
while (at < length) {
|
|
52
|
+
const char = text[at];
|
|
53
|
+
if (char === '"') {
|
|
54
|
+
out += text.slice(from, at);
|
|
55
|
+
at += 1;
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
if (char === "\\") {
|
|
59
|
+
out += text.slice(from, at);
|
|
60
|
+
const escape = text[at + 1] ?? "";
|
|
61
|
+
if (escape === "u") {
|
|
62
|
+
out += String.fromCharCode(Number.parseInt(text.slice(at + 2, at + 6), 16));
|
|
63
|
+
at += 6;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
out += ESCAPES[escape] ?? escape;
|
|
67
|
+
at += 2;
|
|
68
|
+
}
|
|
69
|
+
from = at;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (char === "\n") {
|
|
73
|
+
line += 1;
|
|
74
|
+
}
|
|
75
|
+
at += 1;
|
|
76
|
+
}
|
|
77
|
+
throw new Malformed("unterminated string");
|
|
78
|
+
};
|
|
79
|
+
const value = (path) => {
|
|
80
|
+
space();
|
|
81
|
+
const char = text[at];
|
|
82
|
+
if (char === "{") {
|
|
83
|
+
at += 1;
|
|
84
|
+
space();
|
|
85
|
+
if (text[at] === "}") {
|
|
86
|
+
at += 1;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
for (;;) {
|
|
90
|
+
space();
|
|
91
|
+
if (text[at] !== '"') {
|
|
92
|
+
throw new Malformed("expected a key");
|
|
93
|
+
}
|
|
94
|
+
const key = string();
|
|
95
|
+
space();
|
|
96
|
+
if (text[at] !== ":") {
|
|
97
|
+
throw new Malformed("expected a colon");
|
|
98
|
+
}
|
|
99
|
+
at += 1;
|
|
100
|
+
value([...path, key]);
|
|
101
|
+
space();
|
|
102
|
+
if (text[at] === ",") {
|
|
103
|
+
at += 1;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (text[at] === "}") {
|
|
107
|
+
at += 1;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
throw new Malformed("expected , or }");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (char === "[") {
|
|
114
|
+
at += 1;
|
|
115
|
+
space();
|
|
116
|
+
if (text[at] === "]") {
|
|
117
|
+
at += 1;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
for (let index = 0;; index += 1) {
|
|
121
|
+
value([...path, String(index)]);
|
|
122
|
+
space();
|
|
123
|
+
if (text[at] === ",") {
|
|
124
|
+
at += 1;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (text[at] === "]") {
|
|
128
|
+
at += 1;
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
throw new Malformed("expected , or ]");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (char === '"') {
|
|
135
|
+
const startLine = line;
|
|
136
|
+
entries.push({ key: path.join("."), line: startLine, value: string() });
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
while (at < length && !/[,\]}\s]/.test(text[at])) {
|
|
140
|
+
at += 1;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
try {
|
|
144
|
+
value([]);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
if (!(error instanceof Malformed) && !(error instanceof RangeError)) {
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return entries;
|
|
152
|
+
};
|
|
153
|
+
const cache = new Map();
|
|
154
|
+
const CACHE_MAX = 512;
|
|
155
|
+
export const loadCatalogs = async (entries) => (await Promise.all(entries
|
|
156
|
+
.filter((entry) => isCatalogPath(entry.path))
|
|
157
|
+
.map(async (entry) => {
|
|
158
|
+
const cached = cache.get(entry.abs);
|
|
159
|
+
if (cached !== undefined && cached.mtimeMs === entry.mtimeMs && cached.size === entry.size) {
|
|
160
|
+
return { path: entry.path, locale: localeOf(entry.path), entries: cached.entries };
|
|
161
|
+
}
|
|
162
|
+
const text = await readFile(entry.abs, "utf8").catch(() => undefined);
|
|
163
|
+
if (text === undefined) {
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
const parsed = parseCatalog(text);
|
|
167
|
+
if (cache.size >= CACHE_MAX) {
|
|
168
|
+
cache.clear();
|
|
169
|
+
}
|
|
170
|
+
cache.set(entry.abs, { mtimeMs: entry.mtimeMs, size: entry.size, entries: parsed });
|
|
171
|
+
return { path: entry.path, locale: localeOf(entry.path), entries: parsed };
|
|
172
|
+
}))).filter((catalog) => catalog !== undefined);
|
|
173
|
+
export const normalizeText = (text) => text
|
|
174
|
+
.toLowerCase()
|
|
175
|
+
.replaceAll(/[‘’ʼ`´]/g, "'")
|
|
176
|
+
.replaceAll(/[^\p{L}\p{N}'{}%\s]+/gu, " ")
|
|
177
|
+
.replaceAll(/(^|\s)'+|'+(?=\s|$)/g, "$1")
|
|
178
|
+
.replaceAll(/\s+/g, " ")
|
|
179
|
+
.trim();
|
|
180
|
+
const wordCount = (text) => (text === "" ? 0 : text.split(" ").length);
|
|
181
|
+
const PLACEHOLDER = /\{\{[^{}]*\}\}|%\{[^{}]*\}|\{[^{}]*\}|%[sd]/g;
|
|
182
|
+
const escapeRegExp = (text) => text.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
183
|
+
const SLOT_FILL = String.raw `\S+(?: \S+){0,3}`;
|
|
184
|
+
const templateOf = (value) => {
|
|
185
|
+
const normalized = normalizeText(value);
|
|
186
|
+
const slots = normalized.match(PLACEHOLDER);
|
|
187
|
+
if (slots === null) {
|
|
188
|
+
return { filled: undefined, pieces: [normalized], literalWords: wordCount(normalized) };
|
|
189
|
+
}
|
|
190
|
+
const pieces = normalized
|
|
191
|
+
.split(PLACEHOLDER)
|
|
192
|
+
.map((piece) => piece.trim())
|
|
193
|
+
.filter((piece) => piece !== "");
|
|
194
|
+
const pattern = normalized
|
|
195
|
+
.split(PLACEHOLDER)
|
|
196
|
+
.map((piece) => escapeRegExp(piece))
|
|
197
|
+
.join(SLOT_FILL);
|
|
198
|
+
return { filled: new RegExp(`^${pattern}$`), pieces, literalWords: pieces.reduce((sum, piece) => sum + wordCount(piece), 0) };
|
|
199
|
+
};
|
|
200
|
+
const MIN_TEMPLATE_WORDS = 2;
|
|
201
|
+
const vouches = (template, queryWords) => template.literalWords >= MIN_TEMPLATE_WORDS && template.literalWords * 2 > queryWords;
|
|
202
|
+
const templates = new WeakMap();
|
|
203
|
+
const templatesOf = (entry) => {
|
|
204
|
+
let cached = templates.get(entry);
|
|
205
|
+
if (cached === undefined) {
|
|
206
|
+
cached = entry.value.split(" | ").map(templateOf);
|
|
207
|
+
templates.set(entry, cached);
|
|
208
|
+
}
|
|
209
|
+
return cached;
|
|
210
|
+
};
|
|
211
|
+
export const MIN_PARTIAL_WORDS = 3;
|
|
212
|
+
const PARTIAL_COVERAGE = 0.5;
|
|
213
|
+
const PARTIAL_LONG_WORDS = 5;
|
|
214
|
+
const CONTAINED_COVERAGE = 0.75;
|
|
215
|
+
export const entryMatch = (entry, normalizedQuery, options = {}) => {
|
|
216
|
+
const queryWords = wordCount(normalizedQuery);
|
|
217
|
+
let best;
|
|
218
|
+
const keep = (candidate) => {
|
|
219
|
+
if (best === undefined || candidate.score > best.score) {
|
|
220
|
+
best = candidate;
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
for (const template of templatesOf(entry)) {
|
|
224
|
+
const whole = template.filled === undefined ? template.pieces[0] === normalizedQuery : vouches(template, queryWords) && template.filled.test(normalizedQuery);
|
|
225
|
+
if (whole) {
|
|
226
|
+
return { score: 1, verbatim: true };
|
|
227
|
+
}
|
|
228
|
+
if (options.wholeOnly === true || queryWords < MIN_PARTIAL_WORDS) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
for (const piece of template.pieces) {
|
|
232
|
+
const pieceWords = wordCount(piece);
|
|
233
|
+
if (` ${piece} `.includes(` ${normalizedQuery} `)) {
|
|
234
|
+
const coverage = queryWords / Math.max(pieceWords, queryWords);
|
|
235
|
+
if (coverage >= PARTIAL_COVERAGE || queryWords >= PARTIAL_LONG_WORDS) {
|
|
236
|
+
keep({ score: 0.6 + 0.3 * coverage, verbatim: true });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
else if (pieceWords >= MIN_PARTIAL_WORDS && ` ${normalizedQuery} `.includes(` ${piece} `)) {
|
|
240
|
+
const coverage = pieceWords / queryWords;
|
|
241
|
+
if (coverage >= CONTAINED_COVERAGE) {
|
|
242
|
+
keep({ score: 0.5 + 0.3 * coverage, verbatim: false });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return best;
|
|
248
|
+
};
|
|
249
|
+
export const matchEntry = (entry, normalizedQuery, options = {}) => entryMatch(entry, normalizedQuery, options)?.score;
|
|
250
|
+
const lowered = new WeakMap();
|
|
251
|
+
const lowerOf = (entry) => {
|
|
252
|
+
let lower = lowered.get(entry);
|
|
253
|
+
if (lower === undefined) {
|
|
254
|
+
lower = entry.value.toLowerCase();
|
|
255
|
+
lowered.set(entry, lower);
|
|
256
|
+
}
|
|
257
|
+
return lower;
|
|
258
|
+
};
|
|
259
|
+
const probeWords = (normalized) => [...new Set(normalized.split(" ").filter((word) => word.length >= 3 && !/^[{%]/.test(word)))].toSorted((a, b) => b.length - a.length).slice(0, 3);
|
|
260
|
+
export const matchCatalogs = (catalogs, text, options = {}) => {
|
|
261
|
+
const normalized = normalizeText(text);
|
|
262
|
+
const probes = probeWords(normalized);
|
|
263
|
+
if (probes.length === 0) {
|
|
264
|
+
return [];
|
|
265
|
+
}
|
|
266
|
+
const matches = [];
|
|
267
|
+
for (const catalog of catalogs) {
|
|
268
|
+
for (const entry of catalog.entries) {
|
|
269
|
+
const lower = lowerOf(entry);
|
|
270
|
+
if (!probes.some((probe) => lower.includes(probe))) {
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
const found = entryMatch(entry, normalized, options);
|
|
274
|
+
if (found !== undefined) {
|
|
275
|
+
matches.push({ catalog, entry, ...found });
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return matches.toSorted((a, b) => b.score - a.score ||
|
|
280
|
+
Number(isEnglish(b.catalog.locale)) - Number(isEnglish(a.catalog.locale)) ||
|
|
281
|
+
(a.catalog.path < b.catalog.path ? -1 : a.catalog.path > b.catalog.path ? 1 : a.entry.line - b.entry.line));
|
|
282
|
+
};
|
|
283
|
+
export const lineOfKey = (catalog, key) => catalog.entries.find((entry) => entry.key === key)?.line;
|
|
284
|
+
export const keyUsePattern = (keys) => `[\`'"](?:${keys.map(escapeRegExp).join("|")})[\`'"]`;
|
|
285
|
+
export const dynamicKeyPattern = (key) => {
|
|
286
|
+
const parent = parentKey(key);
|
|
287
|
+
return parent === undefined ? undefined : `[\`'"]${escapeRegExp(parent)}\\.\\$\\{`;
|
|
288
|
+
};
|
|
289
|
+
export const parentKey = (key) => {
|
|
290
|
+
const dot = key.lastIndexOf(".");
|
|
291
|
+
return dot <= 0 ? undefined : key.slice(0, dot);
|
|
292
|
+
};
|
|
293
|
+
//# sourceMappingURL=catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/engines/catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface RouteDecl {
|
|
2
|
+
readonly pattern: string;
|
|
3
|
+
readonly line: number;
|
|
4
|
+
readonly component?: {
|
|
5
|
+
readonly line: number;
|
|
6
|
+
readonly spec: string;
|
|
7
|
+
readonly kind: "import" | "identifier";
|
|
8
|
+
};
|
|
9
|
+
readonly redirect: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare const routeTable: (text: string) => RouteDecl[];
|
|
12
|
+
export declare const jsxRoutes: (text: string) => RouteDecl[];
|
|
13
|
+
export interface RouteAddress {
|
|
14
|
+
readonly path: string;
|
|
15
|
+
readonly segments: readonly string[];
|
|
16
|
+
readonly query: readonly (readonly [string, string])[];
|
|
17
|
+
}
|
|
18
|
+
export declare const parseRouteAddress: (query: string) => RouteAddress | undefined;
|
|
19
|
+
export interface CompiledRoute {
|
|
20
|
+
readonly regex: RegExp;
|
|
21
|
+
readonly params: readonly string[];
|
|
22
|
+
readonly statics: number;
|
|
23
|
+
readonly catchAll: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare const compileRoute: (pattern: string) => CompiledRoute;
|
|
26
|
+
export interface RouteMatch {
|
|
27
|
+
readonly route: RouteDecl;
|
|
28
|
+
readonly values: readonly (readonly [string, string])[];
|
|
29
|
+
readonly statics: number;
|
|
30
|
+
}
|
|
31
|
+
export declare const matchRoutes: (routes: readonly RouteDecl[], address: RouteAddress) => RouteMatch[];
|
|
32
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/engines/routes.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,SAAS;IAEtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,SAAS,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,YAAY,CAAA;KAAE,CAAC;IAE9G,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC9B;AAsED,eAAO,MAAM,UAAU,SAAU,MAAM,KAAG,SAAS,EAwPlD,CAAC;AAcF,eAAO,MAAM,SAAS,SAAU,MAAM,KAAG,SAAS,EAmB5C,CAAC;AAIP,MAAM,WAAW,YAAY;IAEzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAErC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;CAC1D;AAOD,eAAO,MAAM,iBAAiB,UAAW,MAAM,KAAG,YAAY,GAAG,SAwBhE,CAAC;AAEF,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAEnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC9B;AAKD,eAAO,MAAM,YAAY,YAAa,MAAM,KAAG,aA+B9C,CAAC;AAEF,MAAM,WAAW,UAAU;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAE1B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;IACxD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC5B;AAID,eAAO,MAAM,WAAW,WAAY,SAAS,SAAS,EAAE,WAAW,YAAY,KAAG,UAAU,EAkB3F,CAAC"}
|