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,92 @@
1
+ import { DateRangeExpr, InstantExpr } from "../ast/schemas.mjs";
2
+ import { FilterExpressionParseError, InvalidDateFilterError } from "./errors.mjs";
3
+ import { Effect } from "effect";
4
+ //#region src/filter/codec.d.ts
5
+ declare const parseFilter: (filter: {
6
+ readonly gt?: string;
7
+ readonly gte?: string;
8
+ readonly lt?: string;
9
+ readonly lte?: string;
10
+ }) => Effect.Effect<{
11
+ readonly _tag: "DateRange";
12
+ readonly lower: {
13
+ readonly _tag: "GreaterThan";
14
+ readonly value: InstantExpr;
15
+ } | {
16
+ readonly _tag: "GreaterThanOrEqual";
17
+ readonly value: InstantExpr;
18
+ };
19
+ readonly upper: {
20
+ readonly _tag: "LessThan";
21
+ readonly value: InstantExpr;
22
+ } | {
23
+ readonly _tag: "LessThanOrEqual";
24
+ readonly value: InstantExpr;
25
+ };
26
+ } | {
27
+ readonly _tag: "DateRange";
28
+ readonly lower: {
29
+ readonly _tag: "GreaterThan";
30
+ readonly value: InstantExpr;
31
+ } | {
32
+ readonly _tag: "GreaterThanOrEqual";
33
+ readonly value: InstantExpr;
34
+ };
35
+ readonly upper?: never;
36
+ } | {
37
+ readonly _tag: "DateRange";
38
+ readonly lower?: never;
39
+ readonly upper: {
40
+ readonly _tag: "LessThan";
41
+ readonly value: InstantExpr;
42
+ } | {
43
+ readonly _tag: "LessThanOrEqual";
44
+ readonly value: InstantExpr;
45
+ };
46
+ }, FilterExpressionParseError | InvalidDateFilterError, never>;
47
+ declare const formatFilter: (range: DateRangeExpr) => {
48
+ readonly gt?: string;
49
+ readonly gte?: string;
50
+ readonly lt?: string;
51
+ readonly lte?: string;
52
+ };
53
+ declare const rangeKey: (range: DateRangeExpr) => string;
54
+ declare const completePeriod: (start: InstantExpr, end: InstantExpr) => {
55
+ readonly _tag: "DateRange";
56
+ readonly lower: {
57
+ readonly _tag: "GreaterThan";
58
+ readonly value: InstantExpr;
59
+ } | {
60
+ readonly _tag: "GreaterThanOrEqual";
61
+ readonly value: InstantExpr;
62
+ };
63
+ readonly upper: {
64
+ readonly _tag: "LessThan";
65
+ readonly value: InstantExpr;
66
+ } | {
67
+ readonly _tag: "LessThanOrEqual";
68
+ readonly value: InstantExpr;
69
+ };
70
+ } | {
71
+ readonly _tag: "DateRange";
72
+ readonly lower: {
73
+ readonly _tag: "GreaterThan";
74
+ readonly value: InstantExpr;
75
+ } | {
76
+ readonly _tag: "GreaterThanOrEqual";
77
+ readonly value: InstantExpr;
78
+ };
79
+ readonly upper?: never;
80
+ } | {
81
+ readonly _tag: "DateRange";
82
+ readonly lower?: never;
83
+ readonly upper: {
84
+ readonly _tag: "LessThan";
85
+ readonly value: InstantExpr;
86
+ } | {
87
+ readonly _tag: "LessThanOrEqual";
88
+ readonly value: InstantExpr;
89
+ };
90
+ };
91
+ //#endregion
92
+ export { completePeriod, formatFilter, parseFilter, rangeKey };
@@ -0,0 +1,48 @@
1
+ import { boundedRange, greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, lowerOpenRange, upperOpenRange } from "../ast/constructors.mjs";
2
+ import { normalizeRange } from "../ast/normalize.mjs";
3
+ import { InvalidDateFilterError } from "./errors.mjs";
4
+ import { formatInstantExpression, parseInstantExpression } from "./expression.mjs";
5
+ import { DateFilter } from "./schema.mjs";
6
+ import { Effect, Match } from "effect";
7
+ //#region src/filter/codec.ts
8
+ const parseLower = (filter) => {
9
+ if (filter.gt !== void 0) return Effect.map(parseInstantExpression(filter.gt), greaterThan);
10
+ if (filter.gte !== void 0) return Effect.map(parseInstantExpression(filter.gte), greaterThanOrEqual);
11
+ return Effect.void;
12
+ };
13
+ const parseUpper = (filter) => {
14
+ if (filter.lt !== void 0) return Effect.map(parseInstantExpression(filter.lt), lessThan);
15
+ if (filter.lte !== void 0) return Effect.map(parseInstantExpression(filter.lte), lessThanOrEqual);
16
+ return Effect.void;
17
+ };
18
+ const parseFilter = Effect.fn("chronolizer.parseFilter")(function* (filter) {
19
+ const [lower, upper] = yield* Effect.all([parseLower(filter), parseUpper(filter)]);
20
+ if (lower !== void 0 && upper !== void 0) return normalizeRange(boundedRange(lower, upper));
21
+ if (lower !== void 0) return normalizeRange(lowerOpenRange(lower));
22
+ if (upper !== void 0) return normalizeRange(upperOpenRange(upper));
23
+ return yield* new InvalidDateFilterError({ message: "A date filter must contain at least one bound" });
24
+ });
25
+ const formatLower = Match.typeTags()({
26
+ GreaterThan: (bound) => DateFilter.make({ gt: formatInstantExpression(bound.value) }),
27
+ GreaterThanOrEqual: (bound) => DateFilter.make({ gte: formatInstantExpression(bound.value) })
28
+ });
29
+ const formatUpper = Match.typeTags()({
30
+ LessThan: (bound) => DateFilter.make({ lt: formatInstantExpression(bound.value) }),
31
+ LessThanOrEqual: (bound) => DateFilter.make({ lte: formatInstantExpression(bound.value) })
32
+ });
33
+ const formatFilter = (range) => {
34
+ const normalized = normalizeRange(range);
35
+ if (normalized.lower !== void 0 && normalized.upper !== void 0) return DateFilter.make({
36
+ ...formatLower(normalized.lower),
37
+ ...formatUpper(normalized.upper)
38
+ });
39
+ if (normalized.lower !== void 0) return formatLower(normalized.lower);
40
+ return formatUpper(normalized.upper);
41
+ };
42
+ const rangeKey = (range) => {
43
+ const filter = formatFilter(range);
44
+ return `${filter.gt ?? ""}|${filter.gte ?? ""}|${filter.lt ?? ""}|${filter.lte ?? ""}`;
45
+ };
46
+ const completePeriod = (start, end) => boundedRange(greaterThanOrEqual(start), lessThan(end));
47
+ //#endregion
48
+ export { completePeriod, formatFilter, parseFilter, rangeKey };
@@ -0,0 +1,14 @@
1
+ import { Schema } from "effect";
2
+ //#region src/filter/errors.d.ts
3
+ declare const FilterExpressionParseError_base: Schema.Class<FilterExpressionParseError, Schema.TaggedStruct<"FilterExpressionParseError", {
4
+ readonly input: Schema.String;
5
+ readonly offset: Schema.Int;
6
+ readonly expected: Schema.String;
7
+ }>, import("effect/Cause").YieldableError>;
8
+ declare class FilterExpressionParseError extends FilterExpressionParseError_base {}
9
+ declare const InvalidDateFilterError_base: Schema.Class<InvalidDateFilterError, Schema.TaggedStruct<"InvalidDateFilterError", {
10
+ readonly message: Schema.String;
11
+ }>, import("effect/Cause").YieldableError>;
12
+ declare class InvalidDateFilterError extends InvalidDateFilterError_base {}
13
+ //#endregion
14
+ export { FilterExpressionParseError, InvalidDateFilterError };
@@ -0,0 +1,10 @@
1
+ import { Schema } from "effect";
2
+ //#region src/filter/errors.ts
3
+ var FilterExpressionParseError = class extends Schema.TaggedError()("FilterExpressionParseError", {
4
+ input: Schema.String,
5
+ offset: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
6
+ expected: Schema.String
7
+ }) {};
8
+ var InvalidDateFilterError = class extends Schema.TaggedError()("InvalidDateFilterError", { message: Schema.String }) {};
9
+ //#endregion
10
+ export { FilterExpressionParseError, InvalidDateFilterError };
@@ -0,0 +1,8 @@
1
+ import { InstantExpr } from "../ast/schemas.mjs";
2
+ import { FilterExpressionParseError } from "./errors.mjs";
3
+ import { Effect } from "effect";
4
+ //#region src/filter/expression.d.ts
5
+ declare const parseInstantExpression: (input: string) => Effect.Effect<InstantExpr, FilterExpressionParseError, never>;
6
+ declare const formatInstantExpression: (expression: InstantExpr) => string;
7
+ //#endregion
8
+ export { formatInstantExpression, parseInstantExpression };
@@ -0,0 +1,93 @@
1
+ import { isIsoDate } from "../ast/schemas.mjs";
2
+ import { dateLiteral, now, shift, startOf } from "../ast/constructors.mjs";
3
+ import { foldInstant } from "../ast/fold.mjs";
4
+ import { normalizeInstant } from "../ast/normalize.mjs";
5
+ import { FilterExpressionParseError } from "./errors.mjs";
6
+ import { Effect } from "effect";
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
+ };
27
+ const failAt = (input, offset, expected) => Effect.fail(new FilterExpressionParseError({
28
+ input,
29
+ offset,
30
+ expected
31
+ }));
32
+ const isDigit = (value) => value >= "0" && value <= "9";
33
+ const isFirstPositiveDigit = (value) => value >= "1" && value <= "9";
34
+ const parseInstantExpression = Effect.fn(function* (input) {
35
+ let cursor = 0;
36
+ let expression;
37
+ let fixedAnchor = false;
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
+ }
53
+ while (cursor < input.length) {
54
+ const operator = input[cursor];
55
+ if (operator === "/") {
56
+ const unit = unitFromSymbol(input[cursor + 1] ?? "");
57
+ if (unit === void 0) return yield* failAt(input, cursor + 1, "a date unit: d, w, M, q, or y");
58
+ expression = startOf(expression, unit);
59
+ cursor += 2;
60
+ continue;
61
+ }
62
+ if (operator !== "+" && operator !== "-") return yield* failAt(input, cursor, "an operation beginning with \"+\", \"-\", or \"/\"");
63
+ const amountStart = cursor + 1;
64
+ if (!isFirstPositiveDigit(input[amountStart] ?? "")) return yield* failAt(input, amountStart, "a positive integer without a leading zero");
65
+ cursor = amountStart + 1;
66
+ while (cursor < input.length && isDigit(input[cursor] ?? "")) cursor += 1;
67
+ const amount = Number(input.slice(amountStart, cursor));
68
+ if (!Number.isSafeInteger(amount)) return yield* failAt(input, amountStart, "a safe positive integer");
69
+ const unit = unitFromSymbol(input[cursor] ?? "");
70
+ if (unit === void 0) return yield* failAt(input, cursor, "a date unit: d, w, M, q, or y");
71
+ expression = shift(expression, operator === "+" ? amount : -amount, unit);
72
+ cursor += 1;
73
+ }
74
+ return expression;
75
+ });
76
+ const appendOperation = (base, operation) => ({
77
+ text: `${base.text}${base.fixedAnchor ? "||" : ""}${operation}`,
78
+ fixedAnchor: false
79
+ });
80
+ const formatInstantExpression = (expression) => foldInstant(normalizeInstant(expression), {
81
+ now: () => ({
82
+ text: "now",
83
+ fixedAnchor: false
84
+ }),
85
+ dateLiteral: (value) => ({
86
+ text: value,
87
+ fixedAnchor: true
88
+ }),
89
+ shift: (base, amount, unit) => appendOperation(base, `${amount < 0 ? "-" : "+"}${Math.abs(amount)}${symbolFromUnit(unit)}`),
90
+ startOf: (base, unit) => appendOperation(base, `/${symbolFromUnit(unit)}`)
91
+ }).text;
92
+ //#endregion
93
+ export { formatInstantExpression, parseInstantExpression };
@@ -0,0 +1,13 @@
1
+ import { Schema } from "effect";
2
+ //#region src/filter/schema.d.ts
3
+ declare const DateExpressionString: Schema.String;
4
+ type DateExpressionString = typeof DateExpressionString.Type;
5
+ declare const DateFilter: Schema.Struct<{
6
+ readonly gt: Schema.optionalKey<Schema.String>;
7
+ readonly gte: Schema.optionalKey<Schema.String>;
8
+ readonly lt: Schema.optionalKey<Schema.String>;
9
+ readonly lte: Schema.optionalKey<Schema.String>;
10
+ }>;
11
+ type DateFilter = typeof DateFilter.Type;
12
+ //#endregion
13
+ export { DateExpressionString, DateFilter };
@@ -0,0 +1,11 @@
1
+ import { Schema } from "effect";
2
+ //#region src/filter/schema.ts
3
+ const DateExpressionString = Schema.String.check(Schema.isMinLength(1)).annotate({ identifier: "DateExpressionString" });
4
+ const DateFilter = Schema.Struct({
5
+ gt: Schema.optionalKey(DateExpressionString),
6
+ gte: Schema.optionalKey(DateExpressionString),
7
+ lt: Schema.optionalKey(DateExpressionString),
8
+ lte: Schema.optionalKey(DateExpressionString)
9
+ }).check(Schema.makeFilter((filter) => !(filter.gt !== void 0 && filter.gte !== void 0), { expected: "at most one lower date bound" }), Schema.makeFilter((filter) => !(filter.lt !== void 0 && filter.lte !== void 0), { expected: "at most one upper date bound" }), Schema.makeFilter((filter) => filter.gt !== void 0 || filter.gte !== void 0 || filter.lt !== void 0 || filter.lte !== void 0, { expected: "at least one date bound" })).annotate({ identifier: "DateFilter" });
10
+ //#endregion
11
+ export { DateExpressionString, DateFilter };
@@ -0,0 +1,37 @@
1
+ import { InstantExpr } from "../ast/schemas.mjs";
2
+ import { Schema } from "effect";
3
+ //#region src/filter/transformation.d.ts
4
+ declare const InstantExpressionFromString: Schema.decodeTo<Schema.Codec<InstantExpr, InstantExpr, never, never>, Schema.String, never, never>;
5
+ declare const DateRangeFromFilter: Schema.decodeTo<Schema.Union<readonly [Schema.TaggedStruct<"DateRange", {
6
+ readonly lower: Schema.Union<readonly [Schema.TaggedStruct<"GreaterThan", {
7
+ readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
8
+ }>, Schema.TaggedStruct<"GreaterThanOrEqual", {
9
+ readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
10
+ }>]>;
11
+ readonly upper: Schema.Union<readonly [Schema.TaggedStruct<"LessThan", {
12
+ readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
13
+ }>, Schema.TaggedStruct<"LessThanOrEqual", {
14
+ readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
15
+ }>]>;
16
+ }>, Schema.TaggedStruct<"DateRange", {
17
+ readonly lower: Schema.Union<readonly [Schema.TaggedStruct<"GreaterThan", {
18
+ readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
19
+ }>, Schema.TaggedStruct<"GreaterThanOrEqual", {
20
+ readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
21
+ }>]>;
22
+ readonly upper: Schema.optionalKey<Schema.Never>;
23
+ }>, Schema.TaggedStruct<"DateRange", {
24
+ readonly lower: Schema.optionalKey<Schema.Never>;
25
+ readonly upper: Schema.Union<readonly [Schema.TaggedStruct<"LessThan", {
26
+ readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
27
+ }>, Schema.TaggedStruct<"LessThanOrEqual", {
28
+ readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
29
+ }>]>;
30
+ }>]>, Schema.Struct<{
31
+ readonly gt: Schema.optionalKey<Schema.String>;
32
+ readonly gte: Schema.optionalKey<Schema.String>;
33
+ readonly lt: Schema.optionalKey<Schema.String>;
34
+ readonly lte: Schema.optionalKey<Schema.String>;
35
+ }>, never, never>;
36
+ //#endregion
37
+ export { DateRangeFromFilter, InstantExpressionFromString };
@@ -0,0 +1,20 @@
1
+ import { DateRangeExpr, InstantExpr } from "../ast/schemas.mjs";
2
+ import { formatInstantExpression, parseInstantExpression } from "./expression.mjs";
3
+ import { DateFilter } from "./schema.mjs";
4
+ import { formatFilter, parseFilter } from "./codec.mjs";
5
+ import { Effect, Match, Schema, SchemaIssue, SchemaTransformation } from "effect";
6
+ //#region src/filter/transformation.ts
7
+ const expressionIssue = (input, offset, expected) => new SchemaIssue.Forbidden({ message: `Invalid date expression at offset ${offset}: expected ${expected}; input: ${input}` });
8
+ const InstantExpressionFromString = Schema.String.pipe(Schema.decodeTo(InstantExpr, SchemaTransformation.transformOrFail({
9
+ decode: (input) => Effect.mapError(parseInstantExpression(input), (error) => expressionIssue(error.input, error.offset, error.expected)),
10
+ encode: (expression) => Effect.succeed(formatInstantExpression(expression))
11
+ })));
12
+ const DateRangeFromFilter = DateFilter.pipe(Schema.decodeTo(DateRangeExpr, SchemaTransformation.transformOrFail({
13
+ decode: (filter) => Effect.mapError(parseFilter(filter), (error) => new SchemaIssue.Forbidden({ message: Match.valueTags(error, {
14
+ FilterExpressionParseError: (parseError) => `Invalid filter expression at offset ${parseError.offset}: expected ${parseError.expected}`,
15
+ InvalidDateFilterError: (filterError) => filterError.message
16
+ }) })),
17
+ encode: (range) => Effect.succeed(formatFilter(range))
18
+ })));
19
+ //#endregion
20
+ export { DateRangeFromFilter, InstantExpressionFromString };