@khorsheed/dsh-reader 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 (58) hide show
  1. package/README.en.md +90 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +90 -0
  4. package/cordis.patch.yml +13 -0
  5. package/lib/client.js +15119 -0
  6. package/lib/index.js +3660 -0
  7. package/lib/invariant.js +38 -0
  8. package/lib/typert.host.d.ts +3 -0
  9. package/lib/typert.host.js +1738 -0
  10. package/lib/typert.remote-client.d.ts +82 -0
  11. package/lib/typert.remote-client.js +1115 -0
  12. package/lib/types/arxiv.d.ts +47 -0
  13. package/lib/types/arxiv.js +76 -0
  14. package/lib/types/client/ReaderPane.d.ts +71 -0
  15. package/lib/types/client/ReaderPane.js +3135 -0
  16. package/lib/types/client/contract.d.ts +254 -0
  17. package/lib/types/client/contract.js +1 -0
  18. package/lib/types/client/definition.d.ts +27 -0
  19. package/lib/types/client/definition.js +32 -0
  20. package/lib/types/client/extract-article.d.ts +102 -0
  21. package/lib/types/client/extract-article.js +1024 -0
  22. package/lib/types/client/icons.d.ts +27 -0
  23. package/lib/types/client/icons.js +11 -0
  24. package/lib/types/client/index.d.ts +34 -0
  25. package/lib/types/client/index.js +222 -0
  26. package/lib/types/client/locales.d.ts +391 -0
  27. package/lib/types/client/locales.js +382 -0
  28. package/lib/types/client/parse-rss.d.ts +69 -0
  29. package/lib/types/client/parse-rss.js +656 -0
  30. package/lib/types/client/quote.d.ts +89 -0
  31. package/lib/types/client/quote.js +119 -0
  32. package/lib/types/client/selectors.d.ts +167 -0
  33. package/lib/types/client/selectors.js +413 -0
  34. package/lib/types/client/session.d.ts +171 -0
  35. package/lib/types/client/session.js +223 -0
  36. package/lib/types/client/store.d.ts +181 -0
  37. package/lib/types/client/store.js +226 -0
  38. package/lib/types/client/translate.d.ts +379 -0
  39. package/lib/types/client/translate.js +873 -0
  40. package/lib/types/index.d.ts +48 -0
  41. package/lib/types/index.js +25 -0
  42. package/lib/types/invariant.d.ts +29 -0
  43. package/lib/types/invariant.js +38 -0
  44. package/lib/types/link-resolvers.d.ts +107 -0
  45. package/lib/types/link-resolvers.js +299 -0
  46. package/lib/types/remote.d.ts +224 -0
  47. package/lib/types/remote.js +275 -0
  48. package/lib/types/schedule.d.ts +78 -0
  49. package/lib/types/schedule.js +114 -0
  50. package/lib/types/service.d.ts +633 -0
  51. package/lib/types/service.js +1697 -0
  52. package/lib/types/store.d.ts +224 -0
  53. package/lib/types/store.js +916 -0
  54. package/lib/types/types.d.ts +710 -0
  55. package/lib/types/types.js +182 -0
  56. package/locale/en.json +6 -0
  57. package/locale/zh.json +6 -0
  58. package/package.json +128 -0
