@jestek-dev/scripture-engine 0.7.1 → 0.14.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/LICENSE +21 -0
- package/README.md +35 -0
- package/dist/config/engineVersion.d.ts +1 -1
- package/dist/config/engineVersion.js +47 -1
- package/dist/corpus/repository.d.ts +189 -2
- package/dist/corpus/repository.js +332 -1
- package/dist/createEngine.d.ts +76 -1
- package/dist/createEngine.js +716 -52
- package/dist/index.d.ts +20 -14
- package/dist/index.js +15 -13
- package/dist/intents/concept.d.ts +115 -2
- package/dist/intents/concept.js +323 -6
- package/dist/intents/lexical.d.ts +42 -3
- package/dist/intents/lexical.js +111 -11
- package/dist/intents/spelling.d.ts +93 -0
- package/dist/intents/spelling.js +124 -0
- package/dist/internal.d.ts +31 -0
- package/dist/internal.js +31 -0
- package/dist/ranking/budgets.d.ts +12 -0
- package/dist/ranking/budgets.js +39 -0
- package/dist/ranking/rank.js +20 -3
- package/dist/reasons/display.d.ts +86 -0
- package/dist/reasons/display.js +105 -0
- package/dist/reasons/types.d.ts +1 -1
- package/dist/reference/reference.d.ts +57 -0
- package/dist/reference/reference.js +210 -48
- package/dist/tokenizer/index.d.ts +35 -0
- package/dist/tokenizer/index.js +37 -1
- package/dist/types.d.ts +104 -0
- package/package.json +7 -1
package/dist/tokenizer/index.js
CHANGED
|
@@ -174,6 +174,21 @@ export function normalizeToken(raw) {
|
|
|
174
174
|
* set form used for overlap scoring and concept-lexicon matching.
|
|
175
175
|
*/
|
|
176
176
|
export function significantWords(text) {
|
|
177
|
+
return significantWordsWithSurface(text).map((entry) => entry.token);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* The same significant-token set, each token paired with the SURFACE form it
|
|
181
|
+
* was normalized from — the first raw word (lowercased, punctuation-stripped)
|
|
182
|
+
* that produced it. Added for the spelling-correction citation (0.12.0/QR-5):
|
|
183
|
+
* a correction chip must cite what the user actually typed ("beleived"),
|
|
184
|
+
* never the stem the tokenizer made of it ("beleiv").
|
|
185
|
+
*
|
|
186
|
+
* This is a PAIRING, not a second tokenizer: `significantWords` delegates
|
|
187
|
+
* here, the token stream is byte-identical to what it always was (invariance-
|
|
188
|
+
* tested), and there is still no options parameter. TOKENIZER_VERSION stays
|
|
189
|
+
* 1.0.0.
|
|
190
|
+
*/
|
|
191
|
+
export function significantWordsWithSurface(text) {
|
|
177
192
|
const seen = new Set();
|
|
178
193
|
const result = [];
|
|
179
194
|
for (const raw of rawWords(text)) {
|
|
@@ -181,10 +196,31 @@ export function significantWords(text) {
|
|
|
181
196
|
if (token === null || seen.has(token))
|
|
182
197
|
continue;
|
|
183
198
|
seen.add(token);
|
|
184
|
-
result.push(token);
|
|
199
|
+
result.push({ token, surface: raw });
|
|
185
200
|
}
|
|
186
201
|
return result;
|
|
187
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* Whole-query phrase normalization for the curated alias table
|
|
205
|
+
* (0.13.0/QR-6): lowercase, apostrophes removed, punctuation folded to
|
|
206
|
+
* spaces, whitespace collapsed — and NOTHING else. Stopwords are KEPT and no
|
|
207
|
+
* stemming, archaic folding, or lemma lookup applies, because the phrases
|
|
208
|
+
* this key serves are exactly the stopword-heavy lines the token pipeline
|
|
209
|
+
* cannot represent ("it is well with my soul" -> `well soul`). Matching is
|
|
210
|
+
* whole-string EQUALITY against curated_aliases.normalized_raw, never
|
|
211
|
+
* containment, so the minimal normalization is the safety property: the less
|
|
212
|
+
* this folds, the less an alias can accidentally swallow.
|
|
213
|
+
*
|
|
214
|
+
* This is an ADDITIVE surface over the same `rawWords` core every other
|
|
215
|
+
* tokenizer output uses — not a second tokenizer, and there is still no
|
|
216
|
+
* options parameter. The token stream is untouched (invariance-tested) and
|
|
217
|
+
* TOKENIZER_VERSION stays 1.0.0. The pipeline's alias importer imports THIS
|
|
218
|
+
* function (never a mirror), so build-side keys and query-side keys cannot
|
|
219
|
+
* drift.
|
|
220
|
+
*/
|
|
221
|
+
export function normalizedPhrase(text) {
|
|
222
|
+
return rawWords(text).join(' ');
|
|
223
|
+
}
|
|
188
224
|
/**
|
|
189
225
|
* Positional token stream: every significant occurrence, with the word index
|
|
190
226
|
* it came from. Proximity scoring (intent 3) needs positions, which the
|
package/dist/types.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* none of which the engine knows or cares about.
|
|
10
10
|
*/
|
|
11
11
|
import type { Reason } from './reasons/types.js';
|
|
12
|
+
import type { ReferenceSuggestion } from './reference/reference.js';
|
|
12
13
|
export type ContentScalar = string | number | boolean | null | ArrayBuffer | ArrayBufferView;
|
|
13
14
|
export interface ContentQueryResult {
|
|
14
15
|
readonly rows: readonly Readonly<Record<string, ContentScalar>>[];
|
|
@@ -56,12 +57,94 @@ export interface ResultIdentity {
|
|
|
56
57
|
*/
|
|
57
58
|
readonly layerFingerprint: string;
|
|
58
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* One member verse of a grouped (merged) result, its evidence uncollapsed
|
|
62
|
+
* (0.14.0/CO-3 PR 2). The merged row's reasons are strongest-per-label across
|
|
63
|
+
* the group; this is where a consumer finds what EACH verse contributed —
|
|
64
|
+
* "every verse's own evidence still visible" is part of the grouping design,
|
|
65
|
+
* not an optional nicety.
|
|
66
|
+
*/
|
|
67
|
+
export interface GroupedVerse {
|
|
68
|
+
readonly targetId: string;
|
|
69
|
+
readonly reference: string;
|
|
70
|
+
readonly excerpt: string;
|
|
71
|
+
readonly score: number;
|
|
72
|
+
readonly reasons: readonly Reason[];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Why a merged result's verses travel together (0.14.0/CO-3 PR 2). A typed
|
|
76
|
+
* field, deliberately NOT a `Reason`: reasons must correspond to scoring
|
|
77
|
+
* evidence, and grouping contributes ZERO points — the merged row's score is
|
|
78
|
+
* the max of its members, never a sum. The grouping cites a countable fact
|
|
79
|
+
* and names its source; nothing theological is adjudicated (covenant 6).
|
|
80
|
+
*/
|
|
81
|
+
export interface ResultGrouping {
|
|
82
|
+
/**
|
|
83
|
+
* The full span of the grouping unit — the curated anchor span, the alias
|
|
84
|
+
* verse range, or the derived pericope. The result row's own `reference`
|
|
85
|
+
* spans only the verses that actually surfaced (the hits), which may be a
|
|
86
|
+
* subset of this section.
|
|
87
|
+
*/
|
|
88
|
+
readonly section: {
|
|
89
|
+
/** Label of the full section span, e.g. "Psalms 136:1-26". */
|
|
90
|
+
readonly reference: string;
|
|
91
|
+
readonly startVerseId: number;
|
|
92
|
+
readonly endVerseId: number;
|
|
93
|
+
};
|
|
94
|
+
readonly provenance: {
|
|
95
|
+
/**
|
|
96
|
+
* Manifest source id the grouping stands on: the anchor's own source(s)
|
|
97
|
+
* for an anchor-span merge ('+'-joined ascending when several agree,
|
|
98
|
+
* e.g. 'editorial'), 'hymn-aliases' for an alias verse range, or
|
|
99
|
+
* 'openbible-sections' for the pericope path. Authority order is fixed:
|
|
100
|
+
* an anchor span is checked first, so pericope provenance never usurps
|
|
101
|
+
* anchor provenance.
|
|
102
|
+
*/
|
|
103
|
+
readonly sourceId: string;
|
|
104
|
+
/** Human-facing attribution, e.g. "OpenBible section boundaries (CC BY)". */
|
|
105
|
+
readonly label: string;
|
|
106
|
+
/**
|
|
107
|
+
* Pericope groups only: the summed boundary vote at the section's start
|
|
108
|
+
* verse — the exact number the artifact stores (how many of the 20
|
|
109
|
+
* surveyed translations start a section there). A countable structural
|
|
110
|
+
* fact, never a relevance score; the explanation and the shipped data
|
|
111
|
+
* cannot disagree because this is read from the same row.
|
|
112
|
+
*/
|
|
113
|
+
readonly boundaryVotes?: number;
|
|
114
|
+
};
|
|
115
|
+
}
|
|
59
116
|
export interface DiscoveryResult {
|
|
60
117
|
readonly targetId: string;
|
|
61
118
|
readonly reference: string;
|
|
62
119
|
readonly excerpt: string;
|
|
63
120
|
readonly score: number;
|
|
64
121
|
readonly reasons: readonly Reason[];
|
|
122
|
+
/**
|
|
123
|
+
* Additive (0.14.0/CO-3 PR 2): present exactly when this row is a merged
|
|
124
|
+
* passage-level result — the member verses in canonical order, each with
|
|
125
|
+
* its own uncollapsed evidence. Absent on single-verse rows.
|
|
126
|
+
*/
|
|
127
|
+
readonly verses?: readonly GroupedVerse[];
|
|
128
|
+
/**
|
|
129
|
+
* Additive (0.14.0/CO-3 PR 2): present exactly when `verses` is — why the
|
|
130
|
+
* members travel together, citing the source that drew the boundary.
|
|
131
|
+
*/
|
|
132
|
+
readonly grouping?: ResultGrouping;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* One cited spelling correction (0.12.0/QR-5). Corrections are never silent:
|
|
136
|
+
* every substituted token is reported with the SURFACE form the user typed
|
|
137
|
+
* (never the stem the tokenizer made of it), the vocabulary term substituted,
|
|
138
|
+
* and the verified integer Damerau distance that justifies it — the same
|
|
139
|
+
* citation the token chips render as `(corrected from "<typed>")`.
|
|
140
|
+
*/
|
|
141
|
+
export interface SpellingCorrection {
|
|
142
|
+
/** What the user typed (lowercased surface form), e.g. "beleived". */
|
|
143
|
+
readonly typed: string;
|
|
144
|
+
/** The vocabulary term substituted, e.g. "believ". */
|
|
145
|
+
readonly corrected: string;
|
|
146
|
+
/** Verified integer Damerau–Levenshtein distance — the citation. */
|
|
147
|
+
readonly distance: number;
|
|
65
148
|
}
|
|
66
149
|
export type ResearchOutcome = {
|
|
67
150
|
readonly kind: 'reference';
|
|
@@ -69,10 +152,27 @@ export type ResearchOutcome = {
|
|
|
69
152
|
} | {
|
|
70
153
|
readonly kind: 'invalid-reference';
|
|
71
154
|
readonly query: string;
|
|
155
|
+
/**
|
|
156
|
+
* Additive (0.11.0/QR-4): a cited did-you-mean on the dead end — the
|
|
157
|
+
* unique in-policy near-miss book, the validated reference it implies,
|
|
158
|
+
* and the edit distance that justifies the guess. Suggestion only:
|
|
159
|
+
* the engine NEVER silently opens a guessed passage; consumers render
|
|
160
|
+
* it as a question ("did you mean Philippians 4:13?").
|
|
161
|
+
*/
|
|
162
|
+
readonly suggestion?: ReferenceSuggestion;
|
|
72
163
|
} | {
|
|
73
164
|
readonly kind: 'discovery';
|
|
74
165
|
readonly query: string;
|
|
75
166
|
readonly results: readonly DiscoveryResult[];
|
|
167
|
+
/**
|
|
168
|
+
* Additive (0.12.0/QR-5): present iff `research()` substituted
|
|
169
|
+
* corrections for out-of-vocabulary tokens — the machine-readable
|
|
170
|
+
* citation consumers render (J32). Absent means nothing was corrected;
|
|
171
|
+
* a word in ANY vocabulary is never rewritten (the OOV gate).
|
|
172
|
+
* `forSong()` never corrects, so its discovery outcome never carries
|
|
173
|
+
* this field.
|
|
174
|
+
*/
|
|
175
|
+
readonly corrections?: readonly SpellingCorrection[];
|
|
76
176
|
};
|
|
77
177
|
export type ResearchResult = ResearchOutcome & ResultIdentity;
|
|
78
178
|
/** Concept-resolution output for `engine.themes()`. */
|
|
@@ -90,6 +190,8 @@ export type PassageResult = ({
|
|
|
90
190
|
} & ResultIdentity) | ({
|
|
91
191
|
readonly kind: 'invalid-reference';
|
|
92
192
|
readonly query: string;
|
|
193
|
+
/** Additive (0.11.0/QR-4): see ResearchOutcome's invalid-reference. */
|
|
194
|
+
readonly suggestion?: ReferenceSuggestion;
|
|
93
195
|
} & ResultIdentity);
|
|
94
196
|
/**
|
|
95
197
|
* `engine.related()` — what a curated source connects to a passage.
|
|
@@ -107,6 +209,8 @@ export type RelatedResult = ({
|
|
|
107
209
|
} & ResultIdentity) | ({
|
|
108
210
|
readonly kind: 'invalid-reference';
|
|
109
211
|
readonly query: string;
|
|
212
|
+
/** Additive (0.11.0/QR-4): see ResearchOutcome's invalid-reference. */
|
|
213
|
+
readonly suggestion?: ReferenceSuggestion;
|
|
110
214
|
} & ResultIdentity);
|
|
111
215
|
/**
|
|
112
216
|
* Multi-field input for `engine.forSong()`.
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jestek-dev/scripture-engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Pure deterministic Scripture retrieval and ranking core. Zero I/O, zero runtime AI.",
|
|
6
|
+
"license": "MIT",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "git+https://github.com/jestek-dev/scripture-search-engine.git",
|
|
@@ -18,6 +19,10 @@
|
|
|
18
19
|
".": {
|
|
19
20
|
"types": "./dist/index.d.ts",
|
|
20
21
|
"default": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./internal": {
|
|
24
|
+
"types": "./dist/internal.d.ts",
|
|
25
|
+
"default": "./dist/internal.js"
|
|
21
26
|
}
|
|
22
27
|
},
|
|
23
28
|
"files": [
|
|
@@ -30,6 +35,7 @@
|
|
|
30
35
|
"test": "vitest run"
|
|
31
36
|
},
|
|
32
37
|
"devDependencies": {
|
|
38
|
+
"fast-check": "4.9.0",
|
|
33
39
|
"typescript": "5.9.3",
|
|
34
40
|
"vitest": "4.1.10"
|
|
35
41
|
}
|