@connextar/house 0.10.0 → 0.12.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.
- package/README.md +590 -0
- package/dist/jobs/counts.d.ts +82 -0
- package/dist/jobs/counts.d.ts.map +1 -0
- package/dist/jobs/counts.js +112 -0
- package/dist/jobs/counts.js.map +1 -0
- package/dist/jobs/cron-auth.d.ts +44 -0
- package/dist/jobs/cron-auth.d.ts.map +1 -0
- package/dist/jobs/cron-auth.js +20 -0
- package/dist/jobs/cron-auth.js.map +1 -0
- package/dist/jobs/health.d.ts +170 -0
- package/dist/jobs/health.d.ts.map +1 -0
- package/dist/jobs/health.js +149 -0
- package/dist/jobs/health.js.map +1 -0
- package/dist/jobs/index.d.ts +27 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/index.js +27 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/jobs/job-health-list.d.ts +78 -0
- package/dist/jobs/job-health-list.d.ts.map +1 -0
- package/dist/jobs/job-health-list.js +119 -0
- package/dist/jobs/job-health-list.js.map +1 -0
- package/dist/jobs/prune.d.ts +45 -0
- package/dist/jobs/prune.d.ts.map +1 -0
- package/dist/jobs/prune.js +58 -0
- package/dist/jobs/prune.js.map +1 -0
- package/dist/jobs/react-index.d.ts +19 -0
- package/dist/jobs/react-index.d.ts.map +1 -0
- package/dist/jobs/react-index.js +18 -0
- package/dist/jobs/react-index.js.map +1 -0
- package/dist/jobs/registry.d.ts +119 -0
- package/dist/jobs/registry.d.ts.map +1 -0
- package/dist/jobs/registry.js +87 -0
- package/dist/jobs/registry.js.map +1 -0
- package/dist/jobs/scheduler.d.ts +100 -0
- package/dist/jobs/scheduler.d.ts.map +1 -0
- package/dist/jobs/scheduler.js +297 -0
- package/dist/jobs/scheduler.js.map +1 -0
- package/dist/jobs/store.d.ts +119 -0
- package/dist/jobs/store.d.ts.map +1 -0
- package/dist/jobs/store.js +87 -0
- package/dist/jobs/store.js.map +1 -0
- package/dist/jobs/windows.d.ts +66 -0
- package/dist/jobs/windows.d.ts.map +1 -0
- package/dist/jobs/windows.js +130 -0
- package/dist/jobs/windows.js.map +1 -0
- package/package.json +9 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@connextar/house/jobs` — scheduled jobs: declared once, run once per
|
|
3
|
+
* window, switched off and on by name, and honest about their health.
|
|
4
|
+
*
|
|
5
|
+
* AltEd and Quickie each wrote this, from the same seed, and each went on to
|
|
6
|
+
* prove something the other had not: AltEd that a job must be switchable with a
|
|
7
|
+
* name and a date against it, and that some jobs run on their own clock;
|
|
8
|
+
* Quickie that Run now must be fenced to the caller's own tenant. Both printed
|
|
9
|
+
* a job's result as its variable names. This is the union, with that made
|
|
10
|
+
* impossible: a count reaches the screen only through its declaration.
|
|
11
|
+
*
|
|
12
|
+
* Server code. It owns no schema — the app implements `JobStore` over its own
|
|
13
|
+
* table (the README has the Prisma model and adapter) — and it imports no
|
|
14
|
+
* framework: the only thing it reaches for is `node:crypto`, in the tick
|
|
15
|
+
* route's secret check. The health list is `@connextar/house/jobs/react`,
|
|
16
|
+
* which re-exports the words and types a client component needs, so nothing
|
|
17
|
+
* in the browser ever imports this entry.
|
|
18
|
+
*/
|
|
19
|
+
export { checkCronRequest, type CronAuthOptions, type CronAuthResult } from "./cron-auth.js";
|
|
20
|
+
export { countLines, countsSummary, declaredCounts, describeSpan, formatCount, formatDuration, formatMoney, type CountDeclaration, type CountDeclarations, type CountFormatOptions, type CountKind, type CountLabel, type CountLine, type CountValues, } from "./counts.js";
|
|
21
|
+
export { buildJobView, deriveHealth, describeHealth, describeRun, describeSwitch, formatDay, formatWhen, runNowFor, runNowScopesFor, type HealthInput, type HealthWords, type JobAudience, type JobFacts, type JobHealth, type JobHealthState, type JobRunView, type JobView, type RunFacts, type WhenOptions, } from "./health.js";
|
|
22
|
+
export { DEFAULT_COOLDOWN_MS, DEFAULT_TIMEOUT_MS, MAX_JOB_ID, MAX_WINDOW_KEY, defineJob, defineJobs, jobDefinitionProblems, runNowScopes, type AnyJob, type JobActor, type JobContext, type JobDefinition, type RunNowRule, type RunNowScope, } from "./registry.js";
|
|
23
|
+
export { DEFAULT_JOB_RUN_PRUNE_BATCH, DEFAULT_JOB_RUN_RETENTION_DAYS, JOB_RUN_PRUNE_COUNTS, pruneJobRuns, type JobRunPruneCounts, type PruneJobRunsOptions, } from "./prune.js";
|
|
24
|
+
export { createScheduler, refusalStatus, summariseTick, type RunNowCaller, type RunNowOutcome, type RunNowRefusal, type Scheduler, type SchedulerOptions, type SwitchCaller, type SwitchOutcome, type SwitchRefusal, type TickResult, type TickStatus, } from "./scheduler.js";
|
|
25
|
+
export { createMemoryJobStore, pausedFromStored, type JobRunClaim, type JobRunOutcome, type JobRunPrune, type JobRunQuery, type JobRunRecord, type JobStore, type JobSwitchChange, type JobSwitchState, } from "./store.js";
|
|
26
|
+
export { MANUAL_KEY_PREFIX, daily, everyHours, hourly, isManualKey, monthly, weekly, type CalendarWindowOptions, type JobWindow, } from "./windows.js";
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/jobs/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,gBAAgB,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC7F,OAAO,EACL,UAAU,EACV,aAAa,EACb,cAAc,EACd,YAAY,EACZ,WAAW,EACX,cAAc,EACd,WAAW,EACX,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,WAAW,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,WAAW,EACX,cAAc,EACd,SAAS,EACT,UAAU,EACV,SAAS,EACT,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,WAAW,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,SAAS,EACT,UAAU,EACV,qBAAqB,EACrB,YAAY,EACZ,KAAK,MAAM,EACX,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,WAAW,GACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,2BAA2B,EAC3B,8BAA8B,EAC9B,oBAAoB,EACpB,YAAY,EACZ,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,GACzB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,eAAe,EACf,aAAa,EACb,aAAa,EACb,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,KAAK,EACL,UAAU,EACV,MAAM,EACN,WAAW,EACX,OAAO,EACP,MAAM,EACN,KAAK,qBAAqB,EAC1B,KAAK,SAAS,GACf,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@connextar/house/jobs` — scheduled jobs: declared once, run once per
|
|
3
|
+
* window, switched off and on by name, and honest about their health.
|
|
4
|
+
*
|
|
5
|
+
* AltEd and Quickie each wrote this, from the same seed, and each went on to
|
|
6
|
+
* prove something the other had not: AltEd that a job must be switchable with a
|
|
7
|
+
* name and a date against it, and that some jobs run on their own clock;
|
|
8
|
+
* Quickie that Run now must be fenced to the caller's own tenant. Both printed
|
|
9
|
+
* a job's result as its variable names. This is the union, with that made
|
|
10
|
+
* impossible: a count reaches the screen only through its declaration.
|
|
11
|
+
*
|
|
12
|
+
* Server code. It owns no schema — the app implements `JobStore` over its own
|
|
13
|
+
* table (the README has the Prisma model and adapter) — and it imports no
|
|
14
|
+
* framework: the only thing it reaches for is `node:crypto`, in the tick
|
|
15
|
+
* route's secret check. The health list is `@connextar/house/jobs/react`,
|
|
16
|
+
* which re-exports the words and types a client component needs, so nothing
|
|
17
|
+
* in the browser ever imports this entry.
|
|
18
|
+
*/
|
|
19
|
+
export { checkCronRequest } from "./cron-auth.js";
|
|
20
|
+
export { countLines, countsSummary, declaredCounts, describeSpan, formatCount, formatDuration, formatMoney, } from "./counts.js";
|
|
21
|
+
export { buildJobView, deriveHealth, describeHealth, describeRun, describeSwitch, formatDay, formatWhen, runNowFor, runNowScopesFor, } from "./health.js";
|
|
22
|
+
export { DEFAULT_COOLDOWN_MS, DEFAULT_TIMEOUT_MS, MAX_JOB_ID, MAX_WINDOW_KEY, defineJob, defineJobs, jobDefinitionProblems, runNowScopes, } from "./registry.js";
|
|
23
|
+
export { DEFAULT_JOB_RUN_PRUNE_BATCH, DEFAULT_JOB_RUN_RETENTION_DAYS, JOB_RUN_PRUNE_COUNTS, pruneJobRuns, } from "./prune.js";
|
|
24
|
+
export { createScheduler, refusalStatus, summariseTick, } from "./scheduler.js";
|
|
25
|
+
export { createMemoryJobStore, pausedFromStored, } from "./store.js";
|
|
26
|
+
export { MANUAL_KEY_PREFIX, daily, everyHours, hourly, isManualKey, monthly, weekly, } from "./windows.js";
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/jobs/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,gBAAgB,EAA6C,MAAM,gBAAgB,CAAC;AAC7F,OAAO,EACL,UAAU,EACV,aAAa,EACb,cAAc,EACd,YAAY,EACZ,WAAW,EACX,cAAc,EACd,WAAW,GAQZ,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,WAAW,EACX,cAAc,EACd,SAAS,EACT,UAAU,EACV,SAAS,EACT,eAAe,GAWhB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,SAAS,EACT,UAAU,EACV,qBAAqB,EACrB,YAAY,GAOb,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,2BAA2B,EAC3B,8BAA8B,EAC9B,oBAAoB,EACpB,YAAY,GAGb,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,eAAe,EACf,aAAa,EACb,aAAa,GAWd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,GASjB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,KAAK,EACL,UAAU,EACV,MAAM,EACN,WAAW,EACX,OAAO,EACP,MAAM,GAGP,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type JobView, type WhenOptions } from "./health.js";
|
|
3
|
+
import type { RunNowScope } from "./registry.js";
|
|
4
|
+
/** Every element the list draws. Each value replaces the default for that part. */
|
|
5
|
+
export interface JobHealthClassNames {
|
|
6
|
+
list: string;
|
|
7
|
+
empty: string;
|
|
8
|
+
row: string;
|
|
9
|
+
body: string;
|
|
10
|
+
heading: string;
|
|
11
|
+
label: string;
|
|
12
|
+
badge: string;
|
|
13
|
+
badgeGood: string;
|
|
14
|
+
badgeNeutral: string;
|
|
15
|
+
badgeWarning: string;
|
|
16
|
+
badgeBad: string;
|
|
17
|
+
meta: string;
|
|
18
|
+
detail: string;
|
|
19
|
+
error: string;
|
|
20
|
+
counts: string;
|
|
21
|
+
switched: string;
|
|
22
|
+
actions: string;
|
|
23
|
+
runNow: string;
|
|
24
|
+
switch: string;
|
|
25
|
+
switchOn: string;
|
|
26
|
+
switchOff: string;
|
|
27
|
+
thumb: string;
|
|
28
|
+
thumbOn: string;
|
|
29
|
+
thumbOff: string;
|
|
30
|
+
}
|
|
31
|
+
/** The house look, on the shared theme tokens. */
|
|
32
|
+
export declare const JOB_HEALTH_CLASS_NAMES: JobHealthClassNames;
|
|
33
|
+
export interface JobHealthListProps extends WhenOptions {
|
|
34
|
+
/** From `scheduler.views(audience)` on the server. */
|
|
35
|
+
jobs: readonly JobView[];
|
|
36
|
+
/**
|
|
37
|
+
* Switch a job. **Throw to refuse**, with the reason as the message — the
|
|
38
|
+
* switch goes back where it was and the reason is reported through the
|
|
39
|
+
* `ActionFeedbackProvider`. Without it no switch is drawn.
|
|
40
|
+
*/
|
|
41
|
+
onSwitch?: (jobId: string, on: boolean) => Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Run a job now, over `scope` — the button pressed. Resolve with the route's
|
|
44
|
+
* `message` to have it said; throw to refuse. Send the scope to the route and
|
|
45
|
+
* on to `scheduler.runNow` as the caller's `scope`: on a job that offers both,
|
|
46
|
+
* it is what tells the two buttons apart.
|
|
47
|
+
*/
|
|
48
|
+
onRunNow?: (jobId: string, scope: RunNowScope) => Promise<{
|
|
49
|
+
message?: string;
|
|
50
|
+
} | void>;
|
|
51
|
+
/**
|
|
52
|
+
* What each Run now sweeps, in the app's words, for a row that offers this
|
|
53
|
+
* viewer both — "Run for every organisation", "Run for yours". A row with one
|
|
54
|
+
* says only "Run now". Default "every account" and "your account".
|
|
55
|
+
*/
|
|
56
|
+
runNowWords?: Partial<Record<RunNowScope, string>>;
|
|
57
|
+
/** After a switch lands or a run finishes — usually `router.refresh()`. */
|
|
58
|
+
onSettled?: () => void;
|
|
59
|
+
/** Anything of the app's own under a row: AltEd's link to the harvest's screen. */
|
|
60
|
+
extra?: (job: JobView) => React.ReactNode;
|
|
61
|
+
emptyText?: React.ReactNode;
|
|
62
|
+
className?: string;
|
|
63
|
+
classNames?: Partial<JobHealthClassNames>;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The health screen's list: each job's schedule, last run, what it did in
|
|
67
|
+
* words, its health, and — for whoever may — its switch and Run now.
|
|
68
|
+
*
|
|
69
|
+
* The switch is an optimistic value: it moves on the click, the badge with it,
|
|
70
|
+
* and a refusal puts both back and says why. AltEd's switch did not move until
|
|
71
|
+
* the server answered, and greyed out every other switch on the screen while it
|
|
72
|
+
* waited.
|
|
73
|
+
*
|
|
74
|
+
* Draws plain elements on the theme tokens, like the wizard; there is still no
|
|
75
|
+
* `Button` in this package. The app wraps it in its own card and heading.
|
|
76
|
+
*/
|
|
77
|
+
export declare function JobHealthList({ jobs, onSwitch, onRunNow, onSettled, runNowWords, extra, emptyText, className, classNames, locale, timeZone, }: JobHealthListProps): React.JSX.Element;
|
|
78
|
+
//# sourceMappingURL=job-health-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-health-list.d.ts","sourceRoot":"","sources":["../../src/jobs/job-health-list.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,EAML,KAAK,OAAO,EACZ,KAAK,WAAW,EACjB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,mFAAmF;AACnF,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,kDAAkD;AAClD,eAAO,MAAM,sBAAsB,EAAE,mBA2BpC,CAAC;AAWF,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD,sDAAsD;IACtD,IAAI,EAAE,SAAS,OAAO,EAAE,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACvF;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,mFAAmF;IACnF,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,KAAK,CAAC,SAAS,CAAC;IAC1C,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAC3C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,WAAW,EACX,KAAK,EACL,SAAgC,EAChC,SAAS,EACT,UAAU,EACV,MAAM,EACN,QAAQ,GACT,EAAE,kBAAkB,qBAsBpB"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { CircleCheck, CirclePause, CircleX, Clock, LoaderCircle, Play, TriangleAlert, } from "lucide-react";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { cx } from "../internal/cx.js";
|
|
6
|
+
import { useKeyedAction } from "../ux/use-action.js";
|
|
7
|
+
import { useOptimisticValue } from "../ux/use-optimistic-value.js";
|
|
8
|
+
import { countsSummary } from "./counts.js";
|
|
9
|
+
import { describeHealth, describeRun, describeSwitch, } from "./health.js";
|
|
10
|
+
/** The house look, on the shared theme tokens. */
|
|
11
|
+
export const JOB_HEALTH_CLASS_NAMES = {
|
|
12
|
+
list: "flex flex-col divide-y divide-border",
|
|
13
|
+
empty: "text-sm text-muted-foreground",
|
|
14
|
+
row: "flex flex-wrap items-start justify-between gap-3 py-3 first:pt-0 last:pb-0",
|
|
15
|
+
body: "flex min-w-0 flex-1 flex-col gap-1",
|
|
16
|
+
heading: "flex flex-wrap items-center gap-2",
|
|
17
|
+
label: "text-sm font-medium text-foreground",
|
|
18
|
+
badge: "inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs font-medium [&_svg]:size-3",
|
|
19
|
+
badgeGood: "border-primary/30 bg-primary/10 text-primary",
|
|
20
|
+
badgeNeutral: "border-border text-muted-foreground",
|
|
21
|
+
badgeWarning: "border-destructive/40 text-destructive",
|
|
22
|
+
badgeBad: "border-destructive/40 bg-destructive/10 text-destructive",
|
|
23
|
+
meta: "text-xs text-muted-foreground",
|
|
24
|
+
detail: "text-xs text-muted-foreground",
|
|
25
|
+
error: "text-xs text-destructive",
|
|
26
|
+
counts: "text-xs text-muted-foreground tabular-nums",
|
|
27
|
+
switched: "text-xs text-muted-foreground",
|
|
28
|
+
actions: "flex shrink-0 items-center gap-3",
|
|
29
|
+
runNow: "inline-flex items-center gap-1.5 rounded-md border border-border px-2.5 py-1 text-xs font-medium text-foreground transition-colors hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none disabled:pointer-events-none disabled:opacity-60 [&_svg]:size-3.5",
|
|
30
|
+
switch: "relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none",
|
|
31
|
+
switchOn: "bg-primary",
|
|
32
|
+
switchOff: "bg-muted-foreground/30",
|
|
33
|
+
thumb: "pointer-events-none block size-4 rounded-full bg-card shadow transition-transform",
|
|
34
|
+
thumbOn: "translate-x-4",
|
|
35
|
+
thumbOff: "translate-x-0",
|
|
36
|
+
};
|
|
37
|
+
const ICONS = {
|
|
38
|
+
paused: CirclePause,
|
|
39
|
+
"never-run": Clock,
|
|
40
|
+
running: LoaderCircle,
|
|
41
|
+
failed: CircleX,
|
|
42
|
+
stale: TriangleAlert,
|
|
43
|
+
healthy: CircleCheck,
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* The health screen's list: each job's schedule, last run, what it did in
|
|
47
|
+
* words, its health, and — for whoever may — its switch and Run now.
|
|
48
|
+
*
|
|
49
|
+
* The switch is an optimistic value: it moves on the click, the badge with it,
|
|
50
|
+
* and a refusal puts both back and says why. AltEd's switch did not move until
|
|
51
|
+
* the server answered, and greyed out every other switch on the screen while it
|
|
52
|
+
* waited.
|
|
53
|
+
*
|
|
54
|
+
* Draws plain elements on the theme tokens, like the wizard; there is still no
|
|
55
|
+
* `Button` in this package. The app wraps it in its own card and heading.
|
|
56
|
+
*/
|
|
57
|
+
export function JobHealthList({ jobs, onSwitch, onRunNow, onSettled, runNowWords, extra, emptyText = "No scheduled jobs.", className, classNames, locale, timeZone, }) {
|
|
58
|
+
const c = { ...JOB_HEALTH_CLASS_NAMES, ...classNames };
|
|
59
|
+
const actions = useKeyedAction();
|
|
60
|
+
if (jobs.length === 0)
|
|
61
|
+
return _jsx("p", { className: c.empty, children: emptyText });
|
|
62
|
+
return (_jsx("ul", { className: cx(c.list, className), children: jobs.map((job) => (_jsx(JobRow, { job: job, c: c, actions: actions, onSwitch: onSwitch, onRunNow: onRunNow, onSettled: onSettled, scopeWords: { ...RUN_NOW_WORDS, ...runNowWords }, extra: extra, when: { locale, timeZone } }, job.id))) }));
|
|
63
|
+
}
|
|
64
|
+
const RUN_NOW_WORDS = { tenant: "your account", platform: "every account" };
|
|
65
|
+
function toneClass(c, words) {
|
|
66
|
+
switch (words.tone) {
|
|
67
|
+
case "good":
|
|
68
|
+
return c.badgeGood;
|
|
69
|
+
case "warning":
|
|
70
|
+
return c.badgeWarning;
|
|
71
|
+
case "bad":
|
|
72
|
+
return c.badgeBad;
|
|
73
|
+
default:
|
|
74
|
+
return c.badgeNeutral;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function JobRow({ job, c, actions, onSwitch, onRunNow, onSettled, scopeWords, extra, when, }) {
|
|
78
|
+
const on = useOptimisticValue(!job.paused, { onSettled });
|
|
79
|
+
// Health with the switch as the screen shows it, not as the server last said.
|
|
80
|
+
const health = on.value ? job.healthIfOn : { state: "paused" };
|
|
81
|
+
const words = describeHealth(health, when);
|
|
82
|
+
const Icon = ICONS[health.state];
|
|
83
|
+
const run = job.lastRun;
|
|
84
|
+
const success = job.lastSuccess;
|
|
85
|
+
const countsLine = run?.ok && run.counts.length
|
|
86
|
+
? countsSummary(run.counts)
|
|
87
|
+
: success && !run?.ok
|
|
88
|
+
? [describeRun(success, "Last success", when), countsSummary(success.counts)].filter(Boolean).join(": ")
|
|
89
|
+
: null;
|
|
90
|
+
const detail = health.state === "running" ? null : words.detail;
|
|
91
|
+
const switchLine = on.pending ? (on.value ? "Turning back on…" : "Pausing…") : describeSwitch(job, when);
|
|
92
|
+
const toggle = () => {
|
|
93
|
+
if (!onSwitch)
|
|
94
|
+
return;
|
|
95
|
+
const next = !on.value;
|
|
96
|
+
void on.set(next, async () => {
|
|
97
|
+
await onSwitch(job.id, next);
|
|
98
|
+
}, { error: next ? `Couldn't turn ${job.label} back on` : `Couldn't pause ${job.label}` });
|
|
99
|
+
};
|
|
100
|
+
const runNow = (scope) => {
|
|
101
|
+
if (!onRunNow)
|
|
102
|
+
return;
|
|
103
|
+
// Keyed on the job, not the scope: while one of its runs is under way, neither button starts another.
|
|
104
|
+
void actions.run(job.id, () => onRunNow(job.id, scope), {
|
|
105
|
+
success: (result) => (result && result.message) || `Ran ${job.label}.`,
|
|
106
|
+
error: `Couldn't run ${job.label}`,
|
|
107
|
+
// A failed run is on the ledger too, so the screen has something new to show either way.
|
|
108
|
+
onSuccess: () => onSettled?.(),
|
|
109
|
+
onError: () => onSettled?.(),
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
const running = actions.isPending(job.id);
|
|
113
|
+
const showSwitch = job.canSwitch && Boolean(onSwitch);
|
|
114
|
+
// `runNow` null is the last word: a view an app narrowed by hand offers nothing.
|
|
115
|
+
const scopes = !onRunNow || job.runNow === null ? [] : job.runNowScopes.length ? job.runNowScopes : [job.runNow];
|
|
116
|
+
const showRunNow = scopes.length > 0;
|
|
117
|
+
return (_jsxs("li", { className: c.row, "data-job": job.id, "data-health": health.state, children: [_jsxs("div", { className: c.body, children: [_jsxs("div", { className: c.heading, children: [_jsx("span", { className: c.label, children: job.label }), _jsxs("span", { className: cx(c.badge, toneClass(c, words)), children: [_jsx(Icon, { "aria-hidden": "true" }), words.label] })] }), _jsxs("p", { className: c.meta, children: [job.cadence, run ? ` · ${describeRun(run, "Last run", when)}` : ""] }), detail ? _jsx("p", { className: health.state === "failed" ? c.error : c.detail, children: detail }) : null, countsLine ? _jsx("p", { className: c.counts, children: countsLine }) : null, switchLine ? _jsx("p", { className: c.switched, children: switchLine }) : null, extra?.(job)] }), showSwitch || showRunNow ? (_jsxs("div", { className: c.actions, children: [scopes.map((scope) => (_jsxs("button", { type: "button", className: c.runNow, onClick: () => runNow(scope), disabled: running || !on.value, title: on.value ? undefined : "Switched off — turn it back on to run it", "data-busy": running ? "true" : undefined, "data-scope": scope, "aria-label": scopes.length > 1 ? `Run ${job.label} now for ${scopeWords[scope]}` : `Run ${job.label} now`, children: [_jsx(Play, { "aria-hidden": "true" }), scopes.length > 1 ? `Run for ${scopeWords[scope]}` : "Run now"] }, scope))), showSwitch ? (_jsx("button", { type: "button", role: "switch", "aria-checked": on.value, "aria-label": job.label, "aria-busy": on.pending || undefined, className: cx(c.switch, on.value ? c.switchOn : c.switchOff), onClick: toggle, children: _jsx("span", { className: cx(c.thumb, on.value ? c.thumbOn : c.thumbOff) }) })) : null] })) : null] }));
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=job-health-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-health-list.js","sourceRoot":"","sources":["../../src/jobs/job-health-list.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { CountValues } from "./counts.js";
|
|
2
|
+
import type { JobStore } from "./store.js";
|
|
3
|
+
/** How long a run is kept, unless it is one health reads. */
|
|
4
|
+
export declare const DEFAULT_JOB_RUN_RETENTION_DAYS = 90;
|
|
5
|
+
/** At most this many rows a call, so one housekeeping run never holds the table for long. */
|
|
6
|
+
export declare const DEFAULT_JOB_RUN_PRUNE_BATCH = 1000;
|
|
7
|
+
/**
|
|
8
|
+
* What `pruneJobRuns` reports, declared so it reads "12 old job runs removed".
|
|
9
|
+
* Spread it into the housekeeping job's `counts`, and the result into what its
|
|
10
|
+
* `run` returns.
|
|
11
|
+
*/
|
|
12
|
+
export declare const JOB_RUN_PRUNE_COUNTS: {
|
|
13
|
+
readonly jobRunsPruned: {
|
|
14
|
+
readonly label: {
|
|
15
|
+
readonly one: "old job run removed";
|
|
16
|
+
readonly other: "old job runs removed";
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export type JobRunPruneCounts = CountValues<typeof JOB_RUN_PRUNE_COUNTS>;
|
|
21
|
+
export interface PruneJobRunsOptions {
|
|
22
|
+
/** The clock. The housekeeping job passes its `now`. */
|
|
23
|
+
now?: Date;
|
|
24
|
+
/**
|
|
25
|
+
* Keep every run for this many days. Default 90. At least 1 — a day, not a
|
|
26
|
+
* millisecond count, so `90` cannot be mistaken for 90 ms and empty the ledger.
|
|
27
|
+
*/
|
|
28
|
+
retentionDays?: number;
|
|
29
|
+
/** Delete at most this many rows in the call. Default 1000. Whatever is left goes next time. */
|
|
30
|
+
batchLimit?: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Delete runs older than the retention period — never the latest run or the
|
|
34
|
+
* latest successful run of a job (per tenant, for tenants' own runs, and once
|
|
35
|
+
* for the runs over every tenant), and never one that has not finished — at
|
|
36
|
+
* most `batchLimit` of them, oldest first. Running it again straight away
|
|
37
|
+
* deletes only what the first call's limit left.
|
|
38
|
+
*
|
|
39
|
+
* ```ts
|
|
40
|
+
* counts: { windowsPruned: { label: "…" }, ...JOB_RUN_PRUNE_COUNTS },
|
|
41
|
+
* run: async ({ now }) => ({ windowsPruned: await pruneRateLimits(), ...(await pruneJobRuns(jobStore, { now })) }),
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function pruneJobRuns(store: JobStore, options?: PruneJobRunsOptions): Promise<JobRunPruneCounts>;
|
|
45
|
+
//# sourceMappingURL=prune.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prune.d.ts","sourceRoot":"","sources":["../../src/jobs/prune.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,WAAW,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAkB3C,6DAA6D;AAC7D,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAEjD,6FAA6F;AAC7F,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;;;;;;CAEK,CAAC;AAEvC,MAAM,MAAM,iBAAiB,GAAG,WAAW,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEzE,MAAM,WAAW,mBAAmB;IAClC,wDAAwD;IACxD,GAAG,CAAC,EAAE,IAAI,CAAC;IACX;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gGAAgG;IAChG,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAkBjH"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pruning the ledger — from the app's own housekeeping job, a bounded batch a
|
|
3
|
+
* day.
|
|
4
|
+
*
|
|
5
|
+
* The ledger grows without end (AltEd writes about 150 rows a day), and a Run
|
|
6
|
+
* now row keeps the name of whoever pressed it. Old rows are history, not
|
|
7
|
+
* state — with two exceptions, which are the whole of the care here. Health is
|
|
8
|
+
* read from the latest run and the latest successful run of each job (and, for
|
|
9
|
+
* a tenant's own Run now, of each job for that tenant). Delete those and a
|
|
10
|
+
* monthly job that last ran 120 days ago reads **Never run**; delete an
|
|
11
|
+
* unfinished one and a run under way loses its row. So those are never
|
|
12
|
+
* deleted, however old, and everything else past the retention period is.
|
|
13
|
+
*/
|
|
14
|
+
const DAY = 24 * 60 * 60 * 1000;
|
|
15
|
+
/** How long a run is kept, unless it is one health reads. */
|
|
16
|
+
export const DEFAULT_JOB_RUN_RETENTION_DAYS = 90;
|
|
17
|
+
/** At most this many rows a call, so one housekeeping run never holds the table for long. */
|
|
18
|
+
export const DEFAULT_JOB_RUN_PRUNE_BATCH = 1000;
|
|
19
|
+
/**
|
|
20
|
+
* What `pruneJobRuns` reports, declared so it reads "12 old job runs removed".
|
|
21
|
+
* Spread it into the housekeeping job's `counts`, and the result into what its
|
|
22
|
+
* `run` returns.
|
|
23
|
+
*/
|
|
24
|
+
export const JOB_RUN_PRUNE_COUNTS = {
|
|
25
|
+
jobRunsPruned: { label: { one: "old job run removed", other: "old job runs removed" } },
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Delete runs older than the retention period — never the latest run or the
|
|
29
|
+
* latest successful run of a job (per tenant, for tenants' own runs, and once
|
|
30
|
+
* for the runs over every tenant), and never one that has not finished — at
|
|
31
|
+
* most `batchLimit` of them, oldest first. Running it again straight away
|
|
32
|
+
* deletes only what the first call's limit left.
|
|
33
|
+
*
|
|
34
|
+
* ```ts
|
|
35
|
+
* counts: { windowsPruned: { label: "…" }, ...JOB_RUN_PRUNE_COUNTS },
|
|
36
|
+
* run: async ({ now }) => ({ windowsPruned: await pruneRateLimits(), ...(await pruneJobRuns(jobStore, { now })) }),
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export async function pruneJobRuns(store, options = {}) {
|
|
40
|
+
const retentionDays = options.retentionDays ?? DEFAULT_JOB_RUN_RETENTION_DAYS;
|
|
41
|
+
const limit = options.batchLimit ?? DEFAULT_JOB_RUN_PRUNE_BATCH;
|
|
42
|
+
if (!(Number.isFinite(retentionDays) && retentionDays >= 1)) {
|
|
43
|
+
throw new RangeError(`The job run retention is a number of days, at least 1 (got ${retentionDays}).`);
|
|
44
|
+
}
|
|
45
|
+
if (!(Number.isInteger(limit) && limit >= 1)) {
|
|
46
|
+
throw new RangeError(`The job run prune batch is a whole number of rows, at least 1 (got ${limit}).`);
|
|
47
|
+
}
|
|
48
|
+
if (typeof store.pruneRuns !== "function") {
|
|
49
|
+
throw new Error("This JobStore can't prune old runs: add pruneRuns to the adapter (the README has it).");
|
|
50
|
+
}
|
|
51
|
+
const now = options.now ?? new Date();
|
|
52
|
+
const deleted = await store.pruneRuns({ startedBefore: new Date(now.getTime() - retentionDays * DAY), limit });
|
|
53
|
+
if (!(Number.isInteger(deleted) && deleted >= 0)) {
|
|
54
|
+
throw new Error(`The JobStore's pruneRuns didn't say how many runs it deleted (got ${String(deleted)}).`);
|
|
55
|
+
}
|
|
56
|
+
return { jobRunsPruned: deleted };
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=prune.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prune.js","sourceRoot":"","sources":["../../src/jobs/prune.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;GAYG;AAEH,MAAM,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEhC,6DAA6D;AAC7D,MAAM,CAAC,MAAM,8BAA8B,GAAG,EAAE,CAAC;AAEjD,6FAA6F;AAC7F,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC;AAEhD;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,aAAa,EAAE,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,qBAAqB,EAAE,KAAK,EAAE,sBAAsB,EAAE,EAAE;CACnD,CAAC;AAgBvC;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAe,EAAE,UAA+B,EAAE;IACnF,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,8BAA8B,CAAC;IAC9E,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,IAAI,2BAA2B,CAAC;IAChE,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,aAAa,IAAI,CAAC,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,UAAU,CAAC,8DAA8D,aAAa,IAAI,CAAC,CAAC;IACxG,CAAC;IACD,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,UAAU,CAAC,sEAAsE,KAAK,IAAI,CAAC,CAAC;IACxG,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,uFAAuF,CAAC,CAAC;IAC3G,CAAC;IACD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IACtC,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,EAAE,aAAa,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,aAAa,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/G,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,qEAAqE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5G,CAAC;IACD,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@connextar/house/jobs/react` — the health screen's list, and the words it
|
|
3
|
+
* uses.
|
|
4
|
+
*
|
|
5
|
+
* Its own entry point because `@connextar/house/jobs` is server code: it
|
|
6
|
+
* checks the scheduler's secret with `node:crypto`, which no client bundle can
|
|
7
|
+
* load. Everything a client component needs — the list, the health and count
|
|
8
|
+
* wording, the view types — is here, and `entry-points.test.ts` follows the
|
|
9
|
+
* emitted imports to keep `node:` out of it.
|
|
10
|
+
*
|
|
11
|
+
* The switch and Run now go through `@connextar/house/ux` (`useOptimisticValue`,
|
|
12
|
+
* `useKeyedAction`), so a refusal is reported to the app's
|
|
13
|
+
* `ActionFeedbackProvider` and this module imports no toast library.
|
|
14
|
+
*/
|
|
15
|
+
export { countsSummary, describeSpan, formatDuration, formatMoney, type CountKind, type CountLine } from "./counts.js";
|
|
16
|
+
export { describeHealth, describeRun, describeSwitch, formatDay, formatWhen, type HealthWords, type JobHealth, type JobHealthState, type JobRunView, type JobView, type WhenOptions, } from "./health.js";
|
|
17
|
+
export { JOB_HEALTH_CLASS_NAMES, JobHealthList, type JobHealthClassNames, type JobHealthListProps, } from "./job-health-list.js";
|
|
18
|
+
export type { RunNowScope } from "./registry.js";
|
|
19
|
+
//# sourceMappingURL=react-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-index.d.ts","sourceRoot":"","sources":["../../src/jobs/react-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AACvH,OAAO,EACL,cAAc,EACd,WAAW,EACX,cAAc,EACd,SAAS,EACT,UAAU,EACV,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,OAAO,EACZ,KAAK,WAAW,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@connextar/house/jobs/react` — the health screen's list, and the words it
|
|
3
|
+
* uses.
|
|
4
|
+
*
|
|
5
|
+
* Its own entry point because `@connextar/house/jobs` is server code: it
|
|
6
|
+
* checks the scheduler's secret with `node:crypto`, which no client bundle can
|
|
7
|
+
* load. Everything a client component needs — the list, the health and count
|
|
8
|
+
* wording, the view types — is here, and `entry-points.test.ts` follows the
|
|
9
|
+
* emitted imports to keep `node:` out of it.
|
|
10
|
+
*
|
|
11
|
+
* The switch and Run now go through `@connextar/house/ux` (`useOptimisticValue`,
|
|
12
|
+
* `useKeyedAction`), so a refusal is reported to the app's
|
|
13
|
+
* `ActionFeedbackProvider` and this module imports no toast library.
|
|
14
|
+
*/
|
|
15
|
+
export { countsSummary, describeSpan, formatDuration, formatMoney } from "./counts.js";
|
|
16
|
+
export { describeHealth, describeRun, describeSwitch, formatDay, formatWhen, } from "./health.js";
|
|
17
|
+
export { JOB_HEALTH_CLASS_NAMES, JobHealthList, } from "./job-health-list.js";
|
|
18
|
+
//# sourceMappingURL=react-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-index.js","sourceRoot":"","sources":["../../src/jobs/react-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAkC,MAAM,aAAa,CAAC;AACvH,OAAO,EACL,cAAc,EACd,WAAW,EACX,cAAc,EACd,SAAS,EACT,UAAU,GAOX,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,aAAa,GAGd,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { CountDeclarations, CountValues } from "./counts.js";
|
|
2
|
+
import type { JobWindow } from "./windows.js";
|
|
3
|
+
/** Who did something: pressed Run now, switched a job off. Named, because the card says who. */
|
|
4
|
+
export interface JobActor {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Who a manual run may sweep.
|
|
10
|
+
*
|
|
11
|
+
* - `tenant` — only the caller's own tenant. Quickie's owners: every signup owns
|
|
12
|
+
* an organisation, so an unscoped Run now would let anyone fire every tenant's
|
|
13
|
+
* automations, emailing other businesses' customers.
|
|
14
|
+
* - `platform` — everything, which only someone with platform authority may ask
|
|
15
|
+
* for. AltEd's administrators: AltEd is single-tenant, so every job is a sweep
|
|
16
|
+
* over shared tables and there is no smaller slice to offer.
|
|
17
|
+
*/
|
|
18
|
+
export type RunNowScope = "tenant" | "platform";
|
|
19
|
+
export interface RunNowRule {
|
|
20
|
+
/**
|
|
21
|
+
* Over whom Run now may sweep: one scope, or both.
|
|
22
|
+
*
|
|
23
|
+
* `["tenant", "platform"]` — Quickie's per-organisation jobs: an owner re-runs
|
|
24
|
+
* their own organisation, and an operator re-runs the sweep over every
|
|
25
|
+
* organisation when the overnight one failed. The two are fenced from each
|
|
26
|
+
* other: each has its own cool-down (a tenant's per tenant), neither starts
|
|
27
|
+
* while the other is in flight, and only the platform run counts for the
|
|
28
|
+
* job's health. Offering both needs `JobStore.latestUnfinishedRun`.
|
|
29
|
+
*/
|
|
30
|
+
scope: RunNowScope | readonly RunNowScope[];
|
|
31
|
+
/**
|
|
32
|
+
* How soon the same scope may run it again — per tenant for a tenant run.
|
|
33
|
+
* Default ten minutes. A manual run skips the job's hour gates, so without
|
|
34
|
+
* this a second press sends the whole batch again. `0` turns it off.
|
|
35
|
+
*/
|
|
36
|
+
cooldownMs?: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The scopes a rule offers, narrowest first, each once: `["tenant"]`,
|
|
40
|
+
* `["platform"]` or `["tenant", "platform"]`. Nothing when Run now is off.
|
|
41
|
+
*/
|
|
42
|
+
export declare function runNowScopes(rule: false | RunNowRule | undefined): RunNowScope[];
|
|
43
|
+
/** What a run is handed. */
|
|
44
|
+
export interface JobContext {
|
|
45
|
+
now: Date;
|
|
46
|
+
trigger: "schedule" | "manual";
|
|
47
|
+
/**
|
|
48
|
+
* `null`: sweep every tenant (the schedule, or a platform Run now). A string:
|
|
49
|
+
* sweep **only** this tenant — a tenant-scoped Run now always has one, and the
|
|
50
|
+
* job must pass it down to every query it makes.
|
|
51
|
+
*/
|
|
52
|
+
tenantId: string | null;
|
|
53
|
+
/** A manual run: skip the job's own hour gates. It is not a licence to skip a per-recipient throttle. */
|
|
54
|
+
force: boolean;
|
|
55
|
+
windowKey: string;
|
|
56
|
+
/** Who pressed Run now; `null` for the schedule. */
|
|
57
|
+
by: JobActor | null;
|
|
58
|
+
}
|
|
59
|
+
export interface JobDefinition<C extends CountDeclarations = CountDeclarations> {
|
|
60
|
+
/** Stable, and stored: the ledger's `job` column and the switch's key. Lower-case, `-` between words, ≤ 60. */
|
|
61
|
+
id: string;
|
|
62
|
+
/** What the card calls it: "Payment reminders". */
|
|
63
|
+
label: string;
|
|
64
|
+
/** When it runs, in words: "Daily, from 09:00". The window is the rule; this is how the card says it. */
|
|
65
|
+
cadence: string;
|
|
66
|
+
/**
|
|
67
|
+
* `tick` (the default): the dispatcher runs it from the app's one tick route.
|
|
68
|
+
* `own`: it has its own route and clock — AltEd's harvest every ten minutes,
|
|
69
|
+
* its import runner every minute — which calls `runOwn`. The tick never runs it.
|
|
70
|
+
*/
|
|
71
|
+
schedule?: "tick" | "own";
|
|
72
|
+
/** Which window a moment is in, or `null` when not due. Required for `tick`; optional for `own`. */
|
|
73
|
+
window?: JobWindow;
|
|
74
|
+
/**
|
|
75
|
+
* How long without a successful whole run before the card says **Stale**.
|
|
76
|
+
* `null` for a job that runs only when there is work — an import runner idle
|
|
77
|
+
* all week is not stale.
|
|
78
|
+
*/
|
|
79
|
+
staleAfterMs: number | null;
|
|
80
|
+
/** How long a run may go unfinished before the card calls it failed. Default 15 minutes. */
|
|
81
|
+
timeoutMs?: number;
|
|
82
|
+
/** Whether an administrator may switch it off. Default `true`. */
|
|
83
|
+
switchable?: boolean;
|
|
84
|
+
/** Whether Run now is offered, and over whom. Default: never. */
|
|
85
|
+
runNow?: false | RunNowRule;
|
|
86
|
+
/** Every number `run` reports, each with its words and kind. See `CountDeclaration`. */
|
|
87
|
+
counts: C;
|
|
88
|
+
run: (context: JobContext) => Promise<CountValues<C>>;
|
|
89
|
+
}
|
|
90
|
+
/** A job with its count types erased — how the registry holds a list of different ones. */
|
|
91
|
+
export type AnyJob = JobDefinition<CountDeclarations>;
|
|
92
|
+
/** `R`, with every key `C` does not declare forbidden. */
|
|
93
|
+
type OnlyDeclared<R, C> = R & {
|
|
94
|
+
[K in Exclude<keyof R, keyof C>]: never;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* A job's definition, checked against its own counts at compile time: `run`
|
|
98
|
+
* must report every count `counts` declares, and nothing it does not — a
|
|
99
|
+
* number with no declaration has no words to be shown in. (At run time an
|
|
100
|
+
* undeclared key is dropped before it is stored, so it cannot reach a screen
|
|
101
|
+
* either way.)
|
|
102
|
+
*/
|
|
103
|
+
export declare function defineJob<const C extends CountDeclarations, R extends CountValues<C>>(job: Omit<JobDefinition<C>, "run"> & {
|
|
104
|
+
run: (context: JobContext) => Promise<OnlyDeclared<R, C>>;
|
|
105
|
+
}): AnyJob;
|
|
106
|
+
export declare const DEFAULT_TIMEOUT_MS: number;
|
|
107
|
+
export declare const DEFAULT_COOLDOWN_MS: number;
|
|
108
|
+
export declare const MAX_JOB_ID = 60;
|
|
109
|
+
export declare const MAX_WINDOW_KEY = 120;
|
|
110
|
+
/**
|
|
111
|
+
* Every way a list of jobs is wrong, as sentences — the same shape as
|
|
112
|
+
* `releaseHistoryProblems`, so a test can assert it is empty and read the
|
|
113
|
+
* failure when it is not.
|
|
114
|
+
*/
|
|
115
|
+
export declare function jobDefinitionProblems(jobs: readonly AnyJob[]): string[];
|
|
116
|
+
/** The app's job list, checked once, when the module that declares it loads. Throws every problem at once. */
|
|
117
|
+
export declare function defineJobs(jobs: readonly AnyJob[]): readonly AnyJob[];
|
|
118
|
+
export {};
|
|
119
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/jobs/registry.ts"],"names":[],"mappings":"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"}
|