@@ -0,0 +1,114 @@
1
+ /**
2
+ * The daily-refresh clock: pure date math, deliberately separated from the
3
+ * service that arms it.
4
+ *
5
+ * There is no host scheduler to hang a "refresh every morning" job on — the
6
+ * host's `ctx.timer` is fiber-scoped `timeout`/`interval`/`throttle`/`debounce`
7
+ * only, and `dsh-schedule` is agent-session reminders rather than a service a
8
+ * plugin may schedule against. So the plugin owns a `setTimeout` and the only
9
+ * interesting part is deciding *when* — which is what lives here, as functions
10
+ * that take a `now` and are therefore testable without a clock.
11
+ *
12
+ * @module @khorsheed/dsh-reader/schedule
13
+ */
14
+ /** Milliseconds in one day. */
15
+ const DAY_MS = 24 * 60 * 60 * 1000;
16
+ /**
17
+ * Parse an `HH:MM` string into hours and minutes.
18
+ *
19
+ * A malformed value falls back to the caller's default rather than throwing: a
20
+ * hand-edited state file must not be able to break the refresh loop.
21
+ *
22
+ * @param value - the configured time of day.
23
+ * @param fallback - the value to use when `value` is unusable.
24
+ * @returns the parsed hour and minute.
25
+ */
26
+ export function parseTimeOfDay(value, fallback) {
27
+ return parseOrFallback(value) ?? parseOrFallback(fallback) ?? { hour: 10, minute: 0 };
28
+ }
29
+ /** Parse one candidate, or `undefined` when it is not a valid `HH:MM`. */
30
+ function parseOrFallback(value) {
31
+ if (value === undefined)
32
+ return undefined;
33
+ const match = /^(\d{1,2}):(\d{2})$/.exec(value.trim());
34
+ if (match === null)
35
+ return undefined;
36
+ const hour = Number(match[1]);
37
+ const minute = Number(match[2]);
38
+ if (!Number.isFinite(hour) || !Number.isFinite(minute))
39
+ return undefined;
40
+ if (hour < 0 || hour > 23 || minute < 0 || minute > 59)
41
+ return undefined;
42
+ return { hour, minute };
43
+ }
44
+ /**
45
+ * The next wall-clock occurrence of `timeOfDay` strictly after `now`.
46
+ *
47
+ * Computed through the local-time constructor rather than by adding a fixed
48
+ * day length, so a DST shift moves the refresh to the same wall-clock time
49
+ * instead of drifting an hour.
50
+ *
51
+ * @param now - the reference instant.
52
+ * @param timeOfDay - the configured local time.
53
+ * @returns the next occurrence.
54
+ */
55
+ export function nextOccurrence(now, timeOfDay) {
56
+ const { hour, minute } = parseTimeOfDay(timeOfDay, '10:00');
57
+ const candidate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), hour, minute, 0, 0);
58
+ if (candidate.getTime() > now.getTime())
59
+ return candidate;
60
+ // Already past today: step to tomorrow through the constructor so the local
61
+ // calendar does the arithmetic (month ends, DST, leap days).
62
+ return new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, hour, minute, 0, 0);
63
+ }
64
+ /**
65
+ * The most recent occurrence of `timeOfDay` at or before `now` — the deadline
66
+ * a catch-up refresh compares against.
67
+ *
68
+ * @param now - the reference instant.
69
+ * @param timeOfDay - the configured local time.
70
+ * @returns the most recent occurrence.
71
+ */
72
+ export function previousOccurrence(now, timeOfDay) {
73
+ const { hour, minute } = parseTimeOfDay(timeOfDay, '10:00');
74
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate(), hour, minute, 0, 0);
75
+ if (today.getTime() <= now.getTime())
76
+ return today;
77
+ return new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1, hour, minute, 0, 0);
78
+ }
79
+ /** Delay to the next occurrence, clamped to Node's timer ceiling. */
80
+ export const MAX_TIMEOUT_MS = 2_147_483_647;
81
+ /**
82
+ * Whether a boot should refresh immediately because the last run missed its
83
+ * slot — the thing that makes "daily refresh" true on a machine that was
84
+ * asleep or whose process restarted.
85
+ *
86
+ * @param lastRun - the last completed refresh, if any.
87
+ * @param now - the current instant.
88
+ * @param timeOfDay - the configured local time.
89
+ * @param enabled - whether the schedule is on at all.
90
+ * @returns true when a catch-up run is due.
91
+ */
92
+ export function isCatchUpDue(lastRun, now, timeOfDay, enabled) {
93
+ if (!enabled)
94
+ return false;
95
+ // Never refreshed: the first refresh belongs to the user's first action or to
96
+ // the next slot, not to a boot-time stampede of every configured feed.
97
+ if (lastRun === undefined)
98
+ return false;
99
+ return previousOccurrence(now, timeOfDay).getTime() > lastRun.getTime();
100
+ }
101
+ /**
102
+ * The timer delay for the next occurrence, capped so a scheduler survives
103
+ * dates beyond the 32-bit millisecond limit.
104
+ *
105
+ * @param now - the reference instant.
106
+ * @param timeOfDay - the configured local time.
107
+ * @returns the delay in milliseconds.
108
+ */
109
+ export function delayUntilNext(now, timeOfDay) {
110
+ const delta = nextOccurrence(now, timeOfDay).getTime() - now.getTime();
111
+ return Math.min(Math.max(0, delta), MAX_TIMEOUT_MS);
112
+ }
113
+ /** Exposed for tests that want to assert a day's worth of arithmetic. */
114
+ export { DAY_MS };