chronolizer 0.1.0 → 0.2.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.
Files changed (62) hide show
  1. package/README.md +70 -7
  2. package/dist/ast/constructors.d.mts +140 -0
  3. package/dist/ast/constructors.mjs +25 -0
  4. package/dist/ast/fold.d.mts +12 -0
  5. package/dist/ast/fold.mjs +51 -0
  6. package/dist/ast/normalize.d.mts +42 -0
  7. package/dist/ast/normalize.mjs +32 -0
  8. package/dist/ast/schemas.d.mts +87 -0
  9. package/dist/ast/schemas.mjs +74 -0
  10. package/dist/filter/codec.d.mts +92 -0
  11. package/dist/filter/codec.mjs +48 -0
  12. package/dist/filter/errors.d.mts +14 -0
  13. package/dist/filter/errors.mjs +10 -0
  14. package/dist/filter/expression.d.mts +8 -0
  15. package/dist/filter/expression.mjs +93 -0
  16. package/dist/filter/schema.d.mts +13 -0
  17. package/dist/filter/schema.mjs +11 -0
  18. package/dist/filter/transformation.d.mts +37 -0
  19. package/dist/filter/transformation.mjs +20 -0
  20. package/dist/index.d.mts +22 -969
  21. package/dist/index.mjs +22 -2305
  22. package/dist/language/errors.d.mts +38 -0
  23. package/dist/language/errors.mjs +30 -0
  24. package/dist/language/model.d.mts +239 -0
  25. package/dist/language/model.mjs +53 -0
  26. package/dist/language/registry.d.mts +17 -0
  27. package/dist/language/registry.mjs +134 -0
  28. package/dist/locales/cs.d.mts +10 -0
  29. package/dist/locales/cs.mjs +693 -0
  30. package/dist/locales/de.d.mts +10 -0
  31. package/dist/locales/de.mjs +828 -0
  32. package/dist/locales/en.d.mts +10 -0
  33. package/dist/locales/en.mjs +550 -0
  34. package/dist/locales/es.d.mts +10 -0
  35. package/dist/locales/es.mjs +660 -0
  36. package/dist/locales/fr.d.mts +10 -0
  37. package/dist/locales/fr.mjs +737 -0
  38. package/dist/locales/nl.d.mts +10 -0
  39. package/dist/locales/nl.mjs +652 -0
  40. package/dist/locales/pl.d.mts +10 -0
  41. package/dist/locales/pl.mjs +711 -0
  42. package/dist/locales/shared.mjs +289 -0
  43. package/dist/locales/tr.d.mts +10 -0
  44. package/dist/locales/tr.mjs +660 -0
  45. package/dist/natural/correction.d.mts +13 -0
  46. package/dist/natural/correction.mjs +73 -0
  47. package/dist/natural/format.d.mts +47 -0
  48. package/dist/natural/format.mjs +14 -0
  49. package/dist/natural/parse.d.mts +99 -0
  50. package/dist/natural/parse.mjs +66 -0
  51. package/dist/natural/policy.mjs +6 -0
  52. package/dist/natural/suggest.d.mts +53 -0
  53. package/dist/natural/suggest.mjs +24 -0
  54. package/dist/natural/suggestion.d.mts +4 -0
  55. package/dist/natural/suggestion.mjs +79 -0
  56. package/dist/natural/text.d.mts +5 -0
  57. package/dist/natural/text.mjs +5 -0
  58. package/dist/resolve/resolve.d.mts +79 -0
  59. package/dist/resolve/resolve.mjs +66 -0
  60. package/dist/resolve/schema.d.mts +59 -0
  61. package/dist/resolve/schema.mjs +28 -0
  62. package/package.json +15 -3
