@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,99 @@
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 { windowOf } from "./context.js";
15
+ import { clip, complement, intersect, union, } from "./interval-stream.js";
16
+ import { dateIntervals, weekdayIntervals } from "./day-rules.js";
17
+ import { timeOfDayIntervals } from "./time-rules.js";
18
+ /** All of time, before the window narrows it. */
19
+ const UNBOUNDED = [{ start: undefined, end: undefined }];
20
+ /** No time at all. */
21
+ const EMPTY = [];
22
+ /**
23
+ * The intervals a rule covers within a context, in order and coalesced.
24
+ *
25
+ * The stream is lazy, and endless when the context has no end and the rule
26
+ * recurs — which is the point. Take what you need from it.
27
+ *
28
+ * Every interval comes back in the context's zone. The algebra compares
29
+ * instants, so a sweep is free to take one interval's start and another's end,
30
+ * and those two may have been written in different zones — a London rule read
31
+ * from a Tokyo context would otherwise hand back an interval whose two halves
32
+ * disagree about what time it is. The instants are unaffected either way; this
33
+ * only settles which zone reads them back.
34
+ */
35
+ export function intervals(rule, context) {
36
+ return readIn(evaluate(rule, context), context.from.timeZoneId);
37
+ }
38
+ function* readIn(stream, zone) {
39
+ for (const interval of stream) {
40
+ yield {
41
+ start: interval.start?.withTimeZone(zone),
42
+ end: interval.end?.withTimeZone(zone),
43
+ };
44
+ }
45
+ }
46
+ function evaluate(rule, context) {
47
+ const window = windowOf(context);
48
+ switch (rule.type) {
49
+ case "always": {
50
+ return clip(UNBOUNDED, window);
51
+ }
52
+ case "never": {
53
+ return EMPTY;
54
+ }
55
+ case "daysOfWeek": {
56
+ return clip(weekdayIntervals(context, rule.days, rule.zone), window);
57
+ }
58
+ case "dates": {
59
+ return clip(dateIntervals(context, rule.dates, rule.zone), window);
60
+ }
61
+ case "timeOfDay": {
62
+ return clip(timeOfDayIntervals(context, rule.from, rule.to, rule.zone), window);
63
+ }
64
+ case "all": {
65
+ return everyOf(rule.rules, context);
66
+ }
67
+ case "any": {
68
+ return anyOf(rule.rules, context);
69
+ }
70
+ case "not": {
71
+ // Re-clipped, because a complement is unbounded at both ends by nature
72
+ // and would otherwise reach outside the window it was asked about.
73
+ return clip(complement(evaluate(rule.rule, context)), window);
74
+ }
75
+ default: {
76
+ // Compiles only while every rule type above is handled. Adding one to the
77
+ // union makes this the error that says so.
78
+ const unreachable = rule;
79
+ return unreachable;
80
+ }
81
+ }
82
+ }
83
+ /** Intersection, starting from all of time so that no rules means no limits. */
84
+ function everyOf(rules, context) {
85
+ let covered = clip(UNBOUNDED, windowOf(context));
86
+ for (const rule of rules) {
87
+ covered = intersect(covered, evaluate(rule, context));
88
+ }
89
+ return covered;
90
+ }
91
+ /** Union, starting from nothing so that no rules means no times. */
92
+ function anyOf(rules, context) {
93
+ let covered = EMPTY;
94
+ for (const rule of rules) {
95
+ covered = union(covered, evaluate(rule, context));
96
+ }
97
+ return covered;
98
+ }
99
+ //# sourceMappingURL=interpret.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interpret.js","sourceRoot":"","sources":["../src/interpret.ts"],"names":[],"mappings":"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"}
@@ -13,10 +13,17 @@ import { type Interval } from "./interval.js";
13
13
  /**
14
14
  * A lazy sequence of intervals.
15
15
  *
16
- * **Contract, which every producer must uphold:** intervals arrive sorted by
17
- * start, do not overlap, and are already coalesced — no two touching intervals
18
- * where one would do. The sweeps below are single-pass and rely on it; a
19
- * producer that breaks it produces wrong answers rather than errors.
16
+ * **Contract, which every producer must uphold:** intervals arrive in
17
+ * *ascending* order of start, do not overlap, and are already coalesced — no
18
+ * two touching intervals where one would do. The sweeps below are single-pass
19
+ * and rely on all three; a producer that breaks one produces wrong answers
20
+ * rather than errors.
21
+ *
22
+ * Ascending is stated rather than implied because the opposite is a real thing
23
+ * to want later: "when did this last open" wants to walk backwards. A
24
+ * descending stream would satisfy every other clause here while being read
25
+ * wrongly by all of it, so if one is ever added it needs to be a distinct type
26
+ * rather than a stream that happens to run the other way.
20
27
  */
