chronolizer 0.3.0 → 0.4.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/README.md +50 -47
- package/dist/ast/fold.mjs +8 -10
- package/dist/ast/normalize.mjs +7 -16
- package/dist/filter/codec.mjs +5 -6
- package/dist/filter/expression.mjs +26 -21
- package/dist/language/model.mjs +1 -1
- package/dist/language/registry.mjs +34 -12
- package/dist/locales/cs.mjs +194 -36
- package/dist/locales/de.mjs +307 -490
- package/dist/locales/en.mjs +162 -46
- package/dist/locales/es.mjs +187 -37
- package/dist/locales/fr.mjs +168 -52
- package/dist/locales/nl.mjs +125 -37
- package/dist/locales/pl.mjs +163 -36
- package/dist/locales/shared.mjs +68 -5
- package/dist/locales/tr.mjs +144 -42
- package/dist/natural/correction.d.mts +1 -1
- package/dist/natural/correction.mjs +97 -28
- package/dist/natural/format.mjs +1 -1
- package/dist/natural/parse.mjs +6 -5
- package/dist/natural/suggest.mjs +27 -3
- package/dist/natural/suggestion.mjs +50 -26
- package/dist/resolve/resolve.mjs +2 -3
- package/package.json +2 -1
|
@@ -1,36 +1,46 @@
|
|
|
1
1
|
import { damerauLevenshteinDistance } from "./correction.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { Option, String } from "effect";
|
|
3
3
|
//#region src/natural/suggestion.ts
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const completionCost = (input, target, targetStart, targetEnd, workspace) => {
|
|
5
|
+
const targetLength = targetEnd - targetStart;
|
|
6
|
+
if (input.length <= targetLength && target.startsWith(input, targetStart)) return 0;
|
|
7
|
+
if (input.length < 3 || input.charCodeAt(0) !== target.charCodeAt(targetStart)) return void 0;
|
|
8
|
+
const maximum = input.length >= 5 ? 2 : 1;
|
|
9
|
+
let minimum = maximum + 1;
|
|
10
|
+
let previousLength = -1;
|
|
11
|
+
for (const length of [
|
|
6
12
|
Math.max(1, input.length - 1),
|
|
7
13
|
input.length,
|
|
8
|
-
Math.min(
|
|
9
|
-
])
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const maximum = input.length >= 5 ? 2 : 1;
|
|
16
|
-
const distance = fuzzyPrefixDistance(input, target);
|
|
17
|
-
return distance <= maximum ? distance : void 0;
|
|
14
|
+
Math.min(targetLength, input.length + 1)
|
|
15
|
+
]) {
|
|
16
|
+
if (length === previousLength) continue;
|
|
17
|
+
previousLength = length;
|
|
18
|
+
minimum = Math.min(minimum, damerauLevenshteinDistance(input, target.slice(targetStart, targetStart + length), maximum, workspace));
|
|
19
|
+
}
|
|
20
|
+
return minimum <= maximum ? minimum : void 0;
|
|
18
21
|
};
|
|
19
|
-
const phraseScore = (input, phrase) => {
|
|
22
|
+
const phraseScore = (input, inputWords, phrase, workspace) => {
|
|
20
23
|
if (input === phrase) return 0;
|
|
21
24
|
if (phrase.startsWith(input)) return 1;
|
|
22
|
-
const inputWords = input.split(" ");
|
|
23
|
-
const phraseWords = phrase.split(" ");
|
|
24
|
-
if (inputWords.length > phraseWords.length) return void 0;
|
|
25
25
|
let cost = 0;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
let addedWords = 0;
|
|
27
|
+
let phraseStart = 0;
|
|
28
|
+
for (const inputWord of inputWords) {
|
|
29
|
+
let phraseEnd = phrase.indexOf(" ", phraseStart);
|
|
30
|
+
if (phraseEnd === -1) phraseEnd = phrase.length;
|
|
31
|
+
let wordCost = completionCost(inputWord, phrase, phraseStart, phraseEnd, workspace);
|
|
32
|
+
while (wordCost === void 0 && addedWords < 3) {
|
|
33
|
+
phraseStart = phraseEnd + 1;
|
|
34
|
+
phraseEnd = phrase.indexOf(" ", phraseStart);
|
|
35
|
+
if (phraseEnd === -1) phraseEnd = phrase.length;
|
|
36
|
+
addedWords += 1;
|
|
37
|
+
wordCost = completionCost(inputWord, phrase, phraseStart, phraseEnd, workspace);
|
|
38
|
+
}
|
|
30
39
|
if (wordCost === void 0) return void 0;
|
|
31
40
|
cost += wordCost;
|
|
41
|
+
phraseStart = phraseEnd + 1;
|
|
32
42
|
}
|
|
33
|
-
return
|
|
43
|
+
return 2 + cost + addedWords;
|
|
34
44
|
};
|
|
35
45
|
const completeYearPrefix = (input) => {
|
|
36
46
|
const match = String.match(/(?:^| )(\d{3,4})$/u)(input);
|
|
@@ -64,16 +74,30 @@ const naturalCount = (input) => {
|
|
|
64
74
|
};
|
|
65
75
|
const completeNaturalPhrases = (input, phrases, limit) => {
|
|
66
76
|
const ranked = [];
|
|
67
|
-
|
|
68
|
-
|
|
77
|
+
const inputWords = input.split(" ");
|
|
78
|
+
const workspace = [
|
|
79
|
+
[],
|
|
80
|
+
[],
|
|
81
|
+
[]
|
|
82
|
+
];
|
|
83
|
+
let index = 0;
|
|
84
|
+
for (const phrase of new Set(phrases)) {
|
|
85
|
+
const score = phraseScore(input, inputWords, phrase, workspace);
|
|
69
86
|
if (score !== void 0) ranked.push({
|
|
70
87
|
phrase,
|
|
71
88
|
score,
|
|
72
89
|
index
|
|
73
90
|
});
|
|
91
|
+
index += 1;
|
|
92
|
+
}
|
|
93
|
+
ranked.sort((left, right) => left.score - right.score || left.index - right.index);
|
|
94
|
+
const maximumScore = (ranked[0]?.score ?? 2) <= 1 ? 1 : Number.POSITIVE_INFINITY;
|
|
95
|
+
const matches = [];
|
|
96
|
+
for (const entry of ranked) {
|
|
97
|
+
if (entry.score > maximumScore || matches.length >= limit) break;
|
|
98
|
+
matches.push(entry.phrase);
|
|
74
99
|
}
|
|
75
|
-
|
|
76
|
-
return (sorted.some((entry) => entry.score <= 1) ? sorted.filter((entry) => entry.score <= 1) : sorted).slice(0, limit).map((entry) => entry.phrase);
|
|
100
|
+
return matches;
|
|
77
101
|
};
|
|
78
102
|
//#endregion
|
|
79
103
|
export { completeNaturalPhrases, fixedCalendarPeriodPhrases, naturalCount, prefixNaturalPhrases };
|
package/dist/resolve/resolve.mjs
CHANGED
|
@@ -6,8 +6,7 @@ const shiftDateTime = (value, amount, unit) => Match.value(unit).pipe(Match.when
|
|
|
6
6
|
const startOfDateTime = (value, unit) => {
|
|
7
7
|
if (unit === "quarter") {
|
|
8
8
|
const month = DateTime.getPart(value, "month");
|
|
9
|
-
|
|
10
|
-
return DateTime.startOf(DateTime.setParts(value, { month: quarterMonth }), "month");
|
|
9
|
+
return DateTime.startOf(DateTime.subtract(value, { months: (month - 1) % 3 }), "month");
|
|
11
10
|
}
|
|
12
11
|
return DateTime.startOf(value, unit, { weekStartsOn: 1 });
|
|
13
12
|
};
|
|
@@ -45,7 +44,7 @@ const resolve = Effect.fn("chronolizer.resolve")(function* (range) {
|
|
|
45
44
|
if (range.lower !== void 0 && range.upper !== void 0) {
|
|
46
45
|
const lower = yield* resolveLower(range.lower, reference, zone);
|
|
47
46
|
const upper = yield* resolveUpper(range.upper, reference, zone);
|
|
48
|
-
if (DateTime.toEpochMillis(lower.value) >= DateTime.toEpochMillis(upper.value)) return yield* new ResolutionError({ message: "The
|
|
47
|
+
if (DateTime.toEpochMillis(lower.value) >= DateTime.toEpochMillis(upper.value)) return yield* new ResolutionError({ message: "The range start must be before the range end" });
|
|
49
48
|
return ResolvedDateRange.make({
|
|
50
49
|
lower,
|
|
51
50
|
upper
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chronolizer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Bidirectional natural-language date ranges for Effect.",
|
|
5
5
|
"homepage": "https://github.com/tobimori/chronolizer#readme",
|
|
6
6
|
"bugs": "https://github.com/tobimori/chronolizer/issues",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
|
+
"benchmark": "pnpm build && node tools/benchmark.mjs",
|
|
57
58
|
"build": "vp pack",
|
|
58
59
|
"check:bundle-size": "node tools/check-bundle-size.mjs",
|
|
59
60
|
"check:tree-shaking": "node tools/check-tree-shaking.mjs",
|