chronolizer 0.2.0 → 0.4.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 +170 -167
- package/dist/ast/constructors.d.mts +3 -8
- package/dist/ast/fold.d.mts +2 -1
- package/dist/ast/fold.mjs +38 -31
- package/dist/ast/normalize.mjs +4 -2
- package/dist/ast/schemas.d.mts +18 -25
- package/dist/ast/schemas.mjs +8 -17
- package/dist/filter/expression.mjs +27 -38
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/language/model.d.mts +33 -33
- package/dist/language/model.mjs +5 -8
- package/dist/language/registry.mjs +35 -33
- package/dist/locales/cs.mjs +253 -31
- package/dist/locales/de.mjs +366 -42
- package/dist/locales/en.mjs +215 -51
- package/dist/locales/es.mjs +498 -91
- package/dist/locales/fr.mjs +360 -65
- package/dist/locales/nl.mjs +302 -64
- package/dist/locales/pl.mjs +447 -135
- package/dist/locales/shared.mjs +192 -23
- package/dist/locales/tr.mjs +220 -41
- package/dist/natural/correction.d.mts +1 -1
- package/dist/natural/correction.mjs +31 -5
- package/dist/natural/format.mjs +1 -1
- package/dist/natural/parse.d.mts +1 -1
- package/dist/natural/parse.mjs +5 -4
- package/dist/natural/policy.mjs +2 -2
- package/dist/natural/suggest.mjs +19 -3
- package/dist/natural/suggestion.mjs +11 -5
- package/dist/resolve/resolve.mjs +2 -10
- package/package.json +1 -1
package/dist/ast/schemas.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Option, Schema, String } from "effect";
|
|
1
|
+
import { Match, Option, Schema, String } from "effect";
|
|
2
2
|
//#region src/ast/schemas.ts
|
|
3
3
|
const Unit = Schema.Literals([
|
|
4
4
|
"day",
|
|
@@ -8,16 +8,7 @@ const Unit = Schema.Literals([
|
|
|
8
8
|
"year"
|
|
9
9
|
]);
|
|
10
10
|
const isLeapYear = (year) => year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
11
|
-
const daysInMonth = (year, month) =>
|
|
12
|
-
switch (month) {
|
|
13
|
-
case 2: return isLeapYear(year) ? 29 : 28;
|
|
14
|
-
case 4:
|
|
15
|
-
case 6:
|
|
16
|
-
case 9:
|
|
17
|
-
case 11: return 30;
|
|
18
|
-
default: return 31;
|
|
19
|
-
}
|
|
20
|
-
};
|
|
11
|
+
const daysInMonth = (year, month) => Match.value(month).pipe(Match.when(2, () => isLeapYear(year) ? 29 : 28), Match.when(Match.is(4, 6, 9, 11), () => 30), Match.orElse(() => 31));
|
|
21
12
|
const isIsoDate = (value) => {
|
|
22
13
|
if (Option.isNone(String.match(/^\d{4}-\d{2}-\d{2}$/)(value))) return false;
|
|
23
14
|
const year = Number(value.slice(0, 4));
|
|
@@ -26,21 +17,21 @@ const isIsoDate = (value) => {
|
|
|
26
17
|
return month >= 1 && month <= 12 && day >= 1 && day <= daysInMonth(year, month);
|
|
27
18
|
};
|
|
28
19
|
const IsoDate = Schema.String.check(Schema.makeFilter(isIsoDate, { expected: "an ISO calendar date (YYYY-MM-DD)" })).annotate({ identifier: "IsoDate" });
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
var Now = class extends Schema.TaggedClass()("Now", {}) {};
|
|
21
|
+
var DateLiteral = class extends Schema.TaggedClass()("DateLiteral", { value: IsoDate }) {};
|
|
31
22
|
const ShiftAmount = Schema.Int.check(Schema.isBetween({
|
|
32
23
|
minimum: Number.MIN_SAFE_INTEGER,
|
|
33
24
|
maximum: Number.MAX_SAFE_INTEGER
|
|
34
25
|
}));
|
|
35
|
-
|
|
26
|
+
var Shift = class extends Schema.TaggedClass()("Shift", {
|
|
36
27
|
base: Schema.suspend(() => InstantExpr),
|
|
37
28
|
amount: ShiftAmount,
|
|
38
29
|
unit: Unit
|
|
39
|
-
});
|
|
40
|
-
|
|
30
|
+
}) {};
|
|
31
|
+
var StartOf = class extends Schema.TaggedClass()("StartOf", {
|
|
41
32
|
base: Schema.suspend(() => InstantExpr),
|
|
42
33
|
unit: Unit
|
|
43
|
-
});
|
|
34
|
+
}) {};
|
|
44
35
|
const InstantExpr = Schema.suspend(() => Schema.Union([
|
|
45
36
|
Now,
|
|
46
37
|
DateLiteral,
|
|
@@ -3,27 +3,10 @@ import { dateLiteral, now, shift, startOf } from "../ast/constructors.mjs";
|
|
|
3
3
|
import { foldInstant } from "../ast/fold.mjs";
|
|
4
4
|
import { normalizeInstant } from "../ast/normalize.mjs";
|
|
5
5
|
import { FilterExpressionParseError } from "./errors.mjs";
|
|
6
|
-
import { Effect } from "effect";
|
|
6
|
+
import { Effect, Match } from "effect";
|
|
7
7
|
//#region src/filter/expression.ts
|
|
8
|
-
const unitFromSymbol = (symbol) =>
|
|
9
|
-
|
|
10
|
-
case "d": return "day";
|
|
11
|
-
case "w": return "week";
|
|
12
|
-
case "M": return "month";
|
|
13
|
-
case "q": return "quarter";
|
|
14
|
-
case "y": return "year";
|
|
15
|
-
default: return;
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
const symbolFromUnit = (unit) => {
|
|
19
|
-
switch (unit) {
|
|
20
|
-
case "day": return "d";
|
|
21
|
-
case "week": return "w";
|
|
22
|
-
case "month": return "M";
|
|
23
|
-
case "quarter": return "q";
|
|
24
|
-
case "year": return "y";
|
|
25
|
-
}
|
|
26
|
-
};
|
|
8
|
+
const unitFromSymbol = (symbol) => Match.value(symbol).pipe(Match.when("d", () => "day"), Match.when("w", () => "week"), Match.when("M", () => "month"), Match.when("q", () => "quarter"), Match.when("y", () => "year"), Match.orElse(() => void 0));
|
|
9
|
+
const symbolFromUnit = (unit) => Match.value(unit).pipe(Match.when("day", () => "d"), Match.when("week", () => "w"), Match.when("month", () => "M"), Match.when("quarter", () => "q"), Match.when("year", () => "y"), Match.exhaustive);
|
|
27
10
|
const failAt = (input, offset, expected) => Effect.fail(new FilterExpressionParseError({
|
|
28
11
|
input,
|
|
29
12
|
offset,
|
|
@@ -31,25 +14,31 @@ const failAt = (input, offset, expected) => Effect.fail(new FilterExpressionPars
|
|
|
31
14
|
}));
|
|
32
15
|
const isDigit = (value) => value >= "0" && value <= "9";
|
|
33
16
|
const isFirstPositiveDigit = (value) => value >= "1" && value <= "9";
|
|
17
|
+
const parseAnchor = Effect.fn(function* (input) {
|
|
18
|
+
if (input.startsWith("now")) return {
|
|
19
|
+
expression: now(),
|
|
20
|
+
cursor: 3,
|
|
21
|
+
fixed: false
|
|
22
|
+
};
|
|
23
|
+
const candidate = input.slice(0, 10);
|
|
24
|
+
if (!isIsoDate(candidate)) return yield* failAt(input, 0, "\"now\" or an ISO date (YYYY-MM-DD)");
|
|
25
|
+
return {
|
|
26
|
+
expression: dateLiteral(candidate),
|
|
27
|
+
cursor: 10,
|
|
28
|
+
fixed: true
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
const operationStart = Effect.fn(function* (input, anchor) {
|
|
32
|
+
if (!anchor.fixed || anchor.cursor === input.length) return anchor.cursor;
|
|
33
|
+
if (input.slice(anchor.cursor, anchor.cursor + 2) !== "||") return yield* failAt(input, anchor.cursor, "\"||\" before date operations");
|
|
34
|
+
const cursor = anchor.cursor + 2;
|
|
35
|
+
if (cursor === input.length) return yield* failAt(input, cursor, "an operation beginning with \"+\", \"-\", or \"/\"");
|
|
36
|
+
return cursor;
|
|
37
|
+
});
|
|
34
38
|
const parseInstantExpression = Effect.fn(function* (input) {
|
|
35
|
-
|
|
36
|
-
let expression;
|
|
37
|
-
let
|
|
38
|
-
if (input.startsWith("now")) {
|
|
39
|
-
expression = now();
|
|
40
|
-
cursor = 3;
|
|
41
|
-
} else {
|
|
42
|
-
const candidate = input.slice(0, 10);
|
|
43
|
-
if (!isIsoDate(candidate)) return yield* failAt(input, 0, "\"now\" or an ISO date (YYYY-MM-DD)");
|
|
44
|
-
expression = dateLiteral(candidate);
|
|
45
|
-
cursor = 10;
|
|
46
|
-
fixedAnchor = true;
|
|
47
|
-
}
|
|
48
|
-
if (fixedAnchor && cursor < input.length) {
|
|
49
|
-
if (input.slice(cursor, cursor + 2) !== "||") return yield* failAt(input, cursor, "\"||\" before date operations");
|
|
50
|
-
cursor += 2;
|
|
51
|
-
if (cursor === input.length) return yield* failAt(input, cursor, "an operation beginning with \"+\", \"-\", or \"/\"");
|
|
52
|
-
}
|
|
39
|
+
const anchor = yield* parseAnchor(input);
|
|
40
|
+
let expression = anchor.expression;
|
|
41
|
+
let cursor = yield* operationStart(input, anchor);
|
|
53
42
|
while (cursor < input.length) {
|
|
54
43
|
const operator = input[cursor];
|
|
55
44
|
if (operator === "/") {
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DateLiteral, DateRangeExpr, GreaterThan, GreaterThanOrEqual, InstantExpr, IsoDate, LessThan, LessThanOrEqual, LowerBound, Now, Shift, StartOf, Unit, UpperBound, daysInMonth, isIsoDate } from "./ast/schemas.mjs";
|
|
2
2
|
import { boundedRange, dateLiteral, greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, lowerOpenRange, now, shift, startOf, upperOpenRange } from "./ast/constructors.mjs";
|
|
3
|
-
import { InstantAlgebra, containsPositiveShift, foldInstant } from "./ast/fold.mjs";
|
|
3
|
+
import { InstantAlgebra, containsPositiveShift, foldInstant, isCurrentPeriod } from "./ast/fold.mjs";
|
|
4
4
|
import { normalizeInstant, normalizeRange } from "./ast/normalize.mjs";
|
|
5
5
|
import { FilterExpressionParseError, InvalidDateFilterError } from "./filter/errors.mjs";
|
|
6
6
|
import { completePeriod, formatFilter, parseFilter, rangeKey } from "./filter/codec.mjs";
|
|
@@ -19,4 +19,4 @@ import { SuggestNaturalOptions, suggestNatural } from "./natural/suggest.mjs";
|
|
|
19
19
|
import { naturalWords, normalizeNaturalText } from "./natural/text.mjs";
|
|
20
20
|
import { ResolutionError, ResolvedDateRange, ResolvedGreaterThan, ResolvedGreaterThanOrEqual, ResolvedLessThan, ResolvedLessThanOrEqual, ResolvedLowerBound, ResolvedUpperBound } from "./resolve/schema.mjs";
|
|
21
21
|
import { resolve } from "./resolve/resolve.mjs";
|
|
22
|
-
export { AmbiguousNaturalLanguageError, BaseLanguageContribution, BaseLanguageMetadata, CompiledLanguage, Correction, DateExpressionString, DateFilter, DateLiteral, DateRangeExpr, DateRangeFromFilter, EnglishContribution, EnglishLanguage, EnglishLanguageLayer, FilterExpressionParseError, type FormatNaturalOptions, GreaterThan, GreaterThanOrEqual, InstantAlgebra, InstantExpr, InstantExpressionFromString, InvalidDateFilterError, IsoDate, LanguageConflictError, LanguageContribution, LanguageContributionMetadata, LanguageExtensionContribution, LanguageExtensionMetadata, LanguagePlugin, LanguagePluginContext, LanguageRegistrationError, LanguageRegistry, LanguageRegistryLayer, LessThan, LessThanOrEqual, Locale, LowerBound, NaturalAlternative, NaturalCandidate, NaturalCorrectionCandidate, NaturalLanguageParseError, NaturalLanguageRenderError, NaturalParseResult, NaturalSuggestion, Now, type ParseNaturalOptions, ParseQuality, ResolutionError, ResolvedDateRange, ResolvedGreaterThan, ResolvedGreaterThanOrEqual, ResolvedLessThan, ResolvedLessThanOrEqual, ResolvedLowerBound, ResolvedUpperBound, Shift, StartOf, type SuggestNaturalOptions, Unit, UnsupportedLocaleError, UpperBound, boundedRange, canonicalBaseLocale, completeNaturalPhrases, completePeriod, containsPositiveShift, correctWhitespaceSeparatedText, dateLiteral, daysInMonth, defineLanguagePlugin, foldInstant, formatFilter, formatInstantExpression, formatNatural, greaterThan, greaterThanOrEqual, isIsoDate, languagePluginsLayer, lessThan, lessThanOrEqual, lowerOpenRange, naturalWords, normalizeInstant, normalizeNaturalText, normalizeRange, now, parseFilter, parseInstantExpression, parseNatural, rangeKey, resolve, shift, startOf, suggestNatural, upperOpenRange };
|
|
22
|
+
export { AmbiguousNaturalLanguageError, BaseLanguageContribution, BaseLanguageMetadata, CompiledLanguage, Correction, DateExpressionString, DateFilter, DateLiteral, DateRangeExpr, DateRangeFromFilter, EnglishContribution, EnglishLanguage, EnglishLanguageLayer, FilterExpressionParseError, type FormatNaturalOptions, GreaterThan, GreaterThanOrEqual, InstantAlgebra, InstantExpr, InstantExpressionFromString, InvalidDateFilterError, IsoDate, LanguageConflictError, LanguageContribution, LanguageContributionMetadata, LanguageExtensionContribution, LanguageExtensionMetadata, LanguagePlugin, LanguagePluginContext, LanguageRegistrationError, LanguageRegistry, LanguageRegistryLayer, LessThan, LessThanOrEqual, Locale, LowerBound, NaturalAlternative, NaturalCandidate, NaturalCorrectionCandidate, NaturalLanguageParseError, NaturalLanguageRenderError, NaturalParseResult, NaturalSuggestion, Now, type ParseNaturalOptions, ParseQuality, ResolutionError, ResolvedDateRange, ResolvedGreaterThan, ResolvedGreaterThanOrEqual, ResolvedLessThan, ResolvedLessThanOrEqual, ResolvedLowerBound, ResolvedUpperBound, Shift, StartOf, type SuggestNaturalOptions, Unit, UnsupportedLocaleError, UpperBound, boundedRange, canonicalBaseLocale, completeNaturalPhrases, completePeriod, containsPositiveShift, correctWhitespaceSeparatedText, dateLiteral, daysInMonth, defineLanguagePlugin, foldInstant, formatFilter, formatInstantExpression, formatNatural, greaterThan, greaterThanOrEqual, isCurrentPeriod, isIsoDate, languagePluginsLayer, lessThan, lessThanOrEqual, lowerOpenRange, naturalWords, normalizeInstant, normalizeNaturalText, normalizeRange, now, parseFilter, parseInstantExpression, parseNatural, rangeKey, resolve, shift, startOf, suggestNatural, upperOpenRange };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DateLiteral, DateRangeExpr, GreaterThan, GreaterThanOrEqual, InstantExpr, IsoDate, LessThan, LessThanOrEqual, LowerBound, Now, Shift, StartOf, Unit, UpperBound, daysInMonth, isIsoDate } from "./ast/schemas.mjs";
|
|
2
2
|
import { boundedRange, dateLiteral, greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, lowerOpenRange, now, shift, startOf, upperOpenRange } from "./ast/constructors.mjs";
|
|
3
|
-
import { containsPositiveShift, foldInstant } from "./ast/fold.mjs";
|
|
3
|
+
import { containsPositiveShift, foldInstant, isCurrentPeriod } from "./ast/fold.mjs";
|
|
4
4
|
import { normalizeInstant, normalizeRange } from "./ast/normalize.mjs";
|
|
5
5
|
import { FilterExpressionParseError, InvalidDateFilterError } from "./filter/errors.mjs";
|
|
6
6
|
import { formatInstantExpression, parseInstantExpression } from "./filter/expression.mjs";
|
|
@@ -19,4 +19,4 @@ import { parseNatural } from "./natural/parse.mjs";
|
|
|
19
19
|
import { suggestNatural } from "./natural/suggest.mjs";
|
|
20
20
|
import { ResolutionError, ResolvedDateRange, ResolvedGreaterThan, ResolvedGreaterThanOrEqual, ResolvedLessThan, ResolvedLessThanOrEqual, ResolvedLowerBound, ResolvedUpperBound } from "./resolve/schema.mjs";
|
|
21
21
|
import { resolve } from "./resolve/resolve.mjs";
|
|
22
|
-
export { AmbiguousNaturalLanguageError, BaseLanguageContribution, BaseLanguageMetadata, Correction, DateExpressionString, DateFilter, DateLiteral, DateRangeExpr, DateRangeFromFilter, EnglishContribution, EnglishLanguage, EnglishLanguageLayer, FilterExpressionParseError, GreaterThan, GreaterThanOrEqual, InstantExpr, InstantExpressionFromString, InvalidDateFilterError, IsoDate, LanguageConflictError, LanguageContributionMetadata, LanguageExtensionContribution, LanguageExtensionMetadata, LanguageRegistrationError, LanguageRegistry, LanguageRegistryLayer, LessThan, LessThanOrEqual, Locale, LowerBound, NaturalAlternative, NaturalCandidate, NaturalCorrectionCandidate, NaturalLanguageParseError, NaturalLanguageRenderError, NaturalParseResult, NaturalSuggestion, Now, ParseQuality, ResolutionError, ResolvedDateRange, ResolvedGreaterThan, ResolvedGreaterThanOrEqual, ResolvedLessThan, ResolvedLessThanOrEqual, ResolvedLowerBound, ResolvedUpperBound, Shift, StartOf, Unit, UnsupportedLocaleError, UpperBound, boundedRange, canonicalBaseLocale, completeNaturalPhrases, completePeriod, containsPositiveShift, correctWhitespaceSeparatedText, dateLiteral, daysInMonth, defineLanguagePlugin, foldInstant, formatFilter, formatInstantExpression, formatNatural, greaterThan, greaterThanOrEqual, isIsoDate, languagePluginsLayer, lessThan, lessThanOrEqual, lowerOpenRange, naturalWords, normalizeInstant, normalizeNaturalText, normalizeRange, now, parseFilter, parseInstantExpression, parseNatural, rangeKey, resolve, shift, startOf, suggestNatural, upperOpenRange };
|
|
22
|
+
export { AmbiguousNaturalLanguageError, BaseLanguageContribution, BaseLanguageMetadata, Correction, DateExpressionString, DateFilter, DateLiteral, DateRangeExpr, DateRangeFromFilter, EnglishContribution, EnglishLanguage, EnglishLanguageLayer, FilterExpressionParseError, GreaterThan, GreaterThanOrEqual, InstantExpr, InstantExpressionFromString, InvalidDateFilterError, IsoDate, LanguageConflictError, LanguageContributionMetadata, LanguageExtensionContribution, LanguageExtensionMetadata, LanguageRegistrationError, LanguageRegistry, LanguageRegistryLayer, LessThan, LessThanOrEqual, Locale, LowerBound, NaturalAlternative, NaturalCandidate, NaturalCorrectionCandidate, NaturalLanguageParseError, NaturalLanguageRenderError, NaturalParseResult, NaturalSuggestion, Now, ParseQuality, ResolutionError, ResolvedDateRange, ResolvedGreaterThan, ResolvedGreaterThanOrEqual, ResolvedLessThan, ResolvedLessThanOrEqual, ResolvedLowerBound, ResolvedUpperBound, Shift, StartOf, Unit, UnsupportedLocaleError, UpperBound, boundedRange, canonicalBaseLocale, completeNaturalPhrases, completePeriod, containsPositiveShift, correctWhitespaceSeparatedText, dateLiteral, daysInMonth, defineLanguagePlugin, foldInstant, formatFilter, formatInstantExpression, formatNatural, greaterThan, greaterThanOrEqual, isCurrentPeriod, isIsoDate, languagePluginsLayer, lessThan, lessThanOrEqual, lowerOpenRange, naturalWords, normalizeInstant, normalizeNaturalText, normalizeRange, now, parseFilter, parseInstantExpression, parseNatural, rangeKey, resolve, shift, startOf, suggestNatural, upperOpenRange };
|
|
@@ -22,8 +22,37 @@ declare const NaturalCorrectionCandidate: Schema.Struct<{
|
|
|
22
22
|
readonly cost: Schema.Int;
|
|
23
23
|
}>;
|
|
24
24
|
type NaturalCorrectionCandidate = typeof NaturalCorrectionCandidate.Type;
|
|
25
|
-
declare const
|
|
25
|
+
declare const NaturalCandidate: Schema.Struct<{
|
|
26
|
+
readonly range: Schema.Union<readonly [Schema.TaggedStruct<"DateRange", {
|
|
27
|
+
readonly lower: Schema.Union<readonly [Schema.TaggedStruct<"GreaterThan", {
|
|
28
|
+
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
29
|
+
}>, Schema.TaggedStruct<"GreaterThanOrEqual", {
|
|
30
|
+
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
31
|
+
}>]>;
|
|
32
|
+
readonly upper: Schema.Union<readonly [Schema.TaggedStruct<"LessThan", {
|
|
33
|
+
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
34
|
+
}>, Schema.TaggedStruct<"LessThanOrEqual", {
|
|
35
|
+
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
36
|
+
}>]>;
|
|
37
|
+
}>, Schema.TaggedStruct<"DateRange", {
|
|
38
|
+
readonly lower: Schema.Union<readonly [Schema.TaggedStruct<"GreaterThan", {
|
|
39
|
+
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
40
|
+
}>, Schema.TaggedStruct<"GreaterThanOrEqual", {
|
|
41
|
+
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
42
|
+
}>]>;
|
|
43
|
+
readonly upper: Schema.optionalKey<Schema.Never>;
|
|
44
|
+
}>, Schema.TaggedStruct<"DateRange", {
|
|
45
|
+
readonly lower: Schema.optionalKey<Schema.Never>;
|
|
46
|
+
readonly upper: Schema.Union<readonly [Schema.TaggedStruct<"LessThan", {
|
|
47
|
+
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
48
|
+
}>, Schema.TaggedStruct<"LessThanOrEqual", {
|
|
49
|
+
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
50
|
+
}>]>;
|
|
51
|
+
}>]>;
|
|
26
52
|
readonly canonical: Schema.String;
|
|
53
|
+
}>;
|
|
54
|
+
type NaturalCandidate = typeof NaturalCandidate.Type;
|
|
55
|
+
declare const NaturalAlternative: Schema.Struct<{
|
|
27
56
|
readonly range: Schema.Union<readonly [Schema.TaggedStruct<"DateRange", {
|
|
28
57
|
readonly lower: Schema.Union<readonly [Schema.TaggedStruct<"GreaterThan", {
|
|
29
58
|
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
@@ -50,8 +79,9 @@ declare const NaturalAlternative: Schema.Struct<{
|
|
|
50
79
|
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
51
80
|
}>]>;
|
|
52
81
|
}>]>;
|
|
82
|
+
readonly canonical: Schema.String;
|
|
53
83
|
}>;
|
|
54
|
-
type NaturalAlternative =
|
|
84
|
+
type NaturalAlternative = NaturalCandidate;
|
|
55
85
|
declare const NaturalParseResult: Schema.Struct<{
|
|
56
86
|
readonly range: Schema.Union<readonly [Schema.TaggedStruct<"DateRange", {
|
|
57
87
|
readonly lower: Schema.Union<readonly [Schema.TaggedStruct<"GreaterThan", {
|
|
@@ -87,7 +117,6 @@ declare const NaturalParseResult: Schema.Struct<{
|
|
|
87
117
|
readonly offset: Schema.Int;
|
|
88
118
|
}>>;
|
|
89
119
|
readonly alternatives: Schema.$Array<Schema.Struct<{
|
|
90
|
-
readonly canonical: Schema.String;
|
|
91
120
|
readonly range: Schema.Union<readonly [Schema.TaggedStruct<"DateRange", {
|
|
92
121
|
readonly lower: Schema.Union<readonly [Schema.TaggedStruct<"GreaterThan", {
|
|
93
122
|
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
@@ -114,39 +143,10 @@ declare const NaturalParseResult: Schema.Struct<{
|
|
|
114
143
|
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
115
144
|
}>]>;
|
|
116
145
|
}>]>;
|
|
146
|
+
readonly canonical: Schema.String;
|
|
117
147
|
}>>;
|
|
118
148
|
}>;
|
|
119
149
|
type NaturalParseResult = typeof NaturalParseResult.Type;
|
|
120
|
-
declare const NaturalCandidate: Schema.Struct<{
|
|
121
|
-
readonly range: Schema.Union<readonly [Schema.TaggedStruct<"DateRange", {
|
|
122
|
-
readonly lower: Schema.Union<readonly [Schema.TaggedStruct<"GreaterThan", {
|
|
123
|
-
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
124
|
-
}>, Schema.TaggedStruct<"GreaterThanOrEqual", {
|
|
125
|
-
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
126
|
-
}>]>;
|
|
127
|
-
readonly upper: Schema.Union<readonly [Schema.TaggedStruct<"LessThan", {
|
|
128
|
-
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
129
|
-
}>, Schema.TaggedStruct<"LessThanOrEqual", {
|
|
130
|
-
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
131
|
-
}>]>;
|
|
132
|
-
}>, Schema.TaggedStruct<"DateRange", {
|
|
133
|
-
readonly lower: Schema.Union<readonly [Schema.TaggedStruct<"GreaterThan", {
|
|
134
|
-
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
135
|
-
}>, Schema.TaggedStruct<"GreaterThanOrEqual", {
|
|
136
|
-
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
137
|
-
}>]>;
|
|
138
|
-
readonly upper: Schema.optionalKey<Schema.Never>;
|
|
139
|
-
}>, Schema.TaggedStruct<"DateRange", {
|
|
140
|
-
readonly lower: Schema.optionalKey<Schema.Never>;
|
|
141
|
-
readonly upper: Schema.Union<readonly [Schema.TaggedStruct<"LessThan", {
|
|
142
|
-
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
143
|
-
}>, Schema.TaggedStruct<"LessThanOrEqual", {
|
|
144
|
-
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
145
|
-
}>]>;
|
|
146
|
-
}>]>;
|
|
147
|
-
readonly canonical: Schema.String;
|
|
148
|
-
}>;
|
|
149
|
-
type NaturalCandidate = typeof NaturalCandidate.Type;
|
|
150
150
|
declare const NaturalSuggestion: Schema.Struct<{
|
|
151
151
|
readonly text: Schema.String;
|
|
152
152
|
readonly range: Schema.Union<readonly [Schema.TaggedStruct<"DateRange", {
|
package/dist/language/model.mjs
CHANGED
|
@@ -17,20 +17,17 @@ const NaturalCorrectionCandidate = Schema.Struct({
|
|
|
17
17
|
corrections: Schema.Array(Correction),
|
|
18
18
|
cost: Schema.Int.check(Schema.isGreaterThanOrEqualTo(1))
|
|
19
19
|
});
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
const NaturalCandidate = Schema.Struct({
|
|
21
|
+
range: DateRangeExpr,
|
|
22
|
+
canonical: Schema.String
|
|
23
23
|
});
|
|
24
|
+
const NaturalAlternative = NaturalCandidate;
|
|
24
25
|
const NaturalParseResult = Schema.Struct({
|
|
25
26
|
range: DateRangeExpr,
|
|
26
27
|
quality: ParseQuality,
|
|
27
28
|
corrections: Schema.Array(Correction),
|
|
28
29
|
alternatives: Schema.Array(NaturalAlternative)
|
|
29
30
|
});
|
|
30
|
-
const NaturalCandidate = Schema.Struct({
|
|
31
|
-
range: DateRangeExpr,
|
|
32
|
-
canonical: Schema.String
|
|
33
|
-
});
|
|
34
31
|
const NaturalSuggestion = Schema.Struct({
|
|
35
32
|
text: Schema.String,
|
|
36
33
|
range: DateRangeExpr
|
|
@@ -38,7 +35,7 @@ const NaturalSuggestion = Schema.Struct({
|
|
|
38
35
|
var BaseLanguageContribution = class extends Data.TaggedClass("BaseLanguage") {};
|
|
39
36
|
var LanguageExtensionContribution = class extends Data.TaggedClass("LanguageExtension") {};
|
|
40
37
|
const canonicalBaseLocale = (input) => Result.getSuccess(Result.try(() => new Intl.Locale(input).baseName));
|
|
41
|
-
const Locale = Schema.String.check(Schema.makeFilter((value) => Option.contains(canonicalBaseLocale(value), value), { expected: "a
|
|
38
|
+
const Locale = Schema.String.check(Schema.makeFilter((value) => Option.contains(canonicalBaseLocale(value), value), { expected: "a standard BCP 47 language code" })).annotate({ identifier: "Locale" });
|
|
42
39
|
const BaseLanguageMetadata = Schema.TaggedStruct("BaseLanguage", {
|
|
43
40
|
locale: Locale,
|
|
44
41
|
vocabulary: Schema.Array(Schema.String)
|
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
import { LanguageConflictError, LanguageRegistrationError, UnsupportedLocaleError } from "./errors.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { LanguageContributionMetadata, canonicalBaseLocale } from "./model.mjs";
|
|
3
3
|
import { normalizeNaturalText } from "../natural/text.mjs";
|
|
4
4
|
import { Array, Context, Effect, Layer, Match, Option, Order, Ref, Result, Schema } from "effect";
|
|
5
5
|
//#region src/language/registry.ts
|
|
6
6
|
var LanguageRegistry = class extends Context.Service()("chronolizer/LanguageRegistry") {};
|
|
7
|
-
const metadataOf = (contribution) => Match.valueTags(contribution, {
|
|
8
|
-
BaseLanguage: (base) => BaseLanguageMetadata.make({
|
|
9
|
-
locale: base.locale,
|
|
10
|
-
vocabulary: base.vocabulary
|
|
11
|
-
}),
|
|
12
|
-
LanguageExtension: (extension) => LanguageExtensionMetadata.make({
|
|
13
|
-
locale: extension.locale,
|
|
14
|
-
priority: extension.priority,
|
|
15
|
-
vocabulary: extension.vocabulary
|
|
16
|
-
})
|
|
17
|
-
});
|
|
18
7
|
const localeCandidates = (locale) => {
|
|
19
8
|
const candidates = [locale];
|
|
20
9
|
let parent = locale;
|
|
@@ -44,14 +33,7 @@ const compileLanguage = (locale, registered) => {
|
|
|
44
33
|
const vocabulary = Object.freeze(Array.dedupe([...base.contribution.vocabulary, ...Array.flatMap(extensions, (entry) => entry.contribution.vocabulary)]));
|
|
45
34
|
const parsers = Object.freeze([...extensions.map((entry) => entry.contribution.parseExact), base.contribution.parseExact]);
|
|
46
35
|
const suggesters = Object.freeze([...extensions.map((entry) => entry.contribution.suggest), base.contribution.suggest].filter((suggest) => suggest !== void 0));
|
|
47
|
-
const parseExact = (input) =>
|
|
48
|
-
const candidates = [];
|
|
49
|
-
for (const parser of parsers) {
|
|
50
|
-
const candidate = parser(input);
|
|
51
|
-
if (Option.isSome(candidate)) candidates.push(candidate.value);
|
|
52
|
-
}
|
|
53
|
-
return candidates;
|
|
54
|
-
};
|
|
36
|
+
const parseExact = (input) => Array.flatMap(parsers, (parser) => Option.toArray(parser(input)));
|
|
55
37
|
return Option.some(Object.freeze({
|
|
56
38
|
locale: base.contribution.locale,
|
|
57
39
|
vocabulary,
|
|
@@ -63,28 +45,34 @@ const compileLanguage = (locale, registered) => {
|
|
|
63
45
|
}));
|
|
64
46
|
};
|
|
65
47
|
const createRegistry = Effect.fn(function* () {
|
|
66
|
-
const
|
|
48
|
+
const state = yield* Ref.make({
|
|
49
|
+
entries: [],
|
|
50
|
+
compiledLanguages: /* @__PURE__ */ new Map()
|
|
51
|
+
});
|
|
67
52
|
const register = Effect.fn(function* (pluginId, contribution) {
|
|
68
|
-
if (pluginId.length === 0 || !Schema.is(LanguageContributionMetadata)(
|
|
53
|
+
if (pluginId.length === 0 || !Schema.is(LanguageContributionMetadata)(contribution)) return yield* new LanguageRegistrationError({
|
|
69
54
|
pluginId,
|
|
70
55
|
locale: contribution.locale,
|
|
71
|
-
message: "
|
|
56
|
+
message: "The plugin name or language settings are invalid"
|
|
72
57
|
});
|
|
73
58
|
const token = Symbol(pluginId);
|
|
74
|
-
yield* Effect.acquireRelease(Ref.modify(
|
|
59
|
+
yield* Effect.acquireRelease(Ref.modify(state, (current) => {
|
|
75
60
|
const conflictingBase = Match.valueTags(contribution, {
|
|
76
|
-
BaseLanguage: (base) => Array.findFirst(current, (entry) => Match.valueTags(entry.contribution, {
|
|
61
|
+
BaseLanguage: (base) => Array.findFirst(current.entries, (entry) => Match.valueTags(entry.contribution, {
|
|
77
62
|
BaseLanguage: (registeredBase) => registeredBase.locale === base.locale,
|
|
78
63
|
LanguageExtension: () => false
|
|
79
64
|
})),
|
|
80
65
|
LanguageExtension: () => Option.none()
|
|
81
66
|
});
|
|
82
67
|
return Option.match(conflictingBase, {
|
|
83
|
-
onNone: () => [Result.succeed(token),
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
68
|
+
onNone: () => [Result.succeed(token), {
|
|
69
|
+
entries: Array.append(current.entries, {
|
|
70
|
+
token,
|
|
71
|
+
pluginId,
|
|
72
|
+
contribution
|
|
73
|
+
}),
|
|
74
|
+
compiledLanguages: /* @__PURE__ */ new Map()
|
|
75
|
+
}],
|
|
88
76
|
onSome: (conflict) => [Result.fail(new LanguageConflictError({
|
|
89
77
|
locale: contribution.locale,
|
|
90
78
|
firstPluginId: conflict.pluginId,
|
|
@@ -92,12 +80,26 @@ const createRegistry = Effect.fn(function* () {
|
|
|
92
80
|
message: "Only one base language can be registered for a locale"
|
|
93
81
|
})), current]
|
|
94
82
|
});
|
|
95
|
-
}).pipe(Effect.flatMap((result) => Effect.fromResult(result))), (registeredToken) => Ref.update(
|
|
83
|
+
}).pipe(Effect.flatMap((result) => Effect.fromResult(result))), (registeredToken) => Ref.update(state, (current) => ({
|
|
84
|
+
entries: Array.filter(current.entries, (entry) => entry.token !== registeredToken),
|
|
85
|
+
compiledLanguages: /* @__PURE__ */ new Map()
|
|
86
|
+
})));
|
|
96
87
|
});
|
|
97
88
|
const resolve = Effect.fn(function* (locale) {
|
|
98
89
|
const canonical = canonicalBaseLocale(locale);
|
|
99
90
|
if (Option.isSome(canonical)) {
|
|
100
|
-
const compiled =
|
|
91
|
+
const compiled = yield* Ref.modify(state, (current) => {
|
|
92
|
+
const cached = current.compiledLanguages.get(canonical.value);
|
|
93
|
+
if (cached !== void 0) return [Option.some(cached), current];
|
|
94
|
+
const language = compileLanguage(canonical.value, current.entries);
|
|
95
|
+
if (Option.isNone(language)) return [language, current];
|
|
96
|
+
const cache = new Map(current.compiledLanguages);
|
|
97
|
+
cache.set(canonical.value, language.value);
|
|
98
|
+
return [language, {
|
|
99
|
+
entries: current.entries,
|
|
100
|
+
compiledLanguages: cache
|
|
101
|
+
}];
|
|
102
|
+
});
|
|
101
103
|
if (Option.isSome(compiled)) return compiled.value;
|
|
102
104
|
}
|
|
103
105
|
return yield* new UnsupportedLocaleError({ locale });
|
|
@@ -120,7 +122,7 @@ const createPluginRegistry = Effect.fn(function* (plugins) {
|
|
|
120
122
|
if (duplicate !== void 0) return yield* new LanguageRegistrationError({
|
|
121
123
|
pluginId: duplicate,
|
|
122
124
|
locale: "*",
|
|
123
|
-
message: "Plugin
|
|
125
|
+
message: "Plugin names must be unique"
|
|
124
126
|
});
|
|
125
127
|
const registry = yield* createRegistry();
|
|
126
128
|
const context = { register: registry.register };
|