@@ -0,0 +1,289 @@
1
+ import { InstantExpr, Unit, daysInMonth, isIsoDate } from "../ast/schemas.mjs";
2
+ import { boundedRange, dateLiteral, greaterThanOrEqual, lessThan, lessThanOrEqual, lowerOpenRange, now, shift, startOf, upperOpenRange } from "../ast/constructors.mjs";
3
+ import { formatInstantExpression } from "../filter/expression.mjs";
4
+ import { formatFilter, rangeKey } from "../filter/codec.mjs";
5
+ import { NaturalCandidate } from "../language/model.mjs";
6
+ import { Option, Schema, String as String$1 } from "effect";
7
+ //#region src/locales/shared.ts
8
+ const textAt = (values, index) => values[index] ?? "";
9
+ const Period = Schema.Struct({
10
+ start: InstantExpr,
11
+ end: InstantExpr,
12
+ canonical: Schema.String
13
+ });
14
+ const periodRange = (period) => boundedRange(greaterThanOrEqual(period.start), lessThan(period.end));
15
+ const periodToDateRange = (unit) => boundedRange(greaterThanOrEqual(startOf(now(), unit)), lessThanOrEqual(now()));
16
+ const fromNowRange = () => lowerOpenRange(greaterThanOrEqual(now()));
17
+ const untilNowRange = () => upperOpenRange(lessThanOrEqual(now()));
18
+ const remainingPeriodRange = (unit) => {
19
+ const start = startOf(now(), unit);
20
+ return boundedRange(greaterThanOrEqual(now()), lessThan(shift(start, 1, unit)));
21
+ };
22
+ const periodStartDay = (period, canonical) => Period.make({
23
+ start: period.start,
24
+ end: shift(period.start, 1, "day"),
25
+ canonical
26
+ });
27
+ const periodEndDay = (period, canonical) => Period.make({
28
+ start: shift(period.end, -1, "day"),
29
+ end: period.end,
30
+ canonical
31
+ });
32
+ const relativeWeekend = (direction, canonical) => {
33
+ const weekBase = direction === 0 ? now() : shift(now(), direction, "week");
34
+ const weekStart = startOf(weekBase, "week");
35
+ const start = shift(weekStart, 5, "day");
36
+ return Period.make({
37
+ start,
38
+ end: shift(start, 2, "day"),
39
+ canonical
40
+ });
41
+ };
42
+ const TrailingCount = Schema.Int.check(Schema.isBetween({
43
+ minimum: 1,
44
+ maximum: Number.MAX_SAFE_INTEGER
45
+ }));
46
+ const TrailingPeriod = Schema.Struct({
47
+ amount: TrailingCount,
48
+ unit: Unit
49
+ });
50
+ const FuturePeriod = Schema.Struct({
51
+ amount: TrailingCount,
52
+ unit: Unit
53
+ });
54
+ const CalendarPeriodOffset = Schema.Struct({
55
+ amount: Schema.Int,
56
+ unit: Unit
57
+ });
58
+ const validYear = (value) => {
59
+ const year = Number(value);
60
+ return Number.isInteger(year) && year >= 1 && year <= 9998 ? year : void 0;
61
+ };
62
+ const parseTrailingCount = (value) => {
63
+ const amount = Number(value);
64
+ return Schema.is(TrailingCount)(amount) ? Option.some(amount) : Option.none();
65
+ };
66
+ const trailingRange = (amount, unit) => boundedRange(greaterThanOrEqual(shift(now(), -amount, unit)), lessThanOrEqual(now()));
67
+ const futureRange = (amount, unit) => boundedRange(greaterThanOrEqual(now()), lessThanOrEqual(shift(now(), amount, unit)));
68
+ const trailingPeriod = (range) => {
69
+ if (range.lower?._tag !== "GreaterThanOrEqual" || range.upper?._tag !== "LessThanOrEqual" || range.lower.value._tag !== "Shift" || range.lower.value.amount >= 0 || range.lower.value.base._tag !== "Now" || range.upper.value._tag !== "Now") return Option.none();
70
+ return Option.some(TrailingPeriod.make({
71
+ amount: -range.lower.value.amount,
72
+ unit: range.lower.value.unit
73
+ }));
74
+ };
75
+ const futurePeriod = (range) => {
76
+ if (range.lower?._tag !== "GreaterThanOrEqual" || range.upper?._tag !== "LessThanOrEqual" || range.lower.value._tag !== "Now" || range.upper.value._tag !== "Shift" || range.upper.value.amount <= 0 || range.upper.value.base._tag !== "Now") return Option.none();
77
+ return Option.some(FuturePeriod.make({
78
+ amount: range.upper.value.amount,
79
+ unit: range.upper.value.unit
80
+ }));
81
+ };
82
+ const relativePeriod = (unit, direction, canonical) => {
83
+ const base = direction === 0 ? now() : shift(now(), direction, unit);
84
+ const start = startOf(base, unit);
85
+ return Period.make({
86
+ start,
87
+ end: shift(start, 1, unit),
88
+ canonical
89
+ });
90
+ };
91
+ const pad = (value) => String(value).padStart(2, "0");
92
+ const isoDate = (year, month, day) => `${String(year).padStart(4, "0")}-${pad(month)}-${pad(day)}`;
93
+ const nextDay = (year, month, day) => {
94
+ if (day < daysInMonth(year, month)) return isoDate(year, month, day + 1);
95
+ if (month < 12) return isoDate(year, month + 1, 1);
96
+ return isoDate(year + 1, 1, 1);
97
+ };
98
+ const previousDay = (year, month, day) => {
99
+ if (day > 1) return isoDate(year, month, day - 1);
100
+ if (month > 1) return isoDate(year, month - 1, daysInMonth(year, month - 1));
101
+ return isoDate(year - 1, 12, 31);
102
+ };
103
+ const fixedDatePeriod = (value, canonical) => {
104
+ const year = Number(value.slice(0, 4));
105
+ const month = Number(value.slice(5, 7));
106
+ const day = Number(value.slice(8, 10));
107
+ return Period.make({
108
+ start: dateLiteral(value),
109
+ end: dateLiteral(nextDay(year, month, day)),
110
+ canonical
111
+ });
112
+ };
113
+ const namedDatePeriod = (yearText, monthText, dayText, monthNumber, canonical) => {
114
+ const year = validYear(yearText);
115
+ const month = monthNumber(monthText);
116
+ const day = Number(dayText);
117
+ if (year === void 0 || month === void 0) return Option.none();
118
+ const value = isoDate(year, month, day);
119
+ if (!isIsoDate(value) || value === "9999-12-31") return Option.none();
120
+ return Option.some(fixedDatePeriod(value, canonical(day, month, year)));
121
+ };
122
+ const fixedMonthPeriod = (year, month, canonical) => {
123
+ const nextYear = month === 12 ? year + 1 : year;
124
+ const nextMonth = month === 12 ? 1 : month + 1;
125
+ return Period.make({
126
+ start: dateLiteral(isoDate(year, month, 1)),
127
+ end: dateLiteral(isoDate(nextYear, nextMonth, 1)),
128
+ canonical
129
+ });
130
+ };
131
+ const fixedYearPeriod = (year, canonical) => Period.make({
132
+ start: dateLiteral(isoDate(year, 1, 1)),
133
+ end: dateLiteral(isoDate(year + 1, 1, 1)),
134
+ canonical
135
+ });
136
+ const fixedQuarterPeriod = (year, quarter, canonical) => {
137
+ const month = (quarter - 1) * 3 + 1;
138
+ const nextYear = quarter === 4 ? year + 1 : year;
139
+ const nextMonth = quarter === 4 ? 1 : month + 3;
140
+ return Period.make({
141
+ start: dateLiteral(isoDate(year, month, 1)),
142
+ end: dateLiteral(isoDate(nextYear, nextMonth, 1)),
143
+ canonical
144
+ });
145
+ };
146
+ const quarterOfRelativeYear = (quarter, direction, canonical) => {
147
+ const yearBase = direction === 0 ? now() : shift(now(), direction, "year");
148
+ const yearStart = startOf(yearBase, "year");
149
+ const start = quarter === 1 ? yearStart : shift(yearStart, (quarter - 1) * 3, "month");
150
+ return Period.make({
151
+ start,
152
+ end: shift(start, 1, "quarter"),
153
+ canonical
154
+ });
155
+ };
156
+ const monthOfRelativeYear = (month, direction, canonical) => {
157
+ const yearBase = direction === 0 ? now() : shift(now(), direction, "year");
158
+ const yearStart = startOf(yearBase, "year");
159
+ const start = month === 1 ? yearStart : shift(yearStart, month - 1, "month");
160
+ return Period.make({
161
+ start,
162
+ end: shift(start, 1, "month"),
163
+ canonical
164
+ });
165
+ };
166
+ const calendarPeriodOffset = (range) => {
167
+ if (range.lower?._tag !== "GreaterThanOrEqual" || range.upper?._tag !== "LessThan" || range.lower.value._tag !== "StartOf" || range.lower.value.base._tag !== "Shift" || range.lower.value.base.amount === 0 || range.lower.value.base.base._tag !== "Now" || range.lower.value.unit !== range.lower.value.base.unit || range.upper.value._tag !== "Shift" || range.upper.value.amount !== 1 || range.upper.value.unit !== range.lower.value.unit || formatInstantExpression(range.upper.value.base) !== formatInstantExpression(range.lower.value)) return Option.none();
168
+ return Option.some(CalendarPeriodOffset.make({
169
+ amount: range.lower.value.base.amount,
170
+ unit: range.lower.value.unit
171
+ }));
172
+ };
173
+ const candidate = (range, canonical) => NaturalCandidate.make({
174
+ range,
175
+ canonical
176
+ });
177
+ const joinedNowCandidate = (input, periodToNow, nowToPeriod, parsePeriod, canonicalToNow, canonicalFromNow) => {
178
+ for (const [prefix, suffix] of periodToNow) {
179
+ if (!input.startsWith(prefix) || !input.endsWith(suffix)) continue;
180
+ const period = parsePeriod(input.slice(prefix.length, -suffix.length));
181
+ if (Option.isNone(period)) continue;
182
+ return Option.some(candidate(boundedRange(greaterThanOrEqual(period.value.start), lessThanOrEqual(now())), canonicalToNow(period.value.canonical)));
183
+ }
184
+ for (const prefix of nowToPeriod) {
185
+ if (!input.startsWith(prefix)) continue;
186
+ const period = parsePeriod(input.slice(prefix.length));
187
+ if (Option.isNone(period)) continue;
188
+ return Option.some(candidate(boundedRange(greaterThanOrEqual(now()), lessThan(period.value.end)), canonicalFromNow(period.value.canonical)));
189
+ }
190
+ return Option.none();
191
+ };
192
+ const joinedPeriodCandidate = (input, joins, parsePeriod, canonical) => {
193
+ for (const [prefix, separator] of joins) {
194
+ if (!input.startsWith(prefix)) continue;
195
+ const separatorIndex = input.indexOf(separator, prefix.length);
196
+ if (separatorIndex === -1) continue;
197
+ const lower = parsePeriod(input.slice(prefix.length, separatorIndex));
198
+ const upper = parsePeriod(input.slice(separatorIndex + separator.length));
199
+ if (Option.isNone(lower) || Option.isNone(upper)) continue;
200
+ return Option.some(candidate(boundedRange(greaterThanOrEqual(lower.value.start), lessThan(upper.value.end)), canonical(lower.value.canonical, upper.value.canonical)));
201
+ }
202
+ return Option.none();
203
+ };
204
+ const periodBoundaryCandidate = (period, boundary, canonical) => {
205
+ switch (boundary) {
206
+ case "since": return candidate(lowerOpenRange(greaterThanOrEqual(period.start)), canonical);
207
+ case "before": return candidate(upperOpenRange(lessThan(period.start)), canonical);
208
+ case "through": return candidate(upperOpenRange(lessThan(period.end)), canonical);
209
+ case "after": return candidate(lowerOpenRange(greaterThanOrEqual(period.end)), canonical);
210
+ }
211
+ };
212
+ const openBoundaryCandidate = (input, boundaries, parsePeriod) => {
213
+ for (const boundary of boundaries) {
214
+ if (!input.startsWith(boundary[0])) continue;
215
+ const period = parsePeriod(input.slice(boundary[0].length));
216
+ if (Option.isNone(period)) continue;
217
+ const canonical = `${boundary[0]}${period.value.canonical}`;
218
+ return Option.some(periodBoundaryCandidate(period.value, boundary[1], canonical));
219
+ }
220
+ return Option.none();
221
+ };
222
+ const expressionDates = (range) => {
223
+ const filter = formatFilter(range);
224
+ const expressions = [
225
+ filter.gt,
226
+ filter.gte,
227
+ filter.lt,
228
+ filter.lte
229
+ ];
230
+ const dates = /* @__PURE__ */ new Set();
231
+ for (const expression of expressions) {
232
+ if (expression === void 0) continue;
233
+ const match = String$1.match(/^(\d{4})-(\d{2})-(\d{2})/u)(expression);
234
+ if (Option.isSome(match)) dates.add(match.value[0]);
235
+ }
236
+ return [...dates];
237
+ };
238
+ const datedQuarterPeriods = (range) => {
239
+ return [...new Set(expressionDates(range).map((date) => date.slice(0, 4)))].flatMap((year) => [
240
+ 1,
241
+ 2,
242
+ 3,
243
+ 4
244
+ ].map((quarter) => `q${quarter} ${year}`));
245
+ };
246
+ const datedPeriods = (range, months) => {
247
+ const dates = expressionDates(range);
248
+ return [...[...new Set(dates.map((date) => date.slice(0, 4)))].flatMap((year) => [...months.map((month) => `${month} ${year}`), year]), ...dates.flatMap((date) => date === "0000-01-01" ? [date] : [date, previousDay(Number(date.slice(0, 4)), Number(date.slice(5, 7)), Number(date.slice(8, 10)))])];
249
+ };
250
+ const periodsFromPhrases = (phrases, parsePeriod) => phrases.flatMap((phrase) => Option.toArray(parsePeriod(phrase)));
251
+ const renderPeriodRange = (range, toDate, periods, since, before, through, after, between, toNow, fromNow, untilNow, afterNow) => {
252
+ const expected = rangeKey(range);
253
+ for (const entry of toDate) if (rangeKey(entry.range) === expected) return Option.some(entry.canonical);
254
+ const filter = formatFilter(range);
255
+ const startsAt = (expression) => periods.find((period) => formatInstantExpression(period.start) === expression);
256
+ const endsAt = (expression) => periods.find((period) => formatInstantExpression(period.end) === expression);
257
+ if (filter.lte === "now" && filter.gt === void 0 && filter.gte === void 0) return Option.some(untilNow());
258
+ if (filter.gte === "now" && filter.lt === void 0 && filter.lte === void 0) return Option.some(afterNow());
259
+ if (filter.gte !== void 0 && filter.lte === "now") {
260
+ const period = startsAt(filter.gte);
261
+ if (period !== void 0) return Option.some(toNow(period.canonical));
262
+ }
263
+ if (filter.gte === "now" && filter.lt !== void 0) {
264
+ const period = endsAt(filter.lt);
265
+ if (period !== void 0) return Option.some(fromNow(period.canonical));
266
+ }
267
+ if (filter.gte !== void 0 && filter.lt !== void 0) {
268
+ const exact = periods.find((period) => formatInstantExpression(period.start) === filter.gte && formatInstantExpression(period.end) === filter.lt);
269
+ if (exact !== void 0) return Option.some(exact.canonical);
270
+ const lower = startsAt(filter.gte)?.canonical;
271
+ const upper = endsAt(filter.lt)?.canonical;
272
+ return lower === void 0 || upper === void 0 ? Option.none() : Option.some(between(lower, upper));
273
+ }
274
+ if (filter.gte !== void 0 && filter.lt === void 0 && filter.lte === void 0) {
275
+ const start = startsAt(filter.gte);
276
+ if (start !== void 0) return Option.some(since(start.canonical));
277
+ const end = endsAt(filter.gte);
278
+ if (end !== void 0) return Option.some(after(end.canonical));
279
+ }
280
+ if (filter.lt !== void 0 && filter.gt === void 0 && filter.gte === void 0) {
281
+ const start = startsAt(filter.lt);
282
+ if (start !== void 0) return Option.some(before(start.canonical));
283
+ const end = endsAt(filter.lt);
284
+ if (end !== void 0) return Option.some(through(end.canonical));
285
+ }
286
+ return Option.none();
287
+ };
288
+ //#endregion
289
+ export { Period, calendarPeriodOffset, candidate, datedPeriods, datedQuarterPeriods, fixedDatePeriod, fixedMonthPeriod, fixedQuarterPeriod, fixedYearPeriod, fromNowRange, futurePeriod, futureRange, isoDate, joinedNowCandidate, joinedPeriodCandidate, monthOfRelativeYear, namedDatePeriod, openBoundaryCandidate, parseTrailingCount, periodBoundaryCandidate, periodEndDay, periodRange, periodStartDay, periodToDateRange, periodsFromPhrases, quarterOfRelativeYear, relativePeriod, relativeWeekend, remainingPeriodRange, renderPeriodRange, textAt, trailingPeriod, trailingRange, untilNowRange, validYear };
@@ -0,0 +1,10 @@
1
+ import { LanguageConflictError, LanguageRegistrationError } from "../language/errors.mjs";
2
+ import { BaseLanguageContribution, LanguagePlugin } from "../language/model.mjs";
3
+ import { LanguageRegistry } from "../language/registry.mjs";
4
+ import "../index.mjs";
5
+ //#region src/locales/tr.d.ts
6
+ declare const TurkishContribution: BaseLanguageContribution;
7
+ declare const TurkishLanguage: LanguagePlugin;
8
+ declare const TurkishLanguageLayer: import("effect/Layer").Layer<LanguageRegistry, LanguageConflictError | LanguageRegistrationError, never>;
9
+ //#endregion
10
+ export { TurkishContribution, TurkishLanguage, TurkishLanguageLayer };