@kensio/quando 0.1.0 → 1.0.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 (97) hide show
  1. package/README.md +135 -4
  2. package/dist/assigned.d.ts +68 -0
  3. package/dist/assigned.d.ts.map +1 -0
  4. package/dist/assigned.js +84 -0
  5. package/dist/assigned.js.map +1 -0
  6. package/dist/build.d.ts +54 -0
  7. package/dist/build.d.ts.map +1 -0
  8. package/dist/build.js +77 -0
  9. package/dist/build.js.map +1 -0
  10. package/dist/canonical-rule.d.ts +23 -0
  11. package/dist/canonical-rule.d.ts.map +1 -0
  12. package/dist/canonical-rule.js +139 -0
  13. package/dist/canonical-rule.js.map +1 -0
  14. package/dist/canonical.d.ts +44 -0
  15. package/dist/canonical.d.ts.map +1 -0
  16. package/dist/canonical.js +53 -0
  17. package/dist/canonical.js.map +1 -0
  18. package/dist/cascade.d.ts +107 -0
  19. package/dist/cascade.d.ts.map +1 -0
  20. package/dist/cascade.js +53 -0
  21. package/dist/cascade.js.map +1 -0
  22. package/dist/context.d.ts +44 -0
  23. package/dist/context.d.ts.map +1 -0
  24. package/dist/context.js +9 -0
  25. package/dist/context.js.map +1 -0
  26. package/dist/day-rules.d.ts +23 -0
  27. package/dist/day-rules.d.ts.map +1 -0
  28. package/dist/day-rules.js +102 -0
  29. package/dist/day-rules.js.map +1 -0
  30. package/dist/index.d.ts +37 -2
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +24 -2
  33. package/dist/index.js.map +1 -1
  34. package/dist/interpret.d.ts +31 -0
  35. package/dist/interpret.d.ts.map +1 -0
  36. package/dist/interpret.js +99 -0
  37. package/dist/interpret.js.map +1 -0
  38. package/dist/interval-stream.d.ts +11 -4
  39. package/dist/interval-stream.d.ts.map +1 -1
  40. package/dist/interval-stream.js.map +1 -1
  41. package/dist/merge.d.ts +43 -0
  42. package/dist/merge.d.ts.map +1 -0
  43. package/dist/merge.js +79 -0
  44. package/dist/merge.js.map +1 -0
  45. package/dist/parse-cascade.d.ts +37 -0
  46. package/dist/parse-cascade.d.ts.map +1 -0
  47. package/dist/parse-cascade.js +105 -0
  48. package/dist/parse-cascade.js.map +1 -0
  49. package/dist/parse-fields.d.ts +27 -0
  50. package/dist/parse-fields.d.ts.map +1 -0
  51. package/dist/parse-fields.js +65 -0
  52. package/dist/parse-fields.js.map +1 -0
  53. package/dist/parse-shape.d.ts +39 -0
  54. package/dist/parse-shape.d.ts.map +1 -0
  55. package/dist/parse-shape.js +77 -0
  56. package/dist/parse-shape.js.map +1 -0
  57. package/dist/parse.d.ts +22 -0
  58. package/dist/parse.d.ts.map +1 -0
  59. package/dist/parse.js +93 -0
  60. package/dist/parse.js.map +1 -0
  61. package/dist/plain-forms.d.ts +22 -0
  62. package/dist/plain-forms.d.ts.map +1 -0
  63. package/dist/plain-forms.js +54 -0
  64. package/dist/plain-forms.js.map +1 -0
  65. package/dist/query.d.ts +73 -0
  66. package/dist/query.d.ts.map +1 -0
  67. package/dist/query.js +127 -0
  68. package/dist/query.js.map +1 -0
  69. package/dist/resolve.d.ts +31 -0
  70. package/dist/resolve.d.ts.map +1 -0
  71. package/dist/resolve.js +95 -0
  72. package/dist/resolve.js.map +1 -0
  73. package/dist/rota.d.ts +49 -0
  74. package/dist/rota.d.ts.map +1 -0
  75. package/dist/rota.js +41 -0
  76. package/dist/rota.js.map +1 -0
  77. package/dist/rule.d.ts +73 -0
  78. package/dist/rule.d.ts.map +1 -0
  79. package/dist/rule.js +20 -0
  80. package/dist/rule.js.map +1 -0
  81. package/dist/schedule.d.ts +69 -0
  82. package/dist/schedule.d.ts.map +1 -0
  83. package/dist/schedule.js +80 -0
  84. package/dist/schedule.js.map +1 -0
  85. package/dist/tally.d.ts +76 -0
  86. package/dist/tally.d.ts.map +1 -0
  87. package/dist/tally.js +80 -0
  88. package/dist/tally.js.map +1 -0
  89. package/dist/time-rules.d.ts +18 -0
  90. package/dist/time-rules.d.ts.map +1 -0
  91. package/dist/time-rules.js +55 -0
  92. package/dist/time-rules.js.map +1 -0
  93. package/dist/valued-stream.d.ts +51 -0
  94. package/dist/valued-stream.d.ts.map +1 -0
  95. package/dist/valued-stream.js +148 -0
  96. package/dist/valued-stream.js.map +1 -0
  97. package/package.json +22 -20
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Comparing rules and cascades, and the one form that makes it possible.
3
+ *
4
+ * [canonical-rule.ts](./canonical-rule.ts) holds the normal form itself and
5
+ * the reasoning behind where it stops. This is the surface over it: the same
6
+ * treatment for a cascade, a stable key, and equality.
7
+ */
8
+ import { type Cascade } from "./cascade.js";
9
+ import type { Rule } from "./rule.js";
10
+ /**
11
+ * The one form of a rule or a cascade that says what this one says.
12
+ *
13
+ * ```ts
14
+ * canonical(weekdays().except(dates("2026-12-25")));
15
+ * ```
16
+ *
17
+ * Flattens nested `all` and `any`, drops `always` from an `all` and `never`
18
+ * from an `any`, settles the ones that dominate, cancels double negation,
19
+ * deduplicates, and orders what is left. Leaves are ordered too, so
20
+ * `daysOfWeek("friday", "monday")` and `daysOfWeek("monday", "friday")` are
21
+ * the same document afterwards.
22
+ *
23
+ * A cascade keeps its layer order, because that order is its meaning.
24
+ */
25
+ export declare function canonical(rule: Rule): Rule;
26
+ export declare function canonical<V>(cascade: Cascade<V>): Cascade<V>;
27
+ /**
28
+ * A stable string for a rule or a cascade, the same for any two that say the
29
+ * same thing.
30
+ *
31
+ * What a cache key is. A cascade's values go through `JSON.stringify` with
32
+ * everything else, so this is worth as much as those values are storable.
33
+ */
34
+ export declare function fingerprint<V>(value: Rule | Cascade<V>): string;
35
+ /**
36
+ * Whether two rules, or two cascades, say the same thing.
37
+ *
38
+ * Syntactic, and deliberately so. Two rules that cover the same time by
39
+ * different routes are not equal, because deciding that in general means
40
+ * evaluating them over all of time. `always` and all seven days of the week
41
+ * are the pair to remember.
42
+ */
43
+ export declare function equals<V>(left: Rule | Cascade<V>, right: Rule | Cascade<V>): boolean;
44
+ //# sourceMappingURL=canonical.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical.d.ts","sourceRoot":"","sources":["../src/canonical.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,KAAK,OAAO,EAAyB,MAAM,cAAc,CAAC;AACnE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAwBtC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;AAC5C,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAK9D;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,CAE/D;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,CAAC,EACtB,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,EACvB,KAAK,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,GACvB,OAAO,CAET"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Comparing rules and cascades, and the one form that makes it possible.
3
+ *
4
+ * [canonical-rule.ts](./canonical-rule.ts) holds the normal form itself and
5
+ * the reasoning behind where it stops. This is the surface over it: the same
6
+ * treatment for a cascade, a stable key, and equality.
7
+ */
8
+ import { canonicalRule } from "./canonical-rule.js";
9
+ import { isCascade } from "./cascade.js";
10
+ function canonicalLayer(layer) {
11
+ return "value" in layer
12
+ ? { scope: canonicalRule(layer.scope), value: layer.value }
13
+ : {
14
+ scope: canonicalRule(layer.scope),
15
+ replace: canonicalCascade(layer.replace),
16
+ };
17
+ }
18
+ /**
19
+ * Layers keep their order, because order is what a cascade means. What changes
20
+ * is each scope, and a `merge` of `"override"` written out, which is the
21
+ * default said twice.
22
+ */
23
+ function canonicalCascade(cascade) {
24
+ const layers = cascade.layers.map((layer) => canonicalLayer(layer));
25
+ return cascade.merge === undefined || cascade.merge === "override"
26
+ ? { type: "cascade", layers }
27
+ : { type: "cascade", merge: cascade.merge, layers };
28
+ }
29
+ export function canonical(value) {
30
+ return isCascade(value) ? canonicalCascade(value) : canonicalRule(value);
31
+ }
32
+ /**
33
+ * A stable string for a rule or a cascade, the same for any two that say the
34
+ * same thing.
35
+ *
36
+ * What a cache key is. A cascade's values go through `JSON.stringify` with
37
+ * everything else, so this is worth as much as those values are storable.
38
+ */
39
+ export function fingerprint(value) {
40
+ return JSON.stringify(canonical(value));
41
+ }
42
+ /**
43
+ * Whether two rules, or two cascades, say the same thing.
44
+ *
45
+ * Syntactic, and deliberately so. Two rules that cover the same time by
46
+ * different routes are not equal, because deciding that in general means
47
+ * evaluating them over all of time. `always` and all seven days of the week
48
+ * are the pair to remember.
49
+ */
50
+ export function equals(left, right) {
51
+ return fingerprint(left) === fingerprint(right);
52
+ }
53
+ //# sourceMappingURL=canonical.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical.js","sourceRoot":"","sources":["../src/canonical.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAgB,SAAS,EAAc,MAAM,cAAc,CAAC;AAGnE,SAAS,cAAc,CAAI,KAAe;IACxC,OAAO,OAAO,IAAI,KAAK;QACrB,CAAC,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE;QAC3D,CAAC,CAAC;YACE,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC;YACjC,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAC,OAAO,CAAC;SACzC,CAAC;AACR,CAAC;AAED;;;;GAIG;AACH,SAAS,gBAAgB,CAAI,OAAmB;IAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;IAEpE,OAAO,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,KAAK,KAAK,UAAU;QAChE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE;QAC7B,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;AACxD,CAAC;AAmBD,MAAM,UAAU,SAAS,CAAI,KAAwB;IACnD,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;AAC3E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAI,KAAwB;IACrD,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,KAAa,CAAC,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,MAAM,CACpB,IAAuB,EACvB,KAAwB;IAExB,OAAO,WAAW,CAAC,IAAI,CAAC,KAAK,WAAW,CAAC,KAAK,CAAC,CAAC;AAClD,CAAC"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Layers, and what holds inside them.
3
+ *
4
+ * A `Rule` says *when*. A `Cascade<V>` says *what holds when*: an ordered list
5
+ * of layers, each pairing a scope with what applies inside it, resolved like
6
+ * the rules in a stylesheet — the last layer to claim a moment wins.
7
+ *
8
+ * Keeping values here rather than on `Rule` is what lets the set algebra stay
9
+ * simple. If a rule carried a value then every combinator would be generic in
10
+ * it, and `not` would have to answer what the complement of a rota is. Values
11
+ * only appear where they are actually needed, which is assignment.
12
+ */
13
+ import type { Interval } from "./interval.js";
14
+ import type { MergeStrategy } from "./merge.js";
15
+ import type { Rule } from "./rule.js";
16
+ /**
17
+ * An interval with a value assigned to it.
18
+ *
19
+ * Extends `Interval`, so everything that reads an interval — `duration`,
20
+ * `contains`, `isEmpty` — reads one of these unchanged.
21
+ */
22
+ export interface Valued<V> extends Interval {
23
+ readonly value: V;
24
+ }
25
+ /**
26
+ * One layer: where it applies, and what applies there.
27
+ *
28
+ * The two forms are separate fields rather than one field holding either,
29
+ * because with one field the resolver would have to decide which meaning a
30
+ * value carries by inspecting the shape of the caller's own domain type — and
31
+ * for a `Cascade<Rule>` the two are indistinguishable.
32
+ */
33
+ export type Layer<V> = ConstantLayer<V> | ReplacingLayer<V>;
34
+ /** A layer assigning one value across the whole of its scope. */
35
+ export interface ConstantLayer<V> {
36
+ readonly scope: Rule;
37
+ readonly value: V;
38
+ }
39
+ /**
40
+ * A layer whose scope is claimed, but whose value inside it comes from another
41
+ * cascade.
42
+ *
43
+ * This is the case a plain value cannot express. "On the eleventh we close at
44
+ * three" is not `(scope: the eleventh, value: closed)`, which would shut the
45
+ * whole day, and writing it as a value over 15:00–17:00 forces the author to
46
+ * know the hours it is overriding — which is the thing a cascade exists to
47
+ * avoid. What it means is: *within this scope, ignore the layers below and use
48
+ * this instead.*
49
+ */
50
+ export interface ReplacingLayer<V> {
51
+ readonly scope: Rule;
52
+ readonly replace: Cascade<V>;
53
+ }
54
+ /**
55
+ * An ordered list of layers. Later layers win.
56
+ *
57
+ * Order is part of the meaning, so the JSON is an array and reordering it
58
+ * changes the answer.
59
+ */
60
+ export interface Cascade<V> {
61
+ readonly type: "cascade";
62
+ /**
63
+ * What happens where two layers claim the same moment. Absent means
64
+ * `override`, which is the precedence a cascade has always had.
65
+ *
66
+ * The strategy is a name in the document. A merge function passed to
67
+ * `resolve` could not be stored, and a cascade that no longer says how it
68
+ * combines is one two readers can disagree about.
69
+ */
70
+ readonly merge?: MergeStrategy;
71
+ readonly layers: readonly Layer<V>[];
72
+ }
73
+ /** Whether a value is a cascade rather than a rule. */
74
+ export declare function isCascade<V>(value: Rule | Cascade<V>): value is Cascade<V>;
75
+ /** An ordered list of layers, lowest priority first. */
76
+ export declare function cascade<V>(...layers: readonly Layer<V>[]): Cascade<V>;
77
+ /**
78
+ * An ordered list of layers whose overlaps combine rather than displace.
79
+ *
80
+ * ```ts
81
+ * const staff = merged("sum", layer(weekdays(), 3), layer(dates("2026-03-11"), 2));
82
+ * ```
83
+ *
84
+ * The Wednesday has five. Under {@link cascade} it would have two, because the
85
+ * later layer would displace the earlier one.
86
+ */
87
+ export declare function merged<V>(strategy: MergeStrategy, ...layers: readonly Layer<V>[]): Cascade<V>;
88
+ /** One value, across the whole of a scope. */
89
+ export declare function layer<V>(scope: Rule, value: V): ConstantLayer<V>;
90
+ /**
91
+ * True while a rule holds, and unassigned everywhere else.
92
+ *
93
+ * The bridge from *when* to *what*: it is how a plain schedule becomes a
94
+ * cascade, and what {@link replace} lifts a bare rule with.
95
+ */
96
+ export declare function whenever(rule: Rule): Cascade<boolean>;
97
+ /**
98
+ * A scope claimed outright, with what holds inside it given by another
99
+ * cascade — or, for a schedule, by a rule.
100
+ *
101
+ * The rule form is sugar: it stores the lifted cascade, so the document is the
102
+ * same either way and a stored layer never needs a reader to know which form
103
+ * was written.
104
+ */
105
+ export declare function replace<V>(scope: Rule, replacement: Cascade<V>): ReplacingLayer<V>;
106
+ export declare function replace(scope: Rule, replacement: Rule): ReplacingLayer<boolean>;
107
+ //# sourceMappingURL=cascade.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cascade.d.ts","sourceRoot":"","sources":["../src/cascade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC;;;;;GAKG;AACH,MAAM,WAAW,MAAM,CAAC,CAAC,CAAE,SAAQ,QAAQ;IACzC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;AAE5D,iEAAiE;AACjE,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CAC9B;AAED;;;;;GAKG;AACH,MAAM,WAAW,OAAO,CAAC,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB;;;;;;;OAOG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;CACtC;AAED,uDAAuD;AACvD,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,OAAO,CAAC,CAAC,CAAC,CAE1E;AAED,wDAAwD;AACxD,wBAAgB,OAAO,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,SAAS,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAErE;AAED;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,CAAC,EACtB,QAAQ,EAAE,aAAa,EACvB,GAAG,MAAM,EAAE,SAAS,KAAK,CAAC,CAAC,CAAC,EAAE,GAC7B,OAAO,CAAC,CAAC,CAAC,CAEZ;AAED,8CAA8C;AAC9C,wBAAgB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAEhE;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAErD;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,CAAC,EACvB,KAAK,EAAE,IAAI,EACX,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC,GACtB,cAAc,CAAC,CAAC,CAAC,CAAC;AACrB,wBAAgB,OAAO,CACrB,KAAK,EAAE,IAAI,EACX,WAAW,EAAE,IAAI,GAChB,cAAc,CAAC,OAAO,CAAC,CAAC"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Layers, and what holds inside them.
3
+ *
4
+ * A `Rule` says *when*. A `Cascade<V>` says *what holds when*: an ordered list
5
+ * of layers, each pairing a scope with what applies inside it, resolved like
6
+ * the rules in a stylesheet — the last layer to claim a moment wins.
7
+ *
8
+ * Keeping values here rather than on `Rule` is what lets the set algebra stay
9
+ * simple. If a rule carried a value then every combinator would be generic in
10
+ * it, and `not` would have to answer what the complement of a rota is. Values
11
+ * only appear where they are actually needed, which is assignment.
12
+ */
13
+ /** Whether a value is a cascade rather than a rule. */
14
+ export function isCascade(value) {
15
+ return value.type === "cascade";
16
+ }
17
+ /** An ordered list of layers, lowest priority first. */
18
+ export function cascade(...layers) {
19
+ return { type: "cascade", layers };
20
+ }
21
+ /**
22
+ * An ordered list of layers whose overlaps combine rather than displace.
23
+ *
24
+ * ```ts
25
+ * const staff = merged("sum", layer(weekdays(), 3), layer(dates("2026-03-11"), 2));
26
+ * ```
27
+ *
28
+ * The Wednesday has five. Under {@link cascade} it would have two, because the
29
+ * later layer would displace the earlier one.
30
+ */
31
+ export function merged(strategy, ...layers) {
32
+ return { type: "cascade", merge: strategy, layers };
33
+ }
34
+ /** One value, across the whole of a scope. */
35
+ export function layer(scope, value) {
36
+ return { scope, value };
37
+ }
38
+ /**
39
+ * True while a rule holds, and unassigned everywhere else.
40
+ *
41
+ * The bridge from *when* to *what*: it is how a plain schedule becomes a
42
+ * cascade, and what {@link replace} lifts a bare rule with.
43
+ */
44
+ export function whenever(rule) {
45
+ return cascade(layer(rule, true));
46
+ }
47
+ export function replace(scope, replacement) {
48
+ return {
49
+ scope,
50
+ replace: isCascade(replacement) ? replacement : whenever(replacement),
51
+ };
52
+ }
53
+ //# sourceMappingURL=cascade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cascade.js","sourceRoot":"","sources":["../src/cascade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAoEH,uDAAuD;AACvD,MAAM,UAAU,SAAS,CAAI,KAAwB;IACnD,OAAO,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC;AAClC,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,OAAO,CAAI,GAAG,MAA2B;IACvD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AACrC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,MAAM,CACpB,QAAuB,EACvB,GAAG,MAA2B;IAE9B,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AACtD,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,KAAK,CAAI,KAAW,EAAE,KAAQ;IAC5C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC1B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAU;IACjC,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AACpC,CAAC;AAkBD,MAAM,UAAU,OAAO,CACrB,KAAW,EACX,WAAoC;IAEpC,OAAO;QACL,KAAK;QACL,OAAO,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;KACtE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * What a rule is evaluated against.
3
+ *
4
+ * An object rather than a bare window, because rules need more than a window
5
+ * and always will: a sunrise rule needs coordinates, a rule written in the
6
+ * Hebrew calendar needs the calendar, a rendered description needs the locale.
7
+ * Adding a field here is harmless; changing a bare parameter into an object
8
+ * later would break every rule implementation, including anyone else's.
9
+ */
10
+ export interface Context {
11
+ /**
12
+ * Where evaluation begins, and — since a `ZonedDateTime` carries one — the
13
+ * time zone any rule that does not name its own is read in. One source of
14
+ * truth: there is no way for a separate `zone` field to disagree with this.
15
+ */
16
+ readonly from: Temporal.ZonedDateTime;
17
+ /**
18
+ * Where evaluation stops. Optional, because a recurrence genuinely has no
19
+ * end and pretending otherwise would have callers guessing a window big
20
+ * enough to hold an answer they cannot predict.
21
+ *
22
+ * Leaving it out means the streams a rule produces may be endless. That is
23
+ * supported and sometimes what you want — `take(…, 3)` over an endless
24
+ * stream is exact and cheap — but a composition whose answer is empty then
25
+ * has nothing to discover that from, and will not finish. Bound the window
26
+ * when the answer might be empty.
27
+ */
28
+ readonly to?: Temporal.ZonedDateTime;
29
+ /** Where on Earth, for rules about the sun, the moon or the tide. */
30
+ readonly location?: {
31
+ readonly latitude: number;
32
+ readonly longitude: number;
33
+ };
34
+ /** For rendering a rule as text, and for locale-specific conventions. */
35
+ readonly locale?: string;
36
+ }
37
+ /** The context's window, in the form the interval algebra takes. */
38
+ export declare function windowOf(context: Context): {
39
+ readonly start: Temporal.ZonedDateTime;
40
+ readonly end: Temporal.ZonedDateTime | undefined;
41
+ };
42
+ /** The zone a rule is read in when it does not name one of its own. */
43
+ export declare function zoneOf(context: Context, override?: string): string;
44
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,OAAO;IACtB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,aAAa,CAAC;IAEtC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,aAAa,CAAC;IAErC,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAClB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC5B,CAAC;IAEF,yEAAyE;IACzE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,oEAAoE;AACpE,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG;IAC1C,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC;IACvC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,aAAa,GAAG,SAAS,CAAC;CAClD,CAEA;AAED,uEAAuE;AACvE,wBAAgB,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAElE"}
@@ -0,0 +1,9 @@
1
+ /** The context's window, in the form the interval algebra takes. */
2
+ export function windowOf(context) {
3
+ return { start: context.from, end: context.to };
4
+ }
5
+ /** The zone a rule is read in when it does not name one of its own. */
6
+ export function zoneOf(context, override) {
7
+ return override ?? context.from.timeZoneId;
8
+ }
9
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAwCA,oEAAoE;AACpE,MAAM,UAAU,QAAQ,CAAC,OAAgB;IAIvC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;AAClD,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,MAAM,CAAC,OAAgB,EAAE,QAAiB;IACxD,OAAO,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Rules that select whole days: by weekday, and by date.
3
+ *
4
+ * Both uphold the stream contract — sorted, not overlapping, coalesced — and
5
+ * the coalescing is the fiddly part. Two selected days that happen to be
6
+ * consecutive are one interval, not two that touch at midnight, and a stream of
7
+ * touching intervals is one the sweeps in `interval-stream.ts` read wrongly.
8
+ */
9
+ import { type Context } from "./context.js";
10
+ import type { IntervalStream } from "./interval-stream.js";
11
+ import { type Weekday } from "./rule.js";
12
+ /** Whole days selected by day of the week. */
13
+ export declare function weekdayIntervals(context: Context, days: readonly Weekday[], zone?: string): IntervalStream;
14
+ /**
15
+ * Whole days named by date.
16
+ *
17
+ * Walks the given dates rather than the calendar, so a handful of dates costs a
18
+ * handful of steps however far apart they are, and the stream ends when they
19
+ * do. Sorted and de-duplicated first, because the contract is about the output
20
+ * and callers write dates in whatever order they think of them.
21
+ */
22
+ export declare function dateIntervals(context: Context, dates: readonly string[], zone?: string): IntervalStream;
23
+ //# sourceMappingURL=day-rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"day-rules.d.ts","sourceRoot":"","sources":["../src/day-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAY,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAsDnD,8CAA8C;AAC9C,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,SAAS,OAAO,EAAE,EACxB,IAAI,CAAC,EAAE,MAAM,GACZ,cAAc,CAehB;AAED;;;;;;;GAOG;AACH,wBAAiB,aAAa,CAC5B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,IAAI,CAAC,EAAE,MAAM,GACZ,cAAc,CAiChB"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Rules that select whole days: by weekday, and by date.
3
+ *
4
+ * Both uphold the stream contract — sorted, not overlapping, coalesced — and
5
+ * the coalescing is the fiddly part. Two selected days that happen to be
6
+ * consecutive are one interval, not two that touch at midnight, and a stream of
7
+ * touching intervals is one the sweeps in `interval-stream.ts` read wrongly.
8
+ */
9
+ import { zoneOf } from "./context.js";
10
+ import { WEEKDAYS } from "./rule.js";
11
+ function startOfDay(date, zone) {
12
+ return date.toZonedDateTime({ timeZone: zone, plainTime: "00:00" });
13
+ }
14
+ function weekdayOf(date) {
15
+ return WEEKDAYS[date.dayOfWeek - 1];
16
+ }
17
+ /**
18
+ * Whole days matching a predicate, walked forward from the context, with runs
19
+ * of consecutive matches merged into one interval.
20
+ *
21
+ * Endless unless the context bounds it. The run being built is flushed when the
22
+ * window ends, so a rule that matches every day still terminates — without
23
+ * that, a run that never closes would never yield anything at all.
24
+ */
25
+ function* matchingDays(context, zone, matches) {
26
+ const stop = context.to;
27
+ let date = context.from.withTimeZone(zone).toPlainDate();
28
+ let runStart;
29
+ for (;;) {
30
+ const dayStart = startOfDay(date, zone);
31
+ if (stop !== undefined &&
32
+ Temporal.ZonedDateTime.compare(dayStart, stop) >= 0) {
33
+ if (runStart !== undefined) {
34
+ yield { start: startOfDay(runStart, zone), end: dayStart };
35
+ }
36
+ return;
37
+ }
38
+ if (matches(date)) {
39
+ runStart ??= date;
40
+ }
41
+ else if (runStart !== undefined) {
42
+ yield { start: startOfDay(runStart, zone), end: dayStart };
43
+ runStart = undefined;
44
+ }
45
+ date = date.add({ days: 1 });
46
+ }
47
+ }
48
+ /** Whole days selected by day of the week. */
49
+ export function weekdayIntervals(context, days, zone) {
50
+ const wanted = new Set(days);
51
+ // Nothing can match, so there is nothing to walk the calendar for. Without
52
+ // this, an unbounded context sends `matchingDays` forward a day at a time
53
+ // until Temporal's year limit, thousands of centuries later, and reports it
54
+ // as a date range error rather than as the empty rule it is.
55
+ if (wanted.size === 0) {
56
+ return [];
57
+ }
58
+ return matchingDays(context, zoneOf(context, zone), (date) => {
59
+ const weekday = weekdayOf(date);
60
+ return weekday !== undefined && wanted.has(weekday);
61
+ });
62
+ }
63
+ /**
64
+ * Whole days named by date.
65
+ *
66
+ * Walks the given dates rather than the calendar, so a handful of dates costs a
67
+ * handful of steps however far apart they are, and the stream ends when they
68
+ * do. Sorted and de-duplicated first, because the contract is about the output
69
+ * and callers write dates in whatever order they think of them.
70
+ */
71
+ export function* dateIntervals(context, dates, zone) {
72
+ const inZone = zoneOf(context, zone);
73
+ const days = [
74
+ ...new Set(dates.map((date) => Temporal.PlainDate.from(date).toString())),
75
+ ]
76
+ .toSorted()
77
+ .map((date) => Temporal.PlainDate.from(date));
78
+ let runStart;
79
+ let runEnd;
80
+ for (const day of days) {
81
+ if (runEnd !== undefined && Temporal.PlainDate.compare(day, runEnd) === 0) {
82
+ // Consecutive with the run so far, so it extends rather than starts one.
83
+ runEnd = day.add({ days: 1 });
84
+ continue;
85
+ }
86
+ if (runStart !== undefined && runEnd !== undefined) {
87
+ yield {
88
+ start: startOfDay(runStart, inZone),
89
+ end: startOfDay(runEnd, inZone),
90
+ };
91
+ }
92
+ runStart = day;
93
+ runEnd = day.add({ days: 1 });
94
+ }
95
+ if (runStart !== undefined && runEnd !== undefined) {
96
+ yield {
97
+ start: startOfDay(runStart, inZone),
98
+ end: startOfDay(runEnd, inZone),
99
+ };
100
+ }
101
+ }
102
+ //# sourceMappingURL=day-rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"day-rules.js","sourceRoot":"","sources":["../src/day-rules.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -2,8 +2,14 @@
2
2
  * Quando: declarative temporal rules for schedules, deadlines, constraints and
