chronolizer 0.2.0 → 0.3.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.
@@ -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
- const Now = Schema.TaggedStruct("Now", {});
30
- const DateLiteral = Schema.TaggedStruct("DateLiteral", { value: IsoDate });
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
- const Shift = Schema.TaggedStruct("Shift", {
26
+ var Shift = class extends Schema.TaggedClass()("Shift", {
36
27
  base: Schema.suspend(() => InstantExpr),
37
28
  amount: ShiftAmount,
38
29
  unit: Unit
39
- });
40
- const StartOf = Schema.TaggedStruct("StartOf", {
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
- switch (symbol) {
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,
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 NaturalAlternative: Schema.Struct<{
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 = typeof NaturalAlternative.Type;
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", {
@@ -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 NaturalAlternative = Schema.Struct({
21
- canonical: Schema.String,
22
- range: DateRangeExpr
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
@@ -1,20 +1,9 @@
1
1
  import { LanguageConflictError, LanguageRegistrationError, UnsupportedLocaleError } from "./errors.mjs";
2
- import { BaseLanguageMetadata, LanguageContributionMetadata, LanguageExtensionMetadata, canonicalBaseLocale } from "./model.mjs";
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,
@@ -65,7 +47,7 @@ const compileLanguage = (locale, registered) => {
65
47
  const createRegistry = Effect.fn(function* () {
66
48
  const entries = yield* Ref.make([]);
67
49
  const register = Effect.fn(function* (pluginId, contribution) {
68
- if (pluginId.length === 0 || !Schema.is(LanguageContributionMetadata)(metadataOf(contribution))) return yield* new LanguageRegistrationError({
50
+ if (pluginId.length === 0 || !Schema.is(LanguageContributionMetadata)(contribution)) return yield* new LanguageRegistrationError({
69
51
  pluginId,
70
52
  locale: contribution.locale,
71
53
  message: "Invalid plugin identifier or contribution metadata"
@@ -4,7 +4,7 @@ import { normalizeNaturalText } from "../natural/text.mjs";
4
4
  import { defineLanguagePlugin, languagePluginsLayer } from "../language/registry.mjs";
5
5
  import { correctWhitespaceSeparatedText } from "../natural/correction.mjs";
6
6
  import { completeNaturalPhrases, fixedCalendarPeriodPhrases, naturalCount, prefixNaturalPhrases } from "../natural/suggestion.mjs";
7
- import { calendarPeriodOffset, candidate, datedPeriods, datedQuarterPeriods, fixedDatePeriod, fixedMonthPeriod, fixedQuarterPeriod, fixedYearPeriod, fromNowRange, futurePeriod, futureRange, isoDate, joinedNowCandidate, joinedPeriodCandidate, monthOfRelativeYear, namedDatePeriod, openBoundaryCandidate, parseTrailingCount, periodEndDay, periodRange, periodStartDay, periodToDateRange, periodsFromPhrases, quarterOfRelativeYear, relativePeriod, relativeWeekend, remainingPeriodRange, renderPeriodRange, textAt, trailingPeriod, trailingRange, untilNowRange, validYear } from "./shared.mjs";
7
+ import { absoluteDatePeriod, calendarPeriodOffset, candidate, currentYearDatePeriods, datedPeriods, datedQuarterPeriods, fixedDatePeriod, fixedMonthPeriod, fixedQuarterPeriod, fixedYearPeriod, fromNowRange, futurePeriod, futureRange, isoDate, joinedNowCandidate, joinedPeriodCandidate, monthOfRelativeYear, namedCurrentYearDatePeriod, namedDatePeriod, openBoundaryCandidate, parseTrailingCount, periodEndDay, periodRange, periodStartDay, periodToDateRange, periodsFromPhrases, quarterOfRelativeYear, relativePeriod, relativeWeekend, remainingPeriodRange, renderPeriodRange, textAt, trailingPeriod, trailingRange, untilNowRange, validYear } from "./shared.mjs";
8
8
  import { Effect, Option, String as String$1 } from "effect";
9
9
  //#region src/locales/cs.ts
10
10
  const months = [
@@ -85,6 +85,7 @@ const units = [
85
85
  many: "dnů",
86
86
  pastSingular: "dnem",
87
87
  pastPlural: "dny",
88
+ durationGenitive: "dne",
88
89
  current: "dnes",
89
90
  previous: "včera",
90
91
  next: "zítra",
@@ -98,6 +99,7 @@ const units = [
98
99
  many: "týdnů",
99
100
  pastSingular: "týdnem",
100
101
  pastPlural: "týdny",
102
+ durationGenitive: "týdne",
101
103
  current: "tento týden",
102
104
  previous: "minulý týden",
103
105
  next: "příští týden",
@@ -111,6 +113,7 @@ const units = [
111
113
  many: "měsíců",
112
114
  pastSingular: "měsícem",
113
115
  pastPlural: "měsíci",
116
+ durationGenitive: "měsíce",
114
117
  current: "tento měsíc",
115
118
  previous: "minulý měsíc",
116
119
  next: "příští měsíc",
@@ -124,6 +127,7 @@ const units = [
124
127
  many: "čtvrtletí",
125
128
  pastSingular: "čtvrtletím",
126
129
  pastPlural: "čtvrtletími",
130
+ durationGenitive: "čtvrtletí",
127
131
  current: "toto čtvrtletí",
128
132
  previous: "minulé čtvrtletí",
129
133
  next: "příští čtvrtletí",
@@ -137,6 +141,7 @@ const units = [
137
141
  many: "let",
138
142
  pastSingular: "rokem",
139
143
  pastPlural: "lety",
144
+ durationGenitive: "roku",
140
145
  current: "tento rok",
141
146
  previous: "minulý rok",
142
147
  next: "příští rok",
@@ -144,7 +149,6 @@ const units = [
144
149
  remaining: "zbytek roku"
145
150
  }
146
151
  ];
147
- const title = (value) => `${value.slice(0, 1).toLocaleUpperCase("cs")}${value.slice(1)}`;
148
152
  const unitAliases = [
149
153
  ["den", "day"],
150
154
  ["dny", "day"],
@@ -360,17 +364,22 @@ const monthNumber = (value) => {
360
364
  return short === -1 ? void 0 : short + 1;
361
365
  };
362
366
  const dateLabel = (day, month, year) => `${day}. ${textAt(monthGenitives, month - 1)} ${year}`;
367
+ const currentDateLabel = (day, month) => `${day}. ${textAt(monthGenitives, month - 1)}`;
363
368
  const parseNamedDate = (input) => {
364
369
  const named = String$1.match(/^([0-3]?\d)\.?(?: )([a-záčďéěíňóřšťúůýž]+\.?) (\d{4})$/u)(input);
365
370
  if (Option.isSome(named)) return namedDatePeriod(textAt(named.value, 3), textAt(named.value, 2), textAt(named.value, 1), monthNumber, dateLabel);
366
371
  const numeric = String$1.match(/^([0-3]?\d)(?:\. ?|[/-])([01]?\d)(?:\. ?|[/-])(\d{4})$/u)(input);
367
- if (Option.isNone(numeric)) return Option.none();
368
- const year = validYear(textAt(numeric.value, 3));
369
- const month = Number(textAt(numeric.value, 2));
370
- const day = Number(textAt(numeric.value, 1));
371
- if (year === void 0 || month < 1 || month > 12) return Option.none();
372
- const value = isoDate(year, month, day);
373
- return isIsoDate(value) && value !== "9999-12-31" ? Option.some(fixedDatePeriod(value, dateLabel(day, month, year))) : Option.none();
372
+ if (Option.isSome(numeric)) {
373
+ const year = validYear(textAt(numeric.value, 3));
374
+ const month = Number(textAt(numeric.value, 2));
375
+ const day = Number(textAt(numeric.value, 1));
376
+ if (year !== void 0 && month >= 1 && month <= 12) {
377
+ const value = isoDate(year, month, day);
378
+ if (isIsoDate(value) && value !== "9999-12-31") return Option.some(fixedDatePeriod(value, dateLabel(day, month, year)));
379
+ }
380
+ }
381
+ const current = String$1.match(/^([0-3]?\d)\.? ([a-záčďéěíňóřšťúůýž]+\.?)$/u)(input);
382
+ return Option.isSome(current) ? namedCurrentYearDatePeriod(textAt(current.value, 2), textAt(current.value, 1), monthNumber, currentDateLabel) : Option.none();
374
383
  };
375
384
  const quarterNumber = (value) => {
376
385
  if (value.startsWith("q") && value.length === 2) return Number(value.slice(1));
@@ -398,7 +407,8 @@ const parseQuarter = (input) => {
398
407
  return quarter === void 0 ? Option.none() : Option.some(quarterOfRelativeYear(quarter, 0, `Q${quarter}`));
399
408
  };
400
409
  const parseBasePeriod = (input) => {
401
- if (isIsoDate(input) && input !== "9999-12-31") return Option.some(fixedDatePeriod(input, input));
410
+ const absoluteDate = absoluteDatePeriod(input, "cs");
411
+ if (Option.isSome(absoluteDate)) return absoluteDate;
402
412
  const namedDate = parseNamedDate(input);
403
413
  if (Option.isSome(namedDate)) return namedDate;
404
414
  const quarter = parseQuarter(input);
@@ -412,7 +422,7 @@ const parseBasePeriod = (input) => {
412
422
  if (Option.isSome(monthYear)) {
413
423
  const month = monthNumber(textAt(monthYear.value, 1));
414
424
  const year = validYear(textAt(monthYear.value, 2));
415
- if (month !== void 0 && year !== void 0) return Option.some(fixedMonthPeriod(year, month, `${title(textAt(months, month - 1))} ${year}`));
425
+ if (month !== void 0 && year !== void 0) return Option.some(fixedMonthPeriod(year, month, `${textAt(months, month - 1)} ${year}`));
416
426
  }
417
427
  const relativeMonth = String$1.match(/^([a-záčďéěíňóřšťúůýž]+\.?) (minulého roku|příštího roku|tohoto roku)$/u)(input);
418
428
  const relativeMonthYearFirst = String$1.match(/^(minulého roku|příštího roku|tohoto roku) ([a-záčďéěíňóřšťúůýž]+\.?)$/u)(input);
@@ -423,10 +433,10 @@ const parseBasePeriod = (input) => {
423
433
  const yearText = textAt(relativeMatch.value, yearFirst ? 1 : 2);
424
434
  const month = monthNumber(monthText);
425
435
  const direction = relativeYearDirection(yearText);
426
- if (month !== void 0) return Option.some(monthOfRelativeYear(month, direction, `${title(textAt(months, month - 1))} ${relativeYearName(direction)}`));
436
+ if (month !== void 0) return Option.some(monthOfRelativeYear(month, direction, `${textAt(months, month - 1)} ${relativeYearName(direction)}`));
427
437
  }
428
438
  const standaloneMonth = monthNumber(input);
429
- if (standaloneMonth !== void 0) return Option.some(monthOfRelativeYear(standaloneMonth, 0, title(textAt(months, standaloneMonth - 1))));
439
+ if (standaloneMonth !== void 0) return Option.some(monthOfRelativeYear(standaloneMonth, 0, textAt(months, standaloneMonth - 1)));
430
440
  const alias = periodAliases.find((entry) => entry[0] === input);
431
441
  if (alias !== void 0) return Option.some(relativePeriod(alias[1], alias[2], alias[3]));
432
442
  if (["víkend", "tento víkend"].includes(input)) return Option.some(relativeWeekend(0, "tento víkend"));
@@ -464,6 +474,24 @@ const rollingFuturePatterns = [countedPattern("^(?:příští|následující) ([
464
474
  const rollingSincePattern = countedPattern("^za poslední ([1-9]\\d*) (UNIT)$");
465
475
  const rollingBarePattern = countedPattern("^([1-9]\\d*) (UNIT)$");
466
476
  const firstPatternMatch = (input, patterns) => Option.firstSomeOf(patterns.map((pattern) => String$1.match(pattern)(input)));
477
+ const singularRollingPhrases = units.flatMap((entry) => [
478
+ {
479
+ phrase: `poslední ${entry.singular}`,
480
+ entry,
481
+ future: false
482
+ },
483
+ {
484
+ phrase: `během posledního ${entry.durationGenitive}`,
485
+ entry,
486
+ future: false
487
+ },
488
+ {
489
+ phrase: `během následujícího ${entry.durationGenitive}`,
490
+ entry,
491
+ future: true
492
+ }
493
+ ]);
494
+ const singularRollingCanonical = (entry, future) => `během ${future ? "následujícího" : "posledního"} ${entry.durationGenitive}`;
467
495
  const countNoun = (amount, entry) => {
468
496
  if (amount === 1) return entry.singular;
469
497
  const lastTwo = amount % 100;
@@ -487,6 +515,11 @@ const parseCalendarOffset = (input) => {
487
515
  return Option.some(candidate(periodRange(relativePeriod(unit, direction, canonical)), canonical));
488
516
  };
489
517
  const parseRollingPeriod = (input) => {
518
+ const singular = singularRollingPhrases.find((entry) => entry.phrase === input);
519
+ if (singular !== void 0) {
520
+ const range = singular.future ? futureRange(1, singular.entry.unit) : trailingRange(1, singular.entry.unit);
521
+ return Option.some(candidate(range, singularRollingCanonical(singular.entry, singular.future)));
522
+ }
490
523
  const since = String$1.match(rollingSincePattern)(input);
491
524
  const past = firstPatternMatch(input, rollingPastPatterns);
492
525
  const bare = String$1.match(rollingBarePattern)(input);
@@ -505,9 +538,22 @@ const parseRollingPeriod = (input) => {
505
538
  if (entry === void 0) return Option.none();
506
539
  const isFuture = Option.isSome(future);
507
540
  const range = isFuture ? futureRange(amount.value, unit) : trailingRange(amount.value, unit);
541
+ if (amount.value === 1) return Option.some(candidate(range, singularRollingCanonical(entry, isFuture)));
508
542
  const modifier = isFuture ? "příští" : "poslední";
509
543
  return Option.some(candidate(range, `${modifier} ${amount.value} ${countNoun(amount.value, entry)}`));
510
544
  };
545
+ const parseElidedDateRange = (input) => {
546
+ const joined = String$1.match(/^od ([0-3]?\d)\.?(?: do) ([0-3]?\d)\.? ([a-záčďéěíňóřšťúůýž]+\.?)(?: (\d{4}))?$/u)(input);
547
+ const dashed = String$1.match(/^([0-3]?\d)\.?[–—-]([0-3]?\d)\.? ([a-záčďéěíňóřšťúůýž]+\.?)(?: (\d{4}))?$/u)(input);
548
+ const match = Option.firstSomeOf([joined, dashed]);
549
+ if (Option.isNone(match)) return Option.none();
550
+ const lowerDay = textAt(match.value, 1);
551
+ const upperDay = textAt(match.value, 2);
552
+ const month = textAt(match.value, 3);
553
+ const year = textAt(match.value, 4);
554
+ const suffix = year.length === 0 ? "" : ` ${year}`;
555
+ return joinedPeriodCandidate(`od ${lowerDay}. ${month}${suffix} do ${upperDay}. ${month}${suffix}`, [["od ", " do "]], parsePeriod, (lower, upper) => `od ${lower} do ${upper} včetně`);
556
+ };
511
557
  const boundaryCandidate = (input) => {
512
558
  const included = String$1.match(/^do (.+) včetně$/u)(input);
513
559
  if (Option.isSome(included)) return openBoundaryCandidate(`do ${textAt(included.value, 1)}`, [["do ", "through"]], parsePeriod);
@@ -539,6 +585,8 @@ const parseCzech = (input) => {
539
585
  if (Option.isSome(rolling)) return rolling;
540
586
  const toDate = toDatePhrases.find((entry) => entry.phrase === input);
541
587
  if (toDate !== void 0) return Option.some(candidate(periodToDateRange(toDate.entry.unit), toDate.entry.toDate));
588
+ const elided = parseElidedDateRange(input);
589
+ if (Option.isSome(elided)) return elided;
542
590
  const nowBounded = joinedNowCandidate(input, [["od ", " do dneška"], ["mezi ", " a dneškem"]], ["od dneška do ", "mezi dneškem a "], parsePeriod, (period) => `od ${period} do dneška`, (period) => `od dneška do ${period}`);
543
591
  if (Option.isSome(nowBounded)) return nowBounded;
544
592
  const bounded = joinedPeriodCandidate(input, [
@@ -551,7 +599,7 @@ const parseCzech = (input) => {
551
599
  if (Option.isSome(bounded)) return bounded;
552
600
  const boundary = boundaryCandidate(input);
553
601
  if (Option.isSome(boundary)) return boundary;
554
- return Option.map(parsePeriod(input), (period) => candidate(periodRange(period), period.canonical));
602
+ return parsePeriod(input).pipe(Option.map((period) => candidate(periodRange(period), period.canonical)));
555
603
  };
556
604
  const staticPeriodPhrases = [
557
605
  ...periodAliases.map((entry) => entry[0]),
@@ -606,6 +654,7 @@ const countedSuggestions = (input) => {
606
654
  const czechSuggestionPhrases = [
607
655
  ...units.map((entry) => entry.toDate),
608
656
  ...units.map((entry) => entry.remaining),
657
+ ...singularRollingPhrases.map((entry) => entry.phrase),
609
658
  ...staticPeriodPhrases,
610
659
  ...prefixNaturalPhrases(staticPeriodPhrases, boundaryPrefixes),
611
660
  "dosud",
@@ -634,14 +683,18 @@ const renderCzech = (range) => {
634
683
  const future = futurePeriod(range);
635
684
  if (Option.isSome(future)) {
636
685
  const entry = units.find((unit) => unit.unit === future.value.unit);
637
- if (entry !== void 0) return Option.some(`příští ${future.value.amount} ${countNoun(future.value.amount, entry)}`);
686
+ if (entry !== void 0) return Option.some(future.value.amount === 1 ? singularRollingCanonical(entry, true) : `příští ${future.value.amount} ${countNoun(future.value.amount, entry)}`);
638
687
  }
639
688
  const trailing = trailingPeriod(range);
640
689
  if (Option.isSome(trailing)) {
641
690
  const entry = units.find((unit) => unit.unit === trailing.value.unit);
642
- if (entry !== void 0) return Option.some(`poslední ${trailing.value.amount} ${countNoun(trailing.value.amount, entry)}`);
691
+ if (entry !== void 0) return Option.some(trailing.value.amount === 1 ? singularRollingCanonical(entry, false) : `poslední ${trailing.value.amount} ${countNoun(trailing.value.amount, entry)}`);
643
692
  }
644
- const periods = [...staticPeriods, ...periodsFromPhrases([...datedPeriods(range, months), ...datedQuarterPeriods(range)], parsePeriod)];
693
+ const periods = [
694
+ ...staticPeriods,
695
+ ...currentYearDatePeriods(range, currentDateLabel),
696
+ ...periodsFromPhrases([...datedPeriods(range, months), ...datedQuarterPeriods(range)], parsePeriod)
697
+ ];
645
698
  return renderPeriodRange(range, [...units.map((entry) => candidate(periodToDateRange(entry.unit), entry.toDate)), ...units.map((entry) => candidate(remainingPeriodRange(entry.unit), entry.remaining))], periods, (period) => `od ${period}`, (period) => `před ${period}`, (period) => `do ${period} včetně`, (period) => `po ${period}`, (lower, upper) => `od ${lower} do ${upper} včetně`, (period) => `od ${period} do dneška`, (period) => `od dneška do ${period}`, () => "dosud", () => "od nynějška");
646
699
  };
647
700
  const CzechContribution = new BaseLanguageContribution({