21
28
  export type IntervalStream = Iterable<Interval>;
22
29
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"interval-stream.d.ts","sourceRoot":"","sources":["../src/interval-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAIL,KAAK,QAAQ,EAMd,MAAM,eAAe,CAAC;AAGvB;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAEhD;;;;;;;GAOG;AACH,wBAAiB,SAAS,CACxB,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,cAAc,GACpB,cAAc,CAwBhB;AAED;;;;;GAKG;AACH,wBAAiB,KAAK,CACpB,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,cAAc,GACpB,cAAc,CAgChB;AA8BD;;;;;;GAMG;AACH,wBAAiB,UAAU,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAyClE;AAED;;;;;;GAMG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,GAAG,cAAc,CAM7E"}
1
+ {"version":3,"file":"interval-stream.d.ts","sourceRoot":"","sources":["../src/interval-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAIL,KAAK,QAAQ,EAMd,MAAM,eAAe,CAAC;AAGvB;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAEhD;;;;;;;GAOG;AACH,wBAAiB,SAAS,CACxB,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,cAAc,GACpB,cAAc,CAwBhB;AAED;;;;;GAKG;AACH,wBAAiB,KAAK,CACpB,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,cAAc,GACpB,cAAc,CAgChB;AA8BD;;;;;;GAMG;AACH,wBAAiB,UAAU,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAyClE;AAED;;;;;;GAMG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,GAAG,cAAc,CAM7E"}
@@ -1 +1 @@
1
- {"version":3,"file":"interval-stream.js","sourceRoot":"","sources":["../src/interval-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACL,WAAW,EACX,aAAa,EACb,UAAU,EAEV,OAAO,EACP,QAAQ,EACR,UAAU,EACV,mBAAmB,EACnB,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAiB,QAAQ,EAAE,MAAM,aAAa,CAAC;AAYtD;;;;;;;GAOG;AACH,MAAM,SAAS,CAAC,CAAC,SAAS,CACxB,IAAoB,EACpB,KAAqB;IAErB,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACzB,MAAM,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE1B,SAAS,CAAC;QACR,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACnB,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACnB,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;YACvC,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,GAAG,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;YAChC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;QACvB,CAAC;QAED,0EAA0E;QAC1E,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,CAAC,CAAC,IAAI,EAAE,CAAC;QACX,CAAC;aAAM,CAAC;YACN,CAAC,CAAC,IAAI,EAAE,CAAC;QACX,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,SAAS,CAAC,CAAC,KAAK,CACpB,IAAoB,EACpB,KAAqB;IAErB,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACzB,MAAM,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAI,IAA0B,CAAC;IAE/B,SAAS,CAAC;QACR,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,MAAM;QACR,CAAC;QAED,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,GAAG,IAAI,CAAC;QACd,CAAC;aAAM,IAAI,mBAAmB,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACrD,IAAI,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAClE,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC;YACX,IAAI,GAAG,IAAI,CAAC;QACd,CAAC;QAED,oEAAoE;QACpE,2EAA2E;QAC3E,yEAAyE;QACzE,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,IAAI,CAAC;YACX,OAAO;QACT,CAAC;IACH,CAAC;IAED,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,CAAC;IACb,CAAC;AACH,CAAC;AAED,+DAA+D;AAC/D,SAAS,WAAW,CAClB,CAAqB,EACrB,CAAqB;IAErB,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACnB,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAEnB,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;QACpB,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;YACpB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,CAAC,CAAC,IAAI,EAAE,CAAC;QACT,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;QACpB,CAAC,CAAC,IAAI,EAAE,CAAC;QACT,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,aAAa,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,CAAC,CAAC,IAAI,EAAE,CAAC;QACT,OAAO,CAAC,CAAC;IACX,CAAC;IACD,CAAC,CAAC,IAAI,EAAE,CAAC;IACT,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;;;GAMG;AACH,MAAM,SAAS,CAAC,CAAC,UAAU,CAAC,MAAsB;IAChD,oEAAoE;IACpE,IAAI,MAA0C,CAAC;IAC/C,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,KAAK,MAAM,QAAQ,IAAI,MAAM,EAAE,CAAC;QAC9B,uEAAuE;QACvE,0EAA0E;QAC1E,2EAA2E;QAC3E,sCAAsC;QACtC,IAAI,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YACtB,SAAS;QACX,CAAC;QAED,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,IAAI,CAAC;YACf,0EAA0E;YAC1E,sEAAsE;YACtE,wEAAwE;YACxE,6DAA6D;YAC7D,IAAI,QAAQ,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBACjC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;YAClD,CAAC;QACH,CAAC;aAAM,IACL,MAAM,KAAK,SAAS;YACpB,QAAQ,CAAC,KAAK,KAAK,SAAS;YAC5B,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,EAC1D,CAAC;YACD,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;QAC/C,CAAC;QAED,IAAI,QAAQ,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC/B,kEAAkE;YAClE,OAAO;QACT,CAAC;QACD,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC;IACxB,CAAC;IAED,yEAAyE;IACzE,2EAA2E;IAC3E,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;AAChE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,IAAI,CAAC,MAAsB,EAAE,MAAgB;IAC3D,6EAA6E;IAC7E,wEAAwE;IACxE,0EAA0E;IAC1E,iBAAiB;IACjB,OAAO,SAAS,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;AACrC,CAAC"}
1
+ {"version":3,"file":"interval-stream.js","sourceRoot":"","sources":["../src/interval-stream.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * What overlap means when two layers claim the same moment.
3
+ *
4
+ * Precedence answers it one way. The later layer wins and the earlier one is
5
+ * displaced, which is what a rota and a schedule want: one person is on call,
6
+ * and a shop is open or it is shut. Some domains want the other answer. Two
7
+ * teams each putting three people on a Monday have six people on that Monday,
8
+ * and a tariff built from a standing charge and a peak rate is the sum of
9
+ * them.
10
+ *
11
+ * The strategy is a name in the document rather than a function passed to
12
+ * `resolve`. A function cannot be stored, so a cascade carrying one would be a
13
+ * document that no longer says what it means, and two readers of the same
14
+ * stored cascade could disagree about the answer. The cost is a closed
15
+ * vocabulary, which is the same trade the rule language already makes.
16
+ */
17
+ /**
18
+ * How overlapping layers combine.
19
+ *
20
+ * - `override` displaces. The later layer wins outright, and this is what a
21
+ * cascade does when it says nothing.
22
+ * - `sum`, `max` and `min` are arithmetic over numbers.
23
+ * - `concat` joins arrays, which is how a moment claimed by three layers comes
24
+ * back carrying all three.
25
+ */
26
+ export type MergeStrategy = "override" | "sum" | "max" | "min" | "concat";
27
+ export declare const MERGE_STRATEGIES: readonly MergeStrategy[];
28
+ /**
29
+ * Combines the value already accumulated with the one a later layer assigns.
30
+ *
31
+ * `under` is everything below, already folded. `over` is the layer being
32
+ * added. The order matters for `override` and for nothing else, which is why
33
+ * it is stated rather than left to the reader.
34
+ */
35
+ export type Merge<V> = (under: V, over: V) => V;
36
+ /**
37
+ * The function a named strategy stands for.
38
+ *
39
+ * An absent strategy is `override`, so a cascade written before merging
40
+ * existed means exactly what it did then.
41
+ */
42
+ export declare function mergeBy<V>(strategy: MergeStrategy | undefined): Merge<V>;
43
+ //# sourceMappingURL=merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAC;AAE1E,eAAO,MAAM,gBAAgB,EAAE,SAAS,aAAa,EAMpD,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC;AAuChD;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,GAAG,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CA6BxE"}
package/dist/merge.js ADDED
@@ -0,0 +1,79 @@
1
+ /**
2
+ * What overlap means when two layers claim the same moment.
3
+ *
4
+ * Precedence answers it one way. The later layer wins and the earlier one is
5
+ * displaced, which is what a rota and a schedule want: one person is on call,
6
+ * and a shop is open or it is shut. Some domains want the other answer. Two
7
+ * teams each putting three people on a Monday have six people on that Monday,
8
+ * and a tariff built from a standing charge and a peak rate is the sum of
9
+ * them.
10
+ *
11
+ * The strategy is a name in the document rather than a function passed to
12
+ * `resolve`. A function cannot be stored, so a cascade carrying one would be a
13
+ * document that no longer says what it means, and two readers of the same
14
+ * stored cascade could disagree about the answer. The cost is a closed
15
+ * vocabulary, which is the same trade the rule language already makes.
16
+ */
17
+ export const MERGE_STRATEGIES = [
18
+ "override",
19
+ "sum",
20
+ "max",
21
+ "min",
22
+ "concat",
23
+ ];
24
+ /** What a value looks like, for an error message. */
25
+ function nameOf(value) {
26
+ if (value === null) {
27
+ return "null";
28
+ }
29
+ return Array.isArray(value) ? "an array" : typeof value;
30
+ }
31
+ /**
32
+ * The vocabulary is checked when a document is parsed. What the values in it
33
+ * turn out to be is only known once a cascade is resolved, so a `sum` over
34
+ * names fails here rather than at the boundary.
35
+ *
36
+ * The same split the rule language keeps. `parse.ts` checks shape and
37
+ * vocabulary, and meaning is settled where the thing is evaluated.
38
+ */
39
+ function wrongType(strategy, holds, found) {
40
+ throw new TypeError(`A cascade merging by "${strategy}" carries ${holds}, and this one holds ` +
41
+ `${nameOf(found)}. Give it values it can combine, or merge by "override".`);
42
+ }
43
+ function asNumber(strategy, value) {
44
+ return typeof value === "number"
45
+ ? value
46
+ : wrongType(strategy, "numbers", value);
47
+ }
48
+ function asArray(strategy, value) {
49
+ return Array.isArray(value) ? value : wrongType(strategy, "arrays", value);
50
+ }
51
+ /**
52
+ * The function a named strategy stands for.
53
+ *
54
+ * An absent strategy is `override`, so a cascade written before merging
55
+ * existed means exactly what it did then.
56
+ */
57
+ export function mergeBy(strategy) {
58
+ switch (strategy) {
59
+ case "sum": {
60
+ return (under, over) => (asNumber("sum", under) + asNumber("sum", over));
61
+ }
62
+ case "max": {
63
+ return (under, over) => Math.max(asNumber("max", under), asNumber("max", over));
64
+ }
65
+ case "min": {
66
+ return (under, over) => Math.min(asNumber("min", under), asNumber("min", over));
67
+ }
68
+ case "concat": {
69
+ return (under, over) => [...asArray("concat", under), ...asArray("concat", over)];
70
+ }
71
+ case "override":
72
+ case undefined: {
73
+ // The later layer displaces what is under it, which is the precedence a
74
+ // cascade has always had and what one says by saying nothing.
75
+ return (_under, over) => over;
76
+ }
77
+ }
78
+ }
79
+ //# sourceMappingURL=merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAaH,MAAM,CAAC,MAAM,gBAAgB,GAA6B;IACxD,UAAU;IACV,KAAK;IACL,KAAK;IACL,KAAK;IACL,QAAQ;CACT,CAAC;AAWF,qDAAqD;AACrD,SAAS,MAAM,CAAC,KAAc;IAC5B,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC;AAC1D,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,SAAS,CAChB,QAAuB,EACvB,KAAa,EACb,KAAc;IAEd,MAAM,IAAI,SAAS,CACjB,yBAAyB,QAAQ,aAAa,KAAK,uBAAuB;QACxE,GAAG,MAAM,CAAC,KAAK,CAAC,0DAA0D,CAC7E,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,QAAuB,EAAE,KAAc;IACvD,OAAO,OAAO,KAAK,KAAK,QAAQ;QAC9B,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,SAAS,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;AAC5C,CAAC;AAED,SAAS,OAAO,CAAC,QAAuB,EAAE,KAAc;IACtD,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAAI,QAAmC;IAC5D,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CACrB,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAM,CAAC;QAC1D,CAAC;QAED,KAAK,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CACrB,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAM,CAAC;QACjE,CAAC;QAED,KAAK,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CACrB,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAM,CAAC;QACjE,CAAC;QAED,KAAK,QAAQ,EAAE,CAAC;YACd,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CACrB,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAM,CAAC;QACnE,CAAC;QAED,KAAK,UAAU,CAAC;QAChB,KAAK,SAAS,EAAE,CAAC;YACf,wEAAwE;YACxE,8DAA8D;YAC9D,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;QAChC,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Turning arbitrary JSON back into a cascade.
3
+ *
4
+ * The boundary `parseRule` keeps, one level up. A cascade is stored, sent and
5
+ * edited the way a rule is, so what comes back is whatever a database row, an
6
+ * API body or a form actually held, and the useful thing to do with a bad one
7
+ * is say precisely what is wrong and where.
8
+ *
9
+ * One thing differs, and it is why this takes an argument `parseRule` does
10
+ * not. The rule vocabulary is closed, so `parseRule` knows every rule there
11
+ * is. The values in a cascade are the caller's own domain type: a name, a
12
+ * tariff, a headcount. Quando has never seen one and has nothing to check it
13
+ * against, so the caller supplies the function that reads one.
14
+ */
15
+ import type { Cascade } from "./cascade.js";
16
+ /**
17
+ * Reads one stored value back at the type the caller keeps it in, or throws
18
+ * saying what is wrong.
19
+ *
20
+ * The `path` says where in the document the value sits, and belongs at the
21
+ * front of whatever the function throws. {@link fail} writes one in the same
22
+ * form the rest of parsing uses.
23
+ */
24
+ export type ValueParser<V> = (value: unknown, path: string) => V;
25
+ /**
26
+ * A cascade from unknown JSON, or a `TypeError` saying what is wrong and where.
27
+ *
28
+ * ```ts
29
+ * const onCall = parseCascade(JSON.parse(stored), asString);
30
+ * ```
31
+ *
32
+ * The `path` is what appears in front of every message, so a value six layers
33
+ * down reports as `cascade.layers[2].replace.layers[0].value` rather than as a
34
+ * puzzle.
35
+ */
36
+ export declare function parseCascade<V>(value: unknown, parseValue: ValueParser<V>, path?: string): Cascade<V>;
37
+ //# sourceMappingURL=parse-cascade.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-cascade.d.ts","sourceRoot":"","sources":["../src/parse-cascade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAS,MAAM,cAAc,CAAC;AAKnD;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC,CAAC;AAKjE;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAC5B,KAAK,EAAE,OAAO,EACd,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC,EAC1B,IAAI,SAAY,GACf,OAAO,CAAC,CAAC,CAAC,CA6BZ"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Turning arbitrary JSON back into a cascade.
3
+ *
4
+ * The boundary `parseRule` keeps, one level up. A cascade is stored, sent and
5
+ * edited the way a rule is, so what comes back is whatever a database row, an
6
+ * API body or a form actually held, and the useful thing to do with a bad one
7
+ * is say precisely what is wrong and where.
8
+ *
9
+ * One thing differs, and it is why this takes an argument `parseRule` does
10
+ * not. The rule vocabulary is closed, so `parseRule` knows every rule there
11
+ * is. The values in a cascade are the caller's own domain type: a name, a
12
+ * tariff, a headcount. Quando has never seen one and has nothing to check it
13
+ * against, so the caller supplies the function that reads one.
14
+ */
15
+ import { MERGE_STRATEGIES } from "./merge.js";
16
+ import { asRecord, checkFields, fail, shapeOf } from "./parse-shape.js";
17
+ import { parseRule } from "./parse.js";
18
+ const CASCADE_FIELDS = ["merge", "layers"];
19
+ const LAYER_FIELDS = ["scope", "value", "replace"];
20
+ /**
21
+ * A cascade from unknown JSON, or a `TypeError` saying what is wrong and where.
22
+ *
23
+ * ```ts
24
+ * const onCall = parseCascade(JSON.parse(stored), asString);
25
+ * ```
26
+ *
27
+ * The `path` is what appears in front of every message, so a value six layers
28
+ * down reports as `cascade.layers[2].replace.layers[0].value` rather than as a
29
+ * puzzle.
30
+ */
31
+ export function parseCascade(value, parseValue, path = "cascade") {
32
+ const node = asRecord(value, path, "a cascade object");
33
+ const type = node["type"];
34
+ if (type !== "cascade") {
35
+ return fail(`${path}.type`, typeof type === "string"
36
+ ? `expected "cascade", found "${type}"`
37
+ : `expected "cascade", found ${shapeOf(type)}`);
38
+ }
39
+ checkFields(node, CASCADE_FIELDS, path, "a cascade");
40
+ const layers = node["layers"];
41
+ if (!Array.isArray(layers)) {
42
+ return fail(`${path}.layers`, `expected an array of layers, found ${shapeOf(layers)}`);
43
+ }
44
+ return {
45
+ type: "cascade",
46
+ ...mergePart(node, path),
47
+ layers: layers.map((layer, index) => parseLayer(layer, parseValue, `${path}.layers[${index}]`)),
48
+ };
49
+ }
50
+ /**
51
+ * Present or absent, never present-and-undefined, so a cascade that says
52
+ * nothing about merging serialises back to the document it came from.
53
+ *
54
+ * The name is checked here. Whether the values it will be handed are ones it
55
+ * can combine is not, because that is only known once the cascade is resolved.
56
+ */
57
+ function mergePart(node, path) {
58
+ const merge = node["merge"];
59
+ if (merge === undefined) {
60
+ return {};
61
+ }
62
+ if (!MERGE_STRATEGIES.includes(merge)) {
63
+ return fail(`${path}.merge`, typeof merge === "string"
64
+ ? `"${merge}" is not a merge strategy. Expected one of ${MERGE_STRATEGIES.join(", ")}`
65
+ : `expected a merge strategy, found ${shapeOf(merge)}`);
66
+ }
67
+ return { merge: merge };
68
+ }
69
+ /**
70
+ * One layer: where it applies, and what applies there.
71
+ *
72
+ * Order of complaint follows order of reading. What the layer covers is
73
+ * checked before what holds inside it, because a layer whose scope is broken
74
+ * has nothing worth saying about its value.
75
+ */
76
+ function parseLayer(value, parseValue, path) {
77
+ const node = asRecord(value, path, "a layer object");
78
+ // `checkFields` waves `type` past, because every rule and every cascade
79
+ // carries one. A layer does not, and a document that puts one here has
80
+ // confused the layer with the cascade holding it.
81
+ if ("type" in node) {
82
+ fail(`${path}.type`, "is not a field of a layer. The cascade around it carries the type");
83
+ }
84
+ checkFields(node, LAYER_FIELDS, path, "a layer");
85
+ const scope = parseRule(node["scope"], `${path}.scope`);
86
+ const holds = LAYER_FIELDS.filter((field) => field !== "scope" && field in node);
87
+ if (holds.length === 2) {
88
+ return fail(path, "has both a value and a replace, and a layer holds one or the other. " +
89
+ "A value applies across the whole scope, and a replace hands the " +
90
+ "scope to another cascade");
91
+ }
92
+ if (holds[0] === "replace") {
93
+ return {
94
+ scope,
95
+ replace: parseCascade(node["replace"], parseValue, `${path}.replace`),
96
+ };
97
+ }
98
+ if (holds[0] === "value") {
99
+ return { scope, value: parseValue(node["value"], `${path}.value`) };
100
+ }
101
+ return fail(path, "has neither a value nor a replace, so nothing holds inside its scope. " +
102
+ "A layer built with `undefined` as its value arrives this way, because " +
103
+ "`JSON.stringify` drops the field rather than writing it");
104
+ }
105
+ //# sourceMappingURL=parse-cascade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-cascade.js","sourceRoot":"","sources":["../src/parse-cascade.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Checking one field of an incoming rule document.
3
+ *
4
+ * These are the checks that know about time: a day of the week is one of
5
+ * seven names, a date is a date, a zone is one the runtime has heard of. Each
6
+ * either returns the value at the type it claims to be, or throws saying what
7
+ * was found instead and where.
8
+ *
9
+ * [parse-shape.ts](./parse-shape.ts) holds the ones underneath, which check
10
+ * that JSON is the shape it claims to be and know nothing about time.
11
+ */
12
+ import { type Weekday } from "./rule.js";
13
+ export declare function asDays(value: unknown, path: string): Weekday[];
14
+ /** Checked by construction, so a malformed time is caught where it is written. */
15
+ export declare function asTime(value: unknown, path: string): string;
16
+ export declare function asDates(value: unknown, path: string): string[];
17
+ /**
18
+ * A zone is checked here rather than left to fail at query time, because a
19
+ * mistyped one in a stored rule should be a problem when the rule is read, not
20
+ * hours later when something asks a question of it.
21
+ */
22
+ export declare function asZone(value: unknown, path: string): string;
23
+ /** Present or absent, never present-and-undefined. */
24
+ export declare function zonePart(node: Record<string, unknown>, path: string): {
25
+ zone?: string;
26
+ };
27
+ //# sourceMappingURL=parse-fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-fields.d.ts","sourceRoot":"","sources":["../src/parse-fields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAY,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAInD,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAS9D;AAED,kFAAkF;AAClF,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAW3D;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAY9D;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAW3D;AAED,sDAAsD;AACtD,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,IAAI,EAAE,MAAM,GACX;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAGnB"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Checking one field of an incoming rule document.
3
+ *
4
+ * These are the checks that know about time: a day of the week is one of
5
+ * seven names, a date is a date, a zone is one the runtime has heard of. Each
6
+ * either returns the value at the type it claims to be, or throws saying what
7
+ * was found instead and where.
8
+ *
9
+ * [parse-shape.ts](./parse-shape.ts) holds the ones underneath, which check
10
+ * that JSON is the shape it claims to be and know nothing about time.
11
+ */
12
+ import { asString, asStrings, fail } from "./parse-shape.js";
13
+ import { WEEKDAYS } from "./rule.js";
14
+ const WEEKDAY_NAMES = new Set(WEEKDAYS);
15
+ export function asDays(value, path) {
16
+ return asStrings(value, path).map((day, index) => WEEKDAY_NAMES.has(day)
17
+ ? day
18
+ : fail(`${path}[${index}]`, `"${day}" is not a day of the week. Expected one of ${WEEKDAYS.join(", ")}`));
19
+ }
20
+ /** Checked by construction, so a malformed time is caught where it is written. */
21
+ export function asTime(value, path) {
22
+ const time = asString(value, path);
23
+ try {
24
+ Temporal.PlainTime.from(time);
25
+ }
26
+ catch {
27
+ return fail(path, `"${time}" is not a time of day. Expected something like "09:00"`);
28
+ }
29
+ return time;
30
+ }
31
+ export function asDates(value, path) {
32
+ return asStrings(value, path).map((date, index) => {
33
+ try {
34
+ Temporal.PlainDate.from(date);
35
+ }
36
+ catch {
37
+ return fail(`${path}[${index}]`, `"${date}" is not a date. Expected something like "2026-03-14"`);
38
+ }
39
+ return date;
40
+ });
41
+ }
42
+ /**
43
+ * A zone is checked here rather than left to fail at query time, because a
44
+ * mistyped one in a stored rule should be a problem when the rule is read, not
45
+ * hours later when something asks a question of it.
46
+ */
47
+ export function asZone(value, path) {
48
+ const zone = asString(value, path);
49
+ try {
50
+ Temporal.PlainDate.from("2000-01-01").toZonedDateTime({
51
+ timeZone: zone,
52
+ plainTime: "00:00",
53
+ });
54
+ }
55
+ catch {
56
+ return fail(path, `"${zone}" is not a known time zone`);
57
+ }
58
+ return zone;
59
+ }
60
+ /** Present or absent, never present-and-undefined. */
61
+ export function zonePart(node, path) {
62
+ const zone = node["zone"];
63
+ return zone === undefined ? {} : { zone: asZone(zone, `${path}.zone`) };
64
+ }
65
+ //# sourceMappingURL=parse-fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-fields.js","sourceRoot":"","sources":["../src/parse-fields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAgB,MAAM,WAAW,CAAC;AAEnD,MAAM,aAAa,GAAG,IAAI,GAAG,CAAS,QAAQ,CAAC,CAAC;AAEhD,MAAM,UAAU,MAAM,CAAC,KAAc,EAAE,IAAY;IACjD,OAAO,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAC/C,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC;QACpB,CAAC,CAAE,GAAe;QAClB,CAAC,CAAC,IAAI,CACF,GAAG,IAAI,IAAI,KAAK,GAAG,EACnB,IAAI,GAAG,+CAA+C,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC5E,CACN,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,MAAM,CAAC,KAAc,EAAE,IAAY;IACjD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC;QACH,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CACT,IAAI,EACJ,IAAI,IAAI,yDAAyD,CAClE,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,KAAc,EAAE,IAAY;IAClD,OAAO,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAChD,IAAI,CAAC;YACH,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CACT,GAAG,IAAI,IAAI,KAAK,GAAG,EACnB,IAAI,IAAI,uDAAuD,CAChE,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAC,KAAc,EAAE,IAAY;IACjD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC;QACH,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,eAAe,CAAC;YACpD,QAAQ,EAAE,IAAI;YACd,SAAS,EAAE,OAAO;SACnB,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,4BAA4B,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,QAAQ,CACtB,IAA6B,EAC7B,IAAY;IAEZ,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1B,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,OAAO,CAAC,EAAE,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Checking that incoming JSON is the shape it claims to be.
3
+ *
4
+ * Nothing here knows anything about time. These are the checks any document
5
+ * needs on the way in, and they say what was found instead and where. The
6
+ * `path` threaded through them is what turns "not a string" into
7
+ * `rule.rules[1].rules[0].dates[0]: expected a string, found number`.
8
+ *
9
+ * [parse-fields.ts](./parse-fields.ts) sits on top with the checks that do
10
+ * know about time, such as days of the week and zones.
11
+ */
12
+ export declare function fail(path: string, problem: string): never;
13
+ /** What a value looks like, for an error message. */
14
+ export declare function shapeOf(value: unknown): string;
15
+ /**
16
+ * `expected` names what should have been there, and reads straight into the
17
+ * message. "a rule object", "a layer object".
18
+ */
19
+ export declare function asRecord(value: unknown, path: string, expected: string): Record<string, unknown>;
20
+ /**
21
+ * Refuses a field the thing being parsed does not have.
22
+ *
23
+ * Quietly ignoring one is the worse option by some distance. A rule document
24
+ * carrying `"zonee"` would parse as a perfectly valid rule with no zone, which
25
+ * is a *different schedule* read in whatever zone the query happened to use,
26
+ * and nothing would have said so.
27
+ *
28
+ * The cost is that a document written by a later version of Quando, carrying a
29
+ * field this one has not heard of, is rejected rather than tolerated. That is
30
+ * the right way round. A field exists to change what a document means, so
31
+ * ignoring an unknown one is agreeing to get the answer wrong quietly.
32
+ *
33
+ * `type` is exempt, because every caller has already read it to get here.
34
+ */
35
+ export declare function checkFields(node: Record<string, unknown>, allowed: readonly string[], path: string, what: string): void;
36
+ export declare function asString(value: unknown, path: string): string;
37
+ export declare function asBoolean(value: unknown, path: string): boolean;
38
+ export declare function asStrings(value: unknown, path: string): string[];
39
+ //# sourceMappingURL=parse-shape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-shape.d.ts","sourceRoot":"","sources":["../src/parse-shape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CAEzD;AAED,qDAAqD;AACrD,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAQ9C;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKzB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,IAAI,CAWN;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI7D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAI/D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAShE"}