chronolizer 0.4.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 +4 -2
- 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 +2 -3
- package/dist/language/registry.mjs +4 -2
- package/dist/locales/cs.mjs +9 -20
- package/dist/locales/de.mjs +199 -495
- package/dist/locales/en.mjs +13 -13
- package/dist/locales/es.mjs +10 -19
- package/dist/locales/fr.mjs +20 -35
- package/dist/locales/nl.mjs +8 -19
- package/dist/locales/pl.mjs +8 -19
- package/dist/locales/shared.mjs +7 -7
- package/dist/locales/tr.mjs +9 -21
- package/dist/natural/correction.mjs +70 -27
- package/dist/natural/parse.mjs +1 -1
- package/dist/natural/suggest.mjs +9 -1
- package/dist/natural/suggestion.mjs +44 -26
- package/dist/resolve/resolve.mjs +1 -2
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -230,10 +230,12 @@ A date filter has at least one bound. It can have one lower bound and one upper
|
|
|
230
230
|
| `lte` | On or before the value |
|
|
231
231
|
|
|
232
232
|
An expression starts with `now` or an ISO date. Operations run from left to right.
|
|
233
|
+
Calendar shifts are not combined or canceled. For example, `2025-01-31||+1M+1M`
|
|
234
|
+
resolves to March 28, not March 31. Each month shift clamps the day to the last
|
|
235
|
+
valid day of that month. Time-zone gaps can also affect intermediate dates.
|
|
233
236
|
|
|
234
237
|
```text
|
|
235
|
-
expression :=
|
|
236
|
-
anchor := "now" | YYYY-MM-DD | YYYY-MM-DD "||"
|
|
238
|
+
expression := "now" operation* | YYYY-MM-DD | YYYY-MM-DD "||" operation+
|
|
237
239
|
operation := ("+" | "-") positiveInteger unit | "/" unit
|
|
238
240
|
unit := "d" | "w" | "M" | "q" | "y"
|
|
239
241
|
```
|
package/dist/ast/fold.mjs
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Match, Schema
|
|
1
|
+
import { GreaterThanOrEqual, LessThan, Now, Shift, StartOf } from "./schemas.mjs";
|
|
2
|
+
import { Match, Schema } from "effect";
|
|
3
3
|
//#region src/ast/fold.ts
|
|
4
|
-
const isDateLiteral = Schema.is(DateLiteral);
|
|
5
4
|
const isGreaterThanOrEqual = Schema.is(GreaterThanOrEqual);
|
|
6
5
|
const isLessThan = Schema.is(LessThan);
|
|
7
6
|
const isNow = Schema.is(Now);
|
|
8
7
|
const isShift = Schema.is(Shift);
|
|
9
8
|
const isStartOf = Schema.is(StartOf);
|
|
10
|
-
const foldInstant = (expression, algebra) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
};
|
|
9
|
+
const foldInstant = (expression, algebra) => Match.typeTags()({
|
|
10
|
+
Now: () => algebra.now(),
|
|
11
|
+
DateLiteral: (literal) => algebra.dateLiteral(literal.value),
|
|
12
|
+
Shift: (operation) => algebra.shift(foldInstant(operation.base, algebra), operation.amount, operation.unit),
|
|
13
|
+
StartOf: (operation) => algebra.startOf(foldInstant(operation.base, algebra), operation.unit)
|
|
14
|
+
})(expression);
|
|
17
15
|
const shiftOffset = (base, amount, unit) => Match.value(unit).pipe(Match.when("year", () => ({
|
|
18
16
|
...base,
|
|
19
17
|
months: base.months + amount * 12
|
package/dist/ast/normalize.mjs
CHANGED
|
@@ -1,21 +1,12 @@
|
|
|
1
|
-
import { Shift } from "./schemas.mjs";
|
|
2
1
|
import { boundedRange, dateLiteral, greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, lowerOpenRange, now, shift, startOf, upperOpenRange } from "./constructors.mjs";
|
|
3
|
-
import {
|
|
2
|
+
import { foldInstant } from "./fold.mjs";
|
|
3
|
+
import { Match } from "effect";
|
|
4
4
|
//#region src/ast/normalize.ts
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
Shift: (operation) => {
|
|
11
|
-
const base = normalizeInstant(operation.base);
|
|
12
|
-
if (operation.amount === 0) return base;
|
|
13
|
-
if (isShift(base) && base.unit === operation.unit) {
|
|
14
|
-
const amount = base.amount + operation.amount;
|
|
15
|
-
if (Number.isSafeInteger(amount)) return normalizeInstant(shift(base.base, amount, operation.unit));
|
|
16
|
-
}
|
|
17
|
-
return shift(base, operation.amount, operation.unit);
|
|
18
|
-
}
|
|
5
|
+
const normalizeInstant = (expression) => foldInstant(expression, {
|
|
6
|
+
now,
|
|
7
|
+
dateLiteral,
|
|
8
|
+
startOf,
|
|
9
|
+
shift: (base, amount, unit) => amount === 0 ? base : shift(base, amount, unit)
|
|
19
10
|
});
|
|
20
11
|
const normalizeLower = (bound) => Match.valueTags(bound, {
|
|
21
12
|
GreaterThan: (value) => greaterThan(normalizeInstant(value.value)),
|
package/dist/filter/codec.mjs
CHANGED
|
@@ -31,13 +31,12 @@ const formatUpper = Match.typeTags()({
|
|
|
31
31
|
LessThanOrEqual: (bound) => DateFilter.make({ lte: formatInstantExpression(bound.value) })
|
|
32
32
|
});
|
|
33
33
|
const formatFilter = (range) => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
...
|
|
37
|
-
...formatUpper(normalized.upper)
|
|
34
|
+
if (range.lower !== void 0 && range.upper !== void 0) return DateFilter.make({
|
|
35
|
+
...formatLower(range.lower),
|
|
36
|
+
...formatUpper(range.upper)
|
|
38
37
|
});
|
|
39
|
-
if (
|
|
40
|
-
return formatUpper(
|
|
38
|
+
if (range.lower !== void 0) return formatLower(range.lower);
|
|
39
|
+
return formatUpper(range.upper);
|
|
41
40
|
};
|
|
42
41
|
const rangeKey = (range) => {
|
|
43
42
|
const filter = formatFilter(range);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { isIsoDate } from "../ast/schemas.mjs";
|
|
2
2
|
import { dateLiteral, now, shift, startOf } from "../ast/constructors.mjs";
|
|
3
3
|
import { foldInstant } from "../ast/fold.mjs";
|
|
4
|
-
import { normalizeInstant } from "../ast/normalize.mjs";
|
|
5
4
|
import { FilterExpressionParseError } from "./errors.mjs";
|
|
6
5
|
import { Effect, Match } from "effect";
|
|
7
6
|
//#region src/filter/expression.ts
|
|
@@ -66,7 +65,7 @@ const appendOperation = (base, operation) => ({
|
|
|
66
65
|
text: `${base.text}${base.fixedAnchor ? "||" : ""}${operation}`,
|
|
67
66
|
fixedAnchor: false
|
|
68
67
|
});
|
|
69
|
-
const formatInstantExpression = (expression) => foldInstant(
|
|
68
|
+
const formatInstantExpression = (expression) => foldInstant(expression, {
|
|
70
69
|
now: () => ({
|
|
71
70
|
text: "now",
|
|
72
71
|
fixedAnchor: false
|
|
@@ -75,7 +74,7 @@ const formatInstantExpression = (expression) => foldInstant(normalizeInstant(exp
|
|
|
75
74
|
text: value,
|
|
76
75
|
fixedAnchor: true
|
|
77
76
|
}),
|
|
78
|
-
shift: (base, amount, unit) => appendOperation(base, `${amount < 0 ? "-" : "+"}${Math.abs(amount)}${symbolFromUnit(unit)}`),
|
|
77
|
+
shift: (base, amount, unit) => amount === 0 ? base : appendOperation(base, `${amount < 0 ? "-" : "+"}${Math.abs(amount)}${symbolFromUnit(unit)}`),
|
|
79
78
|
startOf: (base, unit) => appendOperation(base, `/${symbolFromUnit(unit)}`)
|
|
80
79
|
}).text;
|
|
81
80
|
//#endregion
|
|
@@ -86,11 +86,13 @@ const createRegistry = Effect.fn(function* () {
|
|
|
86
86
|
})));
|
|
87
87
|
});
|
|
88
88
|
const resolve = Effect.fn(function* (locale) {
|
|
89
|
+
const cached = (yield* Ref.get(state)).compiledLanguages.get(locale);
|
|
90
|
+
if (cached !== void 0) return cached;
|
|
89
91
|
const canonical = canonicalBaseLocale(locale);
|
|
90
92
|
if (Option.isSome(canonical)) {
|
|
91
93
|
const compiled = yield* Ref.modify(state, (current) => {
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
94
|
+
const canonicalCached = current.compiledLanguages.get(canonical.value);
|
|
95
|
+
if (canonicalCached !== void 0) return [Option.some(canonicalCached), current];
|
|
94
96
|
const language = compileLanguage(canonical.value, current.entries);
|
|
95
97
|
if (Option.isNone(language)) return [language, current];
|
|
96
98
|
const cache = new Map(current.compiledLanguages);
|
package/dist/locales/cs.mjs
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { isIsoDate } from "../ast/schemas.mjs";
|
|
2
1
|
import { BaseLanguageContribution } from "../language/model.mjs";
|
|
3
2
|
import { normalizeNaturalText } from "../natural/text.mjs";
|
|
4
3
|
import { defineLanguagePlugin, languagePluginsLayer } from "../language/registry.mjs";
|
|
5
4
|
import { correctWhitespaceSeparatedText } from "../natural/correction.mjs";
|
|
6
5
|
import { completeNaturalPhrases, fixedCalendarPeriodPhrases, naturalCount, prefixNaturalPhrases } from "../natural/suggestion.mjs";
|
|
7
|
-
import { absoluteDatePeriod, calendarPeriodOffset, candidate, compileCountAliasNormalizer, compoundCountAliases, countAliasVocabulary, currentYearDatePeriods, datedPeriods, datedQuarterPeriods, decimalTens, decomposeShiftedPeriodRange,
|
|
6
|
+
import { absoluteDatePeriod, calendarPeriodOffset, candidate, compileCountAliasNormalizer, compoundCountAliases, countAliasVocabulary, currentYearDatePeriods, datedPeriods, datedQuarterPeriods, decimalTens, decomposeShiftedPeriodRange, fixedMonthPeriod, fixedQuarterPeriod, fixedYearPeriod, fromNowRange, futurePeriod, futureRange, joinedNowCandidate, joinedPeriodCandidate, monthOfRelativeYear, namedCurrentYearDatePeriod, namedDatePeriod, openBoundaryCandidate, parseTrailingCount, periodEndDay, periodRange, periodStartDay, periodToDateRange, periodsFromPhrases, quarterOfRelativeYear, relativePeriod, relativeWeekday, relativeWeekend, remainingPeriodRange, renderPeriodRange, sequentialCountAliases, shiftPeriod, textAt, trailingPeriod, trailingRange, untilNowRange, validYear } from "./shared.mjs";
|
|
8
7
|
import { Effect, Option, String as String$1 } from "effect";
|
|
9
8
|
//#region src/locales/cs.ts
|
|
10
9
|
const months = [
|
|
@@ -486,15 +485,7 @@ const parseNamedDate = (input) => {
|
|
|
486
485
|
const named = String$1.match(/^([0-3]?\d)\.?(?: )([a-záčďéěíňóřšťúůýž]+\.?) (\d{4})$/u)(input);
|
|
487
486
|
if (Option.isSome(named)) return namedDatePeriod(textAt(named.value, 3), textAt(named.value, 2), textAt(named.value, 1), monthNumber, dateLabel);
|
|
488
487
|
const numeric = String$1.match(/^([0-3]?\d)(?:\. ?|[/-])([01]?\d)(?:\. ?|[/-])(\d{4})$/u)(input);
|
|
489
|
-
if (Option.isSome(numeric))
|
|
490
|
-
const year = validYear(textAt(numeric.value, 3));
|
|
491
|
-
const month = Number(textAt(numeric.value, 2));
|
|
492
|
-
const day = Number(textAt(numeric.value, 1));
|
|
493
|
-
if (year !== void 0 && month >= 1 && month <= 12) {
|
|
494
|
-
const value = isoDate(year, month, day);
|
|
495
|
-
if (isIsoDate(value) && value !== "9999-12-31") return Option.some(fixedDatePeriod(value, dateLabel(day, month, year)));
|
|
496
|
-
}
|
|
497
|
-
}
|
|
488
|
+
if (Option.isSome(numeric)) return namedDatePeriod(textAt(numeric.value, 3), textAt(numeric.value, 2), textAt(numeric.value, 1), Number, dateLabel);
|
|
498
489
|
const current = String$1.match(/^([0-3]?\d)\.? ([a-záčďéěíňóřšťúůýž]+\.?)$/u)(input);
|
|
499
490
|
return Option.isSome(current) ? namedCurrentYearDatePeriod(textAt(current.value, 2), textAt(current.value, 1), monthNumber, currentDateLabel) : Option.none();
|
|
500
491
|
};
|
|
@@ -616,14 +607,12 @@ const parsePeriod = (input) => {
|
|
|
616
607
|
return Option.isSome(base) ? base : parseCalendarOffset(input);
|
|
617
608
|
};
|
|
618
609
|
const countedUnit = (value) => unitAliases.find((entry) => entry[0] === value)?.[1];
|
|
619
|
-
const
|
|
620
|
-
const
|
|
621
|
-
const
|
|
622
|
-
const
|
|
623
|
-
const
|
|
624
|
-
const
|
|
625
|
-
const rollingSincePattern = countedPattern("^za poslední ([1-9]\\d*) (UNIT)$");
|
|
626
|
-
const rollingBarePattern = countedPattern("^([1-9]\\d*) (UNIT)$");
|
|
610
|
+
const calendarPastPattern = /^před ([1-9]\d*) ([^ ]+)$/u;
|
|
611
|
+
const calendarFuturePatterns = [/^za ([1-9]\d*) ([^ ]+)$/u];
|
|
612
|
+
const rollingPastPatterns = [/^(?:poslední|posledních|uplynulé|uplynulých) ([1-9]\d*) ([^ ]+)$/u];
|
|
613
|
+
const rollingFuturePatterns = [/^(?:příští|následující) ([1-9]\d*) ([^ ]+)$/u];
|
|
614
|
+
const rollingSincePattern = /^za poslední ([1-9]\d*) ([^ ]+)$/u;
|
|
615
|
+
const rollingBarePattern = /^([1-9]\d*) ([^ ]+)$/u;
|
|
627
616
|
const firstPatternMatch = (input, patterns) => Option.firstSomeOf(patterns.map((pattern) => String$1.match(pattern)(input)));
|
|
628
617
|
const singularRollingPhrases = units.flatMap((entry) => [
|
|
629
618
|
{
|
|
@@ -742,7 +731,7 @@ const parseCzech = (input) => {
|
|
|
742
731
|
"dnešek až "
|
|
743
732
|
], parsePeriod, (period) => `od ${period} do dneška`, (period) => `od dneška do ${period}`);
|
|
744
733
|
if (Option.isSome(nowBounded)) return nowBounded;
|
|
745
|
-
const bounded = joinedPeriodCandidate(input, [
|
|
734
|
+
const bounded = joinedPeriodCandidate(input.replace(/ včetně$/u, ""), [
|
|
746
735
|
["od ", " do "],
|
|
747
736
|
["mezi ", " a "],
|
|
748
737
|
["", " - "],
|