3
3
  * exceptions.
4
4
  *
5
- * What exists so far is the interval core the algebra everything else is
6
- * built from. Rules, cascades and queries are still to come.
5
+ * What exists so far is the interval core, the rule language on top of it, and
6
+ * the two ends of "rules are data": a builder that writes one, and a parser
7
+ * that reads one back from whatever a database or a form actually held.
8
+ * Queries sit on top: is it open now, how much working time is in this window,
9
+ * when does it next open, and where do you get to after three hours that only
10
+ * count while it is open. Cascades sit beside them: ordered layers that carry
11
+ * values, resolved by precedence, for the questions a boolean schedule cannot
12
+ * answer.
7
13
  *
8
14
  * Requires a runtime with `Temporal`: Node 26 or later, or a browser that
9
15
  * implements it.
@@ -16,4 +22,33 @@ export { compareEnds, compareStarts, contains, duration, isEmpty, startsAtOrBefo
16
22
  export type { IntervalStream } from "./interval-stream.js";
17
23
  export { clip, complement, intersect, union } from "./interval-stream.js";
18
24
  export { take } from "./stream.js";
25
+ export type { Context } from "./context.js";
26
+ export type { AllRule, AnyRule, AlwaysRule, DatesRule, DaysOfWeekRule, NeverRule, NotRule, Rule, TimeOfDayRule, Weekday, } from "./rule.js";
27
+ export { WEEKDAYS } from "./rule.js";
28
+ export { intervals } from "./interpret.js";
29
+ export type { Built } from "./build.js";
30
+ export { all, always, any, dates, daysOfWeek, inZone, never, not, timeOfDay, weekdays, weekends, } from "./build.js";
31
+ export { parseRule } from "./parse.js";
32
+ export type { ValueParser } from "./parse-cascade.js";
33
+ export { parseCascade } from "./parse-cascade.js";
34
+ export { asBoolean, asString, fail } from "./parse-shape.js";
35
+ export type { Cascade, ConstantLayer, Layer, ReplacingLayer, Valued, } from "./cascade.js";
36
+ export { cascade, isCascade, layer, merged, replace, whenever, } from "./cascade.js";
37
+ export type { Merge, MergeStrategy } from "./merge.js";
38
+ export { MERGE_STRATEGIES } from "./merge.js";
39
+ export type { ValuedStream } from "./valued-stream.js";
40
+ export { overlay } from "./valued-stream.js";
41
+ export { resolve } from "./resolve.js";
42
+ export { canonical, equals, fingerprint } from "./canonical.js";
43
+ export type { PlainRule } from "./plain-forms.js";
44
+ export type { Schedule } from "./schedule.js";
45
+ export { schedule } from "./schedule.js";
46
+ export type { Rota } from "./rota.js";
47
+ export { rota } from "./rota.js";
48
+ export type { Tally } from "./tally.js";
49
+ export { tally } from "./tally.js";
50
+ export type { Assigned, Covers } from "./assigned.js";
51
+ export { assigned, nextValue, valueAt } from "./assigned.js";
52
+ export type { Search } from "./query.js";
53
+ export { activeAt, advanceBy, elapsed, next } from "./query.js";
19
54
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EACL,WAAW,EACX,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,mBAAmB,EACnB,eAAe,GAChB,MAAM,eAAe,CAAC;AAEvB,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAE1E,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EACL,WAAW,EACX,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,mBAAmB,EACnB,eAAe,GAChB,MAAM,eAAe,CAAC;AAEvB,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAE1E,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,YAAY,EACV,OAAO,EACP,OAAO,EACP,UAAU,EACV,SAAS,EACT,cAAc,EACd,SAAS,EACT,OAAO,EACP,IAAI,EACJ,aAAa,EACb,OAAO,GACR,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,YAAY,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EACL,GAAG,EACH,MAAM,EACN,GAAG,EACH,KAAK,EACL,UAAU,EACV,MAAM,EACN,KAAK,EACL,GAAG,EACH,SAAS,EACT,QAAQ,EACR,QAAQ,GACT,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7D,YAAY,EACV,OAAO,EACP,aAAa,EACb,KAAK,EACL,cAAc,EACd,MAAM,GACP,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,OAAO,EACP,SAAS,EACT,KAAK,EACL,MAAM,EACN,OAAO,EACP,QAAQ,GACT,MAAM,cAAc,CAAC;AAEtB,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAEhE,YAAY,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,YAAY,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,YAAY,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7D,YAAY,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -2,8 +2,14 @@
2
2
  * Quando: declarative temporal rules for schedules, deadlines, constraints and
3
3
  * exceptions.
4
4
  *
5
- * What exists so far is the interval core the algebra everything else is
6
- * built from. Rules, cascades and queries are still to come.
5
+ * What exists so far is the interval core, the rule language on top of it, and
6
+ * the two ends of "rules are data": a builder that writes one, and a parser
7
+ * that reads one back from whatever a database or a form actually held.
8
+ * Queries sit on top: is it open now, how much working time is in this window,
9
+ * when does it next open, and where do you get to after three hours that only
10
+ * count while it is open. Cascades sit beside them: ordered layers that carry
11
+ * values, resolved by precedence, for the questions a boolean schedule cannot
12
+ * answer.
7
13
  *
8
14
  * Requires a runtime with `Temporal`: Node 26 or later, or a browser that
9
15
  * implements it.
@@ -14,4 +20,20 @@
14
20
  export { compareEnds, compareStarts, contains, duration, isEmpty, startsAtOrBeforeEnd, startsBeforeEnd, } from "./interval.js";
15
21
  export { clip, complement, intersect, union } from "./interval-stream.js";
16
22
  export { take } from "./stream.js";
23
+ export { WEEKDAYS } from "./rule.js";
24
+ export { intervals } from "./interpret.js";
25
+ export { all, always, any, dates, daysOfWeek, inZone, never, not, timeOfDay, weekdays, weekends, } from "./build.js";
26
+ export { parseRule } from "./parse.js";
27
+ export { parseCascade } from "./parse-cascade.js";
28
+ export { asBoolean, asString, fail } from "./parse-shape.js";
29
+ export { cascade, isCascade, layer, merged, replace, whenever, } from "./cascade.js";
30
+ export { MERGE_STRATEGIES } from "./merge.js";
31
+ export { overlay } from "./valued-stream.js";
32
+ export { resolve } from "./resolve.js";
33
+ export { canonical, equals, fingerprint } from "./canonical.js";
34
+ export { schedule } from "./schedule.js";
35
+ export { rota } from "./rota.js";
36
+ export { tally } from "./tally.js";
37
+ export { assigned, nextValue, valueAt } from "./assigned.js";
38
+ export { activeAt, advanceBy, elapsed, next } from "./query.js";
17
39
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EACL,WAAW,EACX,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,mBAAmB,EACnB,eAAe,GAChB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAE1E,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EACL,WAAW,EACX,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,mBAAmB,EACnB,eAAe,GAChB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAE1E,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAgBnC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG3C,OAAO,EACL,GAAG,EACH,MAAM,EACN,GAAG,EACH,KAAK,EACL,UAAU,EACV,MAAM,EACN,KAAK,EACL,GAAG,EACH,SAAS,EACT,QAAQ,EACR,QAAQ,GACT,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAS7D,OAAO,EACL,OAAO,EACP,SAAS,EACT,KAAK,EACL,MAAM,EACN,OAAO,EACP,QAAQ,GACT,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG9C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAKhE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG7D,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Reading a rule as the times it covers.
3
+ *
4
+ * One function over the whole rule language rather than a method on each rule
5
+ * type, which is what lets the next operation over rules — describing them,
6
+ * validating them, drawing them — be another function here rather than another
7
+ * method everywhere.
8
+ *
9
+ * Everything is clipped to the context's window. That is not tidiness: a
10
+ * composition whose answer is empty has nothing to discover that from, so the
11
+ * only thing that makes it terminate is its sources running out. Clipping at
12
+ * the leaves is what makes them run out.
13
+ */
14
+ import { type Context } from "./context.js";
15
+ import { type IntervalStream } from "./interval-stream.js";
16
+ import type { Rule } from "./rule.js";
17
+ /**
18
+ * The intervals a rule covers within a context, in order and coalesced.
19
+ *
20
+ * The stream is lazy, and endless when the context has no end and the rule
21
+ * recurs — which is the point. Take what you need from it.
22
+ *
23
+ * Every interval comes back in the context's zone. The algebra compares
24
+ * instants, so a sweep is free to take one interval's start and another's end,
25
+ * and those two may have been written in different zones — a London rule read
26
+ * from a Tokyo context would otherwise hand back an interval whose two halves
27
+ * disagree about what time it is. The instants are unaffected either way; this
28
+ * only settles which zone reads them back.
29
+ */
30
+ export declare function intervals(rule: Rule, context: Context): IntervalStream;
31
+ //# sourceMappingURL=interpret.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interpret.d.ts","sourceRoot":"","sources":["../src/interpret.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAY,MAAM,cAAc,CAAC;AACtD,OAAO,EAIL,KAAK,cAAc,EAEpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAUtC;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,GAAG,cAAc,CAEtE"}