@connextar/house 0.10.0 → 0.11.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 (42) hide show
  1. package/README.md +447 -0
  2. package/dist/jobs/counts.d.ts +82 -0
  3. package/dist/jobs/counts.d.ts.map +1 -0
  4. package/dist/jobs/counts.js +112 -0
  5. package/dist/jobs/counts.js.map +1 -0
  6. package/dist/jobs/cron-auth.d.ts +44 -0
  7. package/dist/jobs/cron-auth.d.ts.map +1 -0
  8. package/dist/jobs/cron-auth.js +20 -0
  9. package/dist/jobs/cron-auth.js.map +1 -0
  10. package/dist/jobs/health.d.ts +157 -0
  11. package/dist/jobs/health.d.ts.map +1 -0
  12. package/dist/jobs/health.js +146 -0
  13. package/dist/jobs/health.js.map +1 -0
  14. package/dist/jobs/index.d.ts +26 -0
  15. package/dist/jobs/index.d.ts.map +1 -0
  16. package/dist/jobs/index.js +26 -0
  17. package/dist/jobs/index.js.map +1 -0
  18. package/dist/jobs/job-health-list.d.ts +66 -0
  19. package/dist/jobs/job-health-list.d.ts.map +1 -0
  20. package/dist/jobs/job-health-list.js +115 -0
  21. package/dist/jobs/job-health-list.js.map +1 -0
  22. package/dist/jobs/react-index.d.ts +19 -0
  23. package/dist/jobs/react-index.d.ts.map +1 -0
  24. package/dist/jobs/react-index.js +18 -0
  25. package/dist/jobs/react-index.js.map +1 -0
  26. package/dist/jobs/registry.d.ts +104 -0
  27. package/dist/jobs/registry.d.ts.map +1 -0
  28. package/dist/jobs/registry.js +72 -0
  29. package/dist/jobs/registry.js.map +1 -0
  30. package/dist/jobs/scheduler.d.ts +89 -0
  31. package/dist/jobs/scheduler.d.ts.map +1 -0
  32. package/dist/jobs/scheduler.js +282 -0
  33. package/dist/jobs/scheduler.js.map +1 -0
  34. package/dist/jobs/store.d.ts +94 -0
  35. package/dist/jobs/store.d.ts.map +1 -0
  36. package/dist/jobs/store.js +56 -0
  37. package/dist/jobs/store.js.map +1 -0
  38. package/dist/jobs/windows.d.ts +66 -0
  39. package/dist/jobs/windows.d.ts.map +1 -0
  40. package/dist/jobs/windows.js +130 -0
  41. package/dist/jobs/windows.js.map +1 -0
  42. package/package.json +9 -1
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Run windows: which window a moment falls in, or none.
3
+ *
4
+ * A window is a function from "now" to a key, or `null` when the job is not due
5
+ * at all. The key is what the ledger's unique index is on, together with the
6
+ * job, so **one run per job per key** is the whole of idempotency: a second tick
7
+ * in the same window finds the key taken and does nothing.
8
+ *
9
+ * Both apps wrote this as two functions — `attempt(now)` and `windowKey(now)` —
10
+ * which could disagree. One function cannot.
11
+ *
12
+ * The helpers carry one lesson each:
13
+ *
14
+ * - **A day is a day somewhere.** AltEd keyed its tracker job on the UTC date
15
+ * while the job took "yesterday" from the UK calendar; in summer a tick after
16
+ * 23:00 UTC and the next at 00:00 UTC fell in different windows but the same
17
+ * UK day, so that day's misses were sent twice and the next day's never. Every
18
+ * calendar helper takes the `timeZone` the job's own idea of a day comes from.
19
+ * - **Not before a civil hour.** `from` keeps a morning email out of midnight —
20
+ * AltEd's weekly digest waits for 07:00 "so nobody's phone lights up".
21
+ * - **A missed start is caught up, not lost.** Quickie's monthly report ran only
22
+ * on the 1st, so a day of failed ticks on the 1st lost the month. `weekly` and
23
+ * `monthly` stay open from their start to the end of their period (after
24
+ * `from` each day), and the key keeps it to one run.
25
+ *
26
+ * Keys match what both apps already store (`2026-09-30`, `2026-09-30T14`,
27
+ * `2026-09-30Q2`, `month:2026-09`), so moving a job onto a helper does not run
28
+ * it a second time in the window it moves in.
29
+ */
30
+ const WEEKDAYS = { Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6, Sun: 7 };
31
+ const formatters = new Map();
32
+ function zoned(now, timeZone) {
33
+ let format = formatters.get(timeZone);
34
+ if (!format) {
35
+ format = new Intl.DateTimeFormat("en-US", {
36
+ timeZone,
37
+ year: "numeric",
38
+ month: "2-digit",
39
+ day: "2-digit",
40
+ hour: "2-digit",
41
+ hourCycle: "h23",
42
+ weekday: "short",
43
+ });
44
+ formatters.set(timeZone, format);
45
+ }
46
+ const parts = Object.fromEntries(format.formatToParts(now).map((part) => [part.type, part.value]));
47
+ return {
48
+ year: Number(parts.year),
49
+ month: Number(parts.month),
50
+ day: Number(parts.day),
51
+ hour: Number(parts.hour),
52
+ weekday: WEEKDAYS[parts.weekday ?? ""] ?? 1,
53
+ };
54
+ }
55
+ const pad = (value, width = 2) => String(value).padStart(width, "0");
56
+ /** `YYYY-MM-DD` for a calendar date, moved by whole days without touching a clock. */
57
+ function dateKey(year, month, day, plusDays = 0) {
58
+ const date = new Date(Date.UTC(year, month - 1, day + plusDays));
59
+ return `${pad(date.getUTCFullYear(), 4)}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}`;
60
+ }
61
+ function checkHour(from) {
62
+ if (!Number.isInteger(from) || from < 0 || from > 23)
63
+ throw new RangeError(`"from" must be an hour, 0–23 (got ${from}).`);
64
+ }
65
+ /** Once an hour. Key `2026-09-30T14` (UTC) — Quickie's hourly key. */
66
+ export function hourly() {
67
+ return (now) => `${now.toISOString().slice(0, 13)}`;
68
+ }
69
+ /**
70
+ * Once every `hours` hours, in blocks from UTC midnight. Key `2026-09-30Q2` —
71
+ * with 6, exactly Quickie's four-a-day bank feed key.
72
+ */
73
+ export function everyHours(hours) {
74
+ if (!Number.isInteger(hours) || hours < 1 || 24 % hours !== 0) {
75
+ throw new RangeError(`everyHours takes a whole number of hours that divides the day (got ${hours}).`);
76
+ }
77
+ return (now) => `${now.toISOString().slice(0, 10)}Q${Math.floor(now.getUTCHours() / hours)}`;
78
+ }
79
+ /** Once a day, from `from` o'clock to midnight in `timeZone`. Key `2026-09-30`, the date there. */
80
+ export function daily(options = {}) {
81
+ const { from = 0, timeZone = "UTC" } = options;
82
+ checkHour(from);
83
+ return (now) => {
84
+ const at = zoned(now, timeZone);
85
+ return at.hour >= from ? dateKey(at.year, at.month, at.day) : null;
86
+ };
87
+ }
88
+ /**
89
+ * Once a week, opening on `weekday` (1 = Monday) at `from` and staying open —
90
+ * from `from` each day — until the next one opens. Key `week:2026-09-28`, the
91
+ * date it opened.
92
+ */
93
+ export function weekly(options = {}) {
94
+ const { weekday = 1, from = 0, timeZone = "UTC" } = options;
95
+ checkHour(from);
96
+ if (!Number.isInteger(weekday) || weekday < 1 || weekday > 7) {
97
+ throw new RangeError(`weekday is 1 (Monday) to 7 (Sunday) (got ${weekday}).`);
98
+ }
99
+ return (now) => {
100
+ const at = zoned(now, timeZone);
101
+ if (at.hour < from)
102
+ return null;
103
+ const since = (at.weekday - weekday + 7) % 7;
104
+ return `week:${dateKey(at.year, at.month, at.day, -since)}`;
105
+ };
106
+ }
107
+ /**
108
+ * Once a month, opening on `day` at `from` and staying open — from `from` each
109
+ * day — to the month's end. Key `month:2026-09` — Quickie's monthly key.
110
+ * `day` is 1–28, so the window opens in February too.
111
+ */
112
+ export function monthly(options = {}) {
113
+ const { day = 1, from = 0, timeZone = "UTC" } = options;
114
+ checkHour(from);
115
+ if (!Number.isInteger(day) || day < 1 || day > 28)
116
+ throw new RangeError(`day is 1–28 (got ${day}).`);
117
+ return (now) => {
118
+ const at = zoned(now, timeZone);
119
+ if (at.day < day || at.hour < from)
120
+ return null;
121
+ return `month:${pad(at.year, 4)}-${pad(at.month)}`;
122
+ };
123
+ }
124
+ /** The prefix every Run now key starts with, in both apps' existing rows and in this module's. */
125
+ export const MANUAL_KEY_PREFIX = "manual:";
126
+ /** Whether a stored window key was a Run now rather than the schedule. */
127
+ export function isManualKey(windowKey) {
128
+ return windowKey.startsWith(MANUAL_KEY_PREFIX);
129
+ }
130
+ //# sourceMappingURL=windows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"windows.js","sourceRoot":"","sources":["../../src/jobs/windows.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@connextar/house",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "The pieces every app we build needs and none of them should own a copy of.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -62,6 +62,14 @@
62
62
  "types": "./dist/grid/index.d.ts",
63
63
  "default": "./dist/grid/index.js"
64
64
  },
65
+ "./jobs": {
66
+ "types": "./dist/jobs/index.d.ts",
67
+ "default": "./dist/jobs/index.js"
68
+ },
69
+ "./jobs/react": {
70
+ "types": "./dist/jobs/react-index.d.ts",
71
+ "default": "./dist/jobs/react-index.js"
72
+ },
65
73
  "./web-search": {
66
74
  "types": "./dist/web-search/index.d.ts",
67
75
  "default": "./dist/web-search/index.js"