pixivflow 2.10.31 → 2.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/config/fly-two-bots.example.json +4 -0
- package/dist/commands/SchedulerCommand.d.ts.map +1 -1
- package/dist/commands/SchedulerCommand.js +96 -0
- package/dist/commands/SchedulerCommand.js.map +1 -1
- package/dist/commands/SchedulerRunOnceCommand.d.ts.map +1 -1
- package/dist/commands/SchedulerRunOnceCommand.js +5 -1
- package/dist/commands/SchedulerRunOnceCommand.js.map +1 -1
- package/dist/commands/scheduler-runtime.d.ts +4 -1
- package/dist/commands/scheduler-runtime.d.ts.map +1 -1
- package/dist/commands/scheduler-runtime.js +104 -5
- package/dist/commands/scheduler-runtime.js.map +1 -1
- package/dist/config/defaults.d.ts +7 -0
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +7 -0
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/types.d.ts +72 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/validation.d.ts.map +1 -1
- package/dist/config/validation.js +16 -1
- package/dist/config/validation.js.map +1 -1
- package/dist/delivery/DeliveryOutbox.d.ts +7 -0
- package/dist/delivery/DeliveryOutbox.d.ts.map +1 -1
- package/dist/delivery/DeliveryOutbox.js +19 -0
- package/dist/delivery/DeliveryOutbox.js.map +1 -1
- package/dist/delivery/HttpMultipartDelivery.d.ts.map +1 -1
- package/dist/delivery/HttpMultipartDelivery.js +10 -1
- package/dist/delivery/HttpMultipartDelivery.js.map +1 -1
- package/dist/delivery/types.d.ts +16 -0
- package/dist/delivery/types.d.ts.map +1 -1
- package/dist/download/DownloadManager.d.ts +12 -1
- package/dist/download/DownloadManager.d.ts.map +1 -1
- package/dist/download/DownloadManager.js +15 -0
- package/dist/download/DownloadManager.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/scheduler/MultiScheduleManager.d.ts +17 -1
- package/dist/scheduler/MultiScheduleManager.d.ts.map +1 -1
- package/dist/scheduler/MultiScheduleManager.js +55 -5
- package/dist/scheduler/MultiScheduleManager.js.map +1 -1
- package/dist/scheduler/OccurrenceResolver.d.ts +90 -0
- package/dist/scheduler/OccurrenceResolver.d.ts.map +1 -0
- package/dist/scheduler/OccurrenceResolver.js +127 -0
- package/dist/scheduler/OccurrenceResolver.js.map +1 -0
- package/dist/scheduler/ScheduleTriggerServer.d.ts +60 -0
- package/dist/scheduler/ScheduleTriggerServer.d.ts.map +1 -0
- package/dist/scheduler/ScheduleTriggerServer.js +99 -0
- package/dist/scheduler/ScheduleTriggerServer.js.map +1 -0
- package/dist/scheduler/Scheduler.d.ts +15 -4
- package/dist/scheduler/Scheduler.d.ts.map +1 -1
- package/dist/scheduler/Scheduler.js +36 -15
- package/dist/scheduler/Scheduler.js.map +1 -1
- package/dist/scheduler/SlotCoordinator.d.ts +90 -0
- package/dist/scheduler/SlotCoordinator.d.ts.map +1 -0
- package/dist/scheduler/SlotCoordinator.js +176 -0
- package/dist/scheduler/SlotCoordinator.js.map +1 -0
- package/dist/storage/Database.d.ts +4 -0
- package/dist/storage/Database.d.ts.map +1 -1
- package/dist/storage/Database.js +7 -0
- package/dist/storage/Database.js.map +1 -1
- package/dist/storage/DatabaseMigration.d.ts.map +1 -1
- package/dist/storage/DatabaseMigration.js +74 -2
- package/dist/storage/DatabaseMigration.js.map +1 -1
- package/dist/storage/repositories/SlotRepository.d.ts +102 -0
- package/dist/storage/repositories/SlotRepository.d.ts.map +1 -0
- package/dist/storage/repositories/SlotRepository.js +224 -0
- package/dist/storage/repositories/SlotRepository.js.map +1 -0
- package/dist/webui/package.json +1 -1
- package/package.json +1 -1
|
@@ -4,11 +4,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.MultiScheduleManager = void 0;
|
|
7
|
+
exports.occurrenceToSlotContext = occurrenceToSlotContext;
|
|
7
8
|
const node_fs_1 = require("node:fs");
|
|
8
9
|
const cron_parser_1 = __importDefault(require("cron-parser"));
|
|
9
10
|
const logger_1 = require("../logger");
|
|
10
11
|
const Scheduler_1 = require("./Scheduler");
|
|
11
12
|
const schedules_1 = require("./schedules");
|
|
13
|
+
const OccurrenceResolver_1 = require("./OccurrenceResolver");
|
|
12
14
|
class SerialJobAdmission {
|
|
13
15
|
queueLimit;
|
|
14
16
|
active = false;
|
|
@@ -92,6 +94,13 @@ class MultiScheduleManager {
|
|
|
92
94
|
catchUpMissedRuns(config) {
|
|
93
95
|
if (!this.options.database)
|
|
94
96
|
return;
|
|
97
|
+
// External mode (Fly autosleep): a stopped machine is the normal saving
|
|
98
|
+
// state, not an outage — never self-trigger historical runs on cold start.
|
|
99
|
+
if (this.isExternalMode(config))
|
|
100
|
+
return;
|
|
101
|
+
// Opt-out for internal hosts that do not want startup catch-up.
|
|
102
|
+
if (config.schedulerRuntime?.catchUpMissedRuns === false)
|
|
103
|
+
return;
|
|
95
104
|
const now = new Date();
|
|
96
105
|
for (const plan of (0, schedules_1.resolveSchedules)(config)) {
|
|
97
106
|
if (!plan.enabled)
|
|
@@ -105,13 +114,17 @@ class MultiScheduleManager {
|
|
|
105
114
|
try {
|
|
106
115
|
const interval = cron_parser_1.default.parseExpression(plan.cron, {
|
|
107
116
|
currentDate: lastEnd,
|
|
108
|
-
tz: plan.timezone ??
|
|
117
|
+
tz: plan.timezone ?? config.scheduler?.timezone,
|
|
109
118
|
});
|
|
110
119
|
const nextExpected = interval.next().toDate();
|
|
111
120
|
if (nextExpected.getTime() <= now.getTime()) {
|
|
121
|
+
// Resolve the missed canonical occurrence so catch-up opens the SAME
|
|
122
|
+
// slot a timely cron tick would have (bounded: only the one missed
|
|
123
|
+
// fire, never an unbounded historical replay).
|
|
124
|
+
const missed = (0, OccurrenceResolver_1.resolveOccurrence)({ schedule: plan, at: now, triggerSource: 'catchup' });
|
|
112
125
|
logger_1.logger.warn(`Schedule ${plan.id}: cron fire was missed while the daemon was down ` +
|
|
113
126
|
`(next expected after last run ${lastEnd.toISOString()} at ${nextExpected.toISOString()}); running catch-up now`);
|
|
114
|
-
scheduler.runNow();
|
|
127
|
+
scheduler.runNow({ triggerSource: 'catchup', slot: occurrenceToSlotContext(missed) });
|
|
115
128
|
}
|
|
116
129
|
}
|
|
117
130
|
catch (error) {
|
|
@@ -150,6 +163,7 @@ class MultiScheduleManager {
|
|
|
150
163
|
const plans = (0, schedules_1.resolveSchedules)(config);
|
|
151
164
|
const enabledPlans = plans.filter((plan) => plan.enabled);
|
|
152
165
|
const queueLimit = config.schedulerRuntime?.queueLimit ?? Math.max(enabledPlans.length, 1);
|
|
166
|
+
const registerCron = !this.isExternalMode(config);
|
|
153
167
|
// loadConfig performs full validation. Stop the previous cron table only
|
|
154
168
|
// after every new definition is available, then publish one snapshot.
|
|
155
169
|
for (const scheduler of this.schedulers.values())
|
|
@@ -164,10 +178,11 @@ class MultiScheduleManager {
|
|
|
164
178
|
// wedged run cannot hold the shared admission queue forever.
|
|
165
179
|
const schedulerConfig = plan.timeout !== undefined ? plan : { ...plan, timeout: Scheduler_1.DEFAULT_SCHEDULE_TIMEOUT_MS };
|
|
166
180
|
const scheduler = new Scheduler_1.Scheduler(schedulerConfig, this.options.database, this.options.telemetry, plan.id, this.admission, (failure) => this.options.onFailure?.(config, plan, failure));
|
|
167
|
-
scheduler
|
|
181
|
+
scheduler[registerCron ? 'start' : 'init'](async (options) => {
|
|
168
182
|
// The closure keeps the exact validated snapshot for an in-flight run.
|
|
169
|
-
//
|
|
170
|
-
|
|
183
|
+
// Cron fires pass no options (runJob resolves the occurrence for now);
|
|
184
|
+
// catch-up and HTTP triggers pass a pre-resolved occurrence / source.
|
|
185
|
+
await this.options.execute(config, plan, options ?? { triggerSource: 'cron' });
|
|
171
186
|
});
|
|
172
187
|
this.schedulers.set(plan.id, scheduler);
|
|
173
188
|
}
|
|
@@ -219,6 +234,27 @@ class MultiScheduleManager {
|
|
|
219
234
|
scheduler.stop();
|
|
220
235
|
this.schedulers.clear();
|
|
221
236
|
}
|
|
237
|
+
/** External scheduler mode: runs are HTTP-triggered, never by internal cron. */
|
|
238
|
+
isExternalMode(config = this.activeConfig) {
|
|
239
|
+
return config.schedulerRuntime?.mode === 'external';
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Trigger one schedule immediately (external HTTP trigger / run-once). Returns
|
|
243
|
+
* false if the schedule id is unknown. Honors Scheduler's running/pending and
|
|
244
|
+
* serial-admission guards, so duplicate triggers never double-run. The trigger
|
|
245
|
+
* adapter pre-resolves the canonical occurrence and passes it as `options.slot`
|
|
246
|
+
* with `triggerSource: 'http'`; admission/ledger idempotency are the safety net.
|
|
247
|
+
*/
|
|
248
|
+
triggerSchedule(scheduleId, options) {
|
|
249
|
+
const scheduler = this.schedulers.get(scheduleId);
|
|
250
|
+
if (!scheduler)
|
|
251
|
+
return false;
|
|
252
|
+
return scheduler.runNow(options);
|
|
253
|
+
}
|
|
254
|
+
/** Enabled schedule ids in the active snapshot (for the trigger API / status). */
|
|
255
|
+
scheduleIds() {
|
|
256
|
+
return [...this.schedulers.keys()];
|
|
257
|
+
}
|
|
222
258
|
getStatus() {
|
|
223
259
|
return {
|
|
224
260
|
ok: true,
|
|
@@ -228,4 +264,18 @@ class MultiScheduleManager {
|
|
|
228
264
|
}
|
|
229
265
|
}
|
|
230
266
|
exports.MultiScheduleManager = MultiScheduleManager;
|
|
267
|
+
/** Map a resolved canonical occurrence to the durable SlotContext runJob uses. */
|
|
268
|
+
function occurrenceToSlotContext(o) {
|
|
269
|
+
return {
|
|
270
|
+
slotId: o.slotId,
|
|
271
|
+
scheduleId: o.scheduleId,
|
|
272
|
+
occurrenceAt: o.occurrenceAt.getTime(),
|
|
273
|
+
occurrenceDate: o.occurrenceDate,
|
|
274
|
+
occurrenceLabel: o.occurrenceLabel,
|
|
275
|
+
timezone: o.timezone,
|
|
276
|
+
triggerSource: o.triggerSource,
|
|
277
|
+
slotName: o.occurrenceLabel,
|
|
278
|
+
slotDate: o.occurrenceDate,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
231
281
|
//# sourceMappingURL=MultiScheduleManager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultiScheduleManager.js","sourceRoot":"","sources":["../../src/scheduler/MultiScheduleManager.ts"],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"MultiScheduleManager.js","sourceRoot":"","sources":["../../src/scheduler/MultiScheduleManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical Schedule Occurrence resolution.
|
|
3
|
+
*
|
|
4
|
+
* A Slot is "one durable execution occurrence of a configured Schedule" — it is
|
|
5
|
+
* NOT a morning/evening row. Identity derives from (schedule definition, the
|
|
6
|
+
* schedule's own cron + timezone, and the trigger moment): every trigger source
|
|
7
|
+
* (internal cron tick, authenticated HTTP trigger, bounded internal catch-up)
|
|
8
|
+
* resolves the SAME canonical scheduled time for a given wall-clock instant, so
|
|
9
|
+
* duplicate/concurrent/retry triggers converge on one Slot.
|
|
10
|
+
*
|
|
11
|
+
* Pure + dependency-light (cron-parser is already a production dependency).
|
|
12
|
+
* This module is the ONLY place that computes occurrence identity / due-window.
|
|
13
|
+
* Adapters (cron/http/cli) must never hand-roll slot ids or dates.
|
|
14
|
+
*/
|
|
15
|
+
import { ScheduleConfig } from '../config';
|
|
16
|
+
import type { SlotContext } from './SlotCoordinator';
|
|
17
|
+
export type TriggerSource = 'cron' | 'http' | 'manual' | 'catchup';
|
|
18
|
+
/**
|
|
19
|
+
* Options for running a schedule, accepted by every trigger adapter (internal
|
|
20
|
+
* cron, authenticated HTTP, manual CLI) so they all funnel through the one
|
|
21
|
+
* ScheduleExecutionService. This is the cross-layer contract between the
|
|
22
|
+
* scheduler manager / trigger HTTP server and the runtime's runJob.
|
|
23
|
+
*/
|
|
24
|
+
export interface ScheduleRunOptions {
|
|
25
|
+
/** Run only this target id (explicit operator replacement / "重抓"). */
|
|
26
|
+
onlyTarget?: string;
|
|
27
|
+
/** Why this run was started. Defaults to 'cron' for scheduled runs. */
|
|
28
|
+
triggerSource?: TriggerSource;
|
|
29
|
+
/**
|
|
30
|
+
* Ad-hoc/manual execution (run-once / refetch): runs the download plan but
|
|
31
|
+
* never opens a scheduled Slot, so it can neither mark a scheduled occurrence
|
|
32
|
+
* complete nor be resumed as one. Explicit replacement, not automatic retry.
|
|
33
|
+
*/
|
|
34
|
+
adhoc?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Pre-resolved occurrence for a scheduled run (cron/http/catchup). When
|
|
37
|
+
* omitted and `adhoc` is false, runJob resolves the canonical occurrence for
|
|
38
|
+
* now via the schedule's own cron + timezone.
|
|
39
|
+
*/
|
|
40
|
+
slot?: SlotContext;
|
|
41
|
+
}
|
|
42
|
+
export interface OccurrenceInput {
|
|
43
|
+
schedule: ScheduleConfig;
|
|
44
|
+
/** Wall-clock instant of the trigger. Defaults to now. */
|
|
45
|
+
at?: Date;
|
|
46
|
+
/** Why this execution was started (recorded for observability only). */
|
|
47
|
+
triggerSource: TriggerSource;
|
|
48
|
+
}
|
|
49
|
+
export interface ResolvedOccurrence {
|
|
50
|
+
/**
|
|
51
|
+
* Durable, schedule-scoped identity: `<scheduleId>@<occurrenceAt ISO in tz>`.
|
|
52
|
+
* occurrenceAt is the canonical scheduled fire time (minute precision), so a
|
|
53
|
+
* 10:10 watchdog and a 10:00 cron both map to the 10:00 occurrence.
|
|
54
|
+
*/
|
|
55
|
+
slotId: string;
|
|
56
|
+
scheduleId: string;
|
|
57
|
+
/** Canonical scheduled fire time (UTC Date). */
|
|
58
|
+
occurrenceAt: Date;
|
|
59
|
+
/** ISO-8601 with the schedule's tz offset, e.g. 2026-09-08T10:00:00+08:00. */
|
|
60
|
+
occurrenceAtIso: string;
|
|
61
|
+
/** Schedule date (YYYY-MM-DD) in the schedule timezone — for display only. */
|
|
62
|
+
occurrenceDate: string;
|
|
63
|
+
/** Human label of the scheduled time-of-day in tz (e.g. "10:00"). */
|
|
64
|
+
occurrenceLabel: string;
|
|
65
|
+
timezone: string;
|
|
66
|
+
triggerSource: TriggerSource;
|
|
67
|
+
/** Schedule display name (provenance only; never parsed). */
|
|
68
|
+
scheduleName: string;
|
|
69
|
+
}
|
|
70
|
+
export interface OccurrenceWindow {
|
|
71
|
+
ok: boolean;
|
|
72
|
+
/** 425 = not due yet, 410 = expired (outside grace). */
|
|
73
|
+
status?: 425 | 410;
|
|
74
|
+
error?: string;
|
|
75
|
+
}
|
|
76
|
+
/** Resolve the schedule's configured timezone (never the server's local tz). */
|
|
77
|
+
export declare function scheduleTimezone(schedule: ScheduleConfig | undefined, fallback?: string): string;
|
|
78
|
+
/**
|
|
79
|
+
* Return the most recent scheduled fire time of `cron` at or before `at` in
|
|
80
|
+
* `tz`. This is the canonical occurrence for a trigger arriving at `at`: late
|
|
81
|
+
* triggers (watchdog/retry/wake delay) resolve to the fire they belong to.
|
|
82
|
+
*/
|
|
83
|
+
export declare function resolveOccurrence(input: OccurrenceInput): ResolvedOccurrence;
|
|
84
|
+
/**
|
|
85
|
+
* Validate that a trigger at `at` may run `occurrence`: it is runnable from a
|
|
86
|
+
* small lead before its scheduled time until `graceMinutes` after. Outside that
|
|
87
|
+
* window external clocks must not execute (no historical back-fill).
|
|
88
|
+
*/
|
|
89
|
+
export declare function checkOccurrenceWindow(occurrence: ResolvedOccurrence, at: Date, graceMinutes: number, leadMinutes?: number): OccurrenceWindow;
|
|
90
|
+
//# sourceMappingURL=OccurrenceResolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OccurrenceResolver.d.ts","sourceRoot":"","sources":["../../src/scheduler/OccurrenceResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEnE;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,cAAc,CAAC;IACzB,0DAA0D;IAC1D,EAAE,CAAC,EAAE,IAAI,CAAC;IACV,wEAAwE;IACxE,aAAa,EAAE,aAAa,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,YAAY,EAAE,IAAI,CAAC;IACnB,8EAA8E;IAC9E,eAAe,EAAE,MAAM,CAAC;IACxB,8EAA8E;IAC9E,cAAc,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,aAAa,CAAC;IAC7B,6DAA6D;IAC7D,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,wDAAwD;IACxD,MAAM,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID,gFAAgF;AAChF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,cAAc,GAAG,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhG;AAwBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,GAAG,kBAAkB,CAkD5E;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,kBAAkB,EAAE,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,SAAK,GAAG,gBAAgB,CAoBxI"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Canonical Schedule Occurrence resolution.
|
|
4
|
+
*
|
|
5
|
+
* A Slot is "one durable execution occurrence of a configured Schedule" — it is
|
|
6
|
+
* NOT a morning/evening row. Identity derives from (schedule definition, the
|
|
7
|
+
* schedule's own cron + timezone, and the trigger moment): every trigger source
|
|
8
|
+
* (internal cron tick, authenticated HTTP trigger, bounded internal catch-up)
|
|
9
|
+
* resolves the SAME canonical scheduled time for a given wall-clock instant, so
|
|
10
|
+
* duplicate/concurrent/retry triggers converge on one Slot.
|
|
11
|
+
*
|
|
12
|
+
* Pure + dependency-light (cron-parser is already a production dependency).
|
|
13
|
+
* This module is the ONLY place that computes occurrence identity / due-window.
|
|
14
|
+
* Adapters (cron/http/cli) must never hand-roll slot ids or dates.
|
|
15
|
+
*/
|
|
16
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.scheduleTimezone = scheduleTimezone;
|
|
21
|
+
exports.resolveOccurrence = resolveOccurrence;
|
|
22
|
+
exports.checkOccurrenceWindow = checkOccurrenceWindow;
|
|
23
|
+
const cron_parser_1 = __importDefault(require("cron-parser"));
|
|
24
|
+
const DEFAULT_TZ = 'UTC';
|
|
25
|
+
/** Resolve the schedule's configured timezone (never the server's local tz). */
|
|
26
|
+
function scheduleTimezone(schedule, fallback) {
|
|
27
|
+
return schedule?.timezone?.trim() || fallback?.trim() || DEFAULT_TZ;
|
|
28
|
+
}
|
|
29
|
+
function partsInTz(date, tz) {
|
|
30
|
+
const parts = new Intl.DateTimeFormat('en-CA', {
|
|
31
|
+
timeZone: tz,
|
|
32
|
+
year: 'numeric',
|
|
33
|
+
month: '2-digit',
|
|
34
|
+
day: '2-digit',
|
|
35
|
+
}).formatToParts(date);
|
|
36
|
+
const get = (t) => parts.find((p) => p.type === t)?.value ?? '';
|
|
37
|
+
return { y: get('year'), m: get('month'), d: get('day') };
|
|
38
|
+
}
|
|
39
|
+
function hhmmInTz(date, tz) {
|
|
40
|
+
const parts = new Intl.DateTimeFormat('en-GB', {
|
|
41
|
+
timeZone: tz,
|
|
42
|
+
hour: '2-digit',
|
|
43
|
+
minute: '2-digit',
|
|
44
|
+
hour12: false,
|
|
45
|
+
}).formatToParts(date);
|
|
46
|
+
const get = (t) => parts.find((p) => p.type === t)?.value ?? '00';
|
|
47
|
+
return `${get('hour')}:${get('minute')}`;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Return the most recent scheduled fire time of `cron` at or before `at` in
|
|
51
|
+
* `tz`. This is the canonical occurrence for a trigger arriving at `at`: late
|
|
52
|
+
* triggers (watchdog/retry/wake delay) resolve to the fire they belong to.
|
|
53
|
+
*/
|
|
54
|
+
function resolveOccurrence(input) {
|
|
55
|
+
const { schedule, at = new Date(), triggerSource } = input;
|
|
56
|
+
const tz = scheduleTimezone(schedule);
|
|
57
|
+
const cronExpr = schedule.cron;
|
|
58
|
+
if (!cronExpr) {
|
|
59
|
+
throw new Error(`schedule ${schedule.id} has no cron expression; cannot resolve occurrence`);
|
|
60
|
+
}
|
|
61
|
+
// A trigger belongs to the occurrence whose window contains the trigger
|
|
62
|
+
// instant: the last fire at/before `at`, OR — when `at` is just before a fire
|
|
63
|
+
// (clock fired a hair early / a small lead) — that upcoming fire. Resolving to
|
|
64
|
+
// "yesterday's fire" for an on-time tick is what a naive prev() would do
|
|
65
|
+
// (cron-parser is exclusive of the anchor), so peek both sides and pick the
|
|
66
|
+
// occurrence whose lead/grace window actually contains `at`.
|
|
67
|
+
// cron-parser treats the anchor instant as exclusive: prev() exactly AT a
|
|
68
|
+
// fire time returns the PREVIOUS fire. Nudge the anchor a minute forward (a
|
|
69
|
+
// schedule is minute-granular) so an on-time tick resolves to its own fire,
|
|
70
|
+
// then pick prev vs the upcoming fire by window.
|
|
71
|
+
const anchor = new Date(at.getTime() + 60_000);
|
|
72
|
+
const interval = cron_parser_1.default.parseExpression(cronExpr, { currentDate: anchor, tz, iterator: false });
|
|
73
|
+
const prev = interval.prev().toDate();
|
|
74
|
+
const next = cron_parser_1.default
|
|
75
|
+
.parseExpression(cronExpr, { currentDate: at, tz, iterator: false })
|
|
76
|
+
.next()
|
|
77
|
+
.toDate();
|
|
78
|
+
// Default to the most recent fire; prefer the upcoming one only when `at` is
|
|
79
|
+
// within its lead window (a clock that fired a hair early). Lead is
|
|
80
|
+
// resolver-internal and small (15 min).
|
|
81
|
+
const LEAD_MS = 15 * 60_000;
|
|
82
|
+
const fire = next.getTime() - at.getTime() <= LEAD_MS && next.getTime() >= at.getTime() ? next : prev;
|
|
83
|
+
const { y, m, d } = partsInTz(fire, tz);
|
|
84
|
+
const occurrenceDate = `${y}-${m}-${d}`;
|
|
85
|
+
const occurrenceLabel = hhmmInTz(fire, tz);
|
|
86
|
+
// Stable, filesystem/URL-safe id: minute precision in the tz wall clock.
|
|
87
|
+
const occurrenceStamp = `${occurrenceDate}T${occurrenceLabel.replace(':', '')}`;
|
|
88
|
+
const slotId = `${schedule.id}@${occurrenceStamp}`;
|
|
89
|
+
return {
|
|
90
|
+
slotId,
|
|
91
|
+
scheduleId: schedule.id,
|
|
92
|
+
occurrenceAt: fire,
|
|
93
|
+
occurrenceAtIso: fire.toISOString(),
|
|
94
|
+
occurrenceDate,
|
|
95
|
+
occurrenceLabel,
|
|
96
|
+
timezone: tz,
|
|
97
|
+
triggerSource,
|
|
98
|
+
scheduleName: schedule.name?.trim() || schedule.id,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Validate that a trigger at `at` may run `occurrence`: it is runnable from a
|
|
103
|
+
* small lead before its scheduled time until `graceMinutes` after. Outside that
|
|
104
|
+
* window external clocks must not execute (no historical back-fill).
|
|
105
|
+
*/
|
|
106
|
+
function checkOccurrenceWindow(occurrence, at, graceMinutes, leadMinutes = 15) {
|
|
107
|
+
const sched = occurrence.occurrenceAt.getTime();
|
|
108
|
+
const leadMs = leadMinutes * 60_000;
|
|
109
|
+
const graceMs = graceMinutes * 60_000;
|
|
110
|
+
const now = at.getTime();
|
|
111
|
+
if (now < sched - leadMs) {
|
|
112
|
+
return {
|
|
113
|
+
ok: false,
|
|
114
|
+
status: 425,
|
|
115
|
+
error: `schedule ${occurrence.scheduleId} occurrence ${occurrence.occurrenceAtIso} is not due yet`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (now > sched + graceMs) {
|
|
119
|
+
return {
|
|
120
|
+
ok: false,
|
|
121
|
+
status: 410,
|
|
122
|
+
error: `schedule ${occurrence.scheduleId} occurrence ${occurrence.occurrenceAtIso} has expired (grace ${graceMinutes}m)`,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return { ok: true };
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=OccurrenceResolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OccurrenceResolver.js","sourceRoot":"","sources":["../../src/scheduler/OccurrenceResolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { SlotContext } from './SlotCoordinator';
|
|
2
|
+
import { TriggerSource } from './OccurrenceResolver';
|
|
3
|
+
/**
|
|
4
|
+
* Authenticated HTTP schedule trigger.
|
|
5
|
+
*
|
|
6
|
+
* A dumb external clock (Cloudflare cron worker, cron-job.org, GitHub Actions)
|
|
7
|
+
* POSTs a schedule id; this server wakes with the machine, verifies a bearer
|
|
8
|
+
* token, resolves the canonical occurrence from the schedule's OWN cron +
|
|
9
|
+
* timezone (never a client-supplied date), and runs that schedule synchronously
|
|
10
|
+
* so the open HTTP request keeps the machine active for the whole run — the
|
|
11
|
+
* request itself is the activity lease (no background fire-and-forget that lets
|
|
12
|
+
* a scale-to-zero host stop mid-run). All business state lives in the Slot
|
|
13
|
+
* ledger; the handler only authenticates, validates, resolves and delegates. It
|
|
14
|
+
* never queries Pixiv, loops targets, or touches the Slot DB itself.
|
|
15
|
+
*
|
|
16
|
+
* Mounting is independent of `schedulerRuntime.mode`: external mode mounts it as
|
|
17
|
+
* the primary clock; always-on/internal mode may also mount it for manual ops.
|
|
18
|
+
*/
|
|
19
|
+
export interface TriggerRunResult {
|
|
20
|
+
scheduleId: string;
|
|
21
|
+
slotId: string;
|
|
22
|
+
status: string;
|
|
23
|
+
alreadyCompleted?: boolean;
|
|
24
|
+
cells?: Array<{
|
|
25
|
+
targetId: string;
|
|
26
|
+
status: string;
|
|
27
|
+
workId: string | null;
|
|
28
|
+
error?: string | null;
|
|
29
|
+
}>;
|
|
30
|
+
}
|
|
31
|
+
export interface TriggerHandlers {
|
|
32
|
+
/** Enabled schedule ids (for 404 on unknown / GET listing). */
|
|
33
|
+
listSchedules(): string[];
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the canonical occurrence for a trigger to `scheduleId` at `at`.
|
|
36
|
+
* Returns a durable SlotContext or an HTTP error ({ status, error }).
|
|
37
|
+
*/
|
|
38
|
+
resolve(scheduleId: string, source: TriggerSource, at: Date, label?: string): {
|
|
39
|
+
context: SlotContext;
|
|
40
|
+
} | {
|
|
41
|
+
error: string;
|
|
42
|
+
status: number;
|
|
43
|
+
};
|
|
44
|
+
/** Run the schedule for a resolved occurrence; idempotent. */
|
|
45
|
+
run(scheduleId: string, context: SlotContext): Promise<TriggerRunResult>;
|
|
46
|
+
/** Read-only snapshot of a schedule's current occurrence (for GET). */
|
|
47
|
+
status(scheduleId: string): unknown;
|
|
48
|
+
}
|
|
49
|
+
export declare class ScheduleTriggerServer {
|
|
50
|
+
private readonly token;
|
|
51
|
+
private readonly handlers;
|
|
52
|
+
private server;
|
|
53
|
+
constructor(token: string | undefined, handlers: TriggerHandlers);
|
|
54
|
+
/** Token from config or SCHEDULER_TRIGGER_TOKEN env; empty => fail closed. */
|
|
55
|
+
static resolveToken(configured?: string): string | undefined;
|
|
56
|
+
start(host: string, port: number): void;
|
|
57
|
+
private auth;
|
|
58
|
+
stop(): void;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=ScheduleTriggerServer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScheduleTriggerServer.d.ts","sourceRoot":"","sources":["../../src/scheduler/ScheduleTriggerServer.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CACnG;AAED,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,aAAa,IAAI,MAAM,EAAE,CAAC;IAC1B;;;OAGG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,MAAM,GACvE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,GACxB;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,8DAA8D;IAC9D,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzE,uEAAuE;IACvE,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;CACrC;AAED,qBAAa,qBAAqB;IAI9B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ3B,OAAO,CAAC,MAAM,CAAuB;gBAGlB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,QAAQ,EAAE,eAAe;IAG5C,8EAA8E;IAC9E,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI5D,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAuDvC,OAAO,CAAC,IAAI,CAsBV;IAEF,IAAI,IAAI,IAAI;CAIb"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ScheduleTriggerServer = void 0;
|
|
7
|
+
const express_1 = __importDefault(require("express"));
|
|
8
|
+
const node_crypto_1 = require("node:crypto");
|
|
9
|
+
const logger_1 = require("../logger");
|
|
10
|
+
class ScheduleTriggerServer {
|
|
11
|
+
token;
|
|
12
|
+
handlers;
|
|
13
|
+
server = null;
|
|
14
|
+
constructor(token, handlers) {
|
|
15
|
+
this.token = token;
|
|
16
|
+
this.handlers = handlers;
|
|
17
|
+
}
|
|
18
|
+
/** Token from config or SCHEDULER_TRIGGER_TOKEN env; empty => fail closed. */
|
|
19
|
+
static resolveToken(configured) {
|
|
20
|
+
return (configured ?? process.env.SCHEDULER_TRIGGER_TOKEN ?? '').trim() || undefined;
|
|
21
|
+
}
|
|
22
|
+
start(host, port) {
|
|
23
|
+
const app = (0, express_1.default)();
|
|
24
|
+
app.use(express_1.default.json());
|
|
25
|
+
app.get('/health', (_req, res) => {
|
|
26
|
+
res.json({ status: 'ok', service: 'pixivflow-scheduler-trigger' });
|
|
27
|
+
});
|
|
28
|
+
// Read-only: list enabled schedules + their current occurrence status.
|
|
29
|
+
app.get('/internal/schedules', this.auth, (_req, res) => {
|
|
30
|
+
const schedules = this.handlers.listSchedules().map((id) => this.handlers.status(id));
|
|
31
|
+
res.json({ schedules });
|
|
32
|
+
});
|
|
33
|
+
// Trigger one schedule by id. The server resolves the occurrence from the
|
|
34
|
+
// schedule cron; the body carries no date and cannot back-fill history.
|
|
35
|
+
app.post('/internal/schedules/:scheduleId/run', this.auth, async (req, res) => {
|
|
36
|
+
try {
|
|
37
|
+
const scheduleId = req.params.scheduleId;
|
|
38
|
+
if (!this.handlers.listSchedules().includes(scheduleId)) {
|
|
39
|
+
res.status(404).json({ status: 'error', error: `unknown schedule: ${scheduleId}` });
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// Optional human label for provenance (e.g. a deploy-layer "今日早班").
|
|
43
|
+
// Bounded; never parsed; identity always derives from the cron occurrence.
|
|
44
|
+
const label = typeof req.body?.label === 'string' ? req.body.label.slice(0, 80) : undefined;
|
|
45
|
+
const resolved = this.handlers.resolve(scheduleId, 'http', new Date(), label);
|
|
46
|
+
if (!('context' in resolved)) {
|
|
47
|
+
res.status(resolved.status).json({ status: 'error', error: resolved.error });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const result = await this.handlers.run(scheduleId, resolved.context);
|
|
51
|
+
// alreadyCompleted => 200 with a clear note; real run => 200 completed.
|
|
52
|
+
res.status(result.alreadyCompleted ? 200 : 200).json({
|
|
53
|
+
status: result.status === 'failed' ? 'failed' : 'ok',
|
|
54
|
+
schedule: result,
|
|
55
|
+
note: result.alreadyCompleted ? 'already_completed' : 'completed',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
logger_1.logger.error('Schedule trigger failed', { error: error instanceof Error ? error.message : String(error) });
|
|
60
|
+
// The slot ledger resumes on the next trigger; a 500 tells the clock to
|
|
61
|
+
// retry safely (idempotent — the same occurrence/ slot is reused).
|
|
62
|
+
res.status(500).json({ status: 'error', error: 'schedule run failed; the occurrence will resume on the next trigger' });
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
this.server = app.listen(port, host, () => {
|
|
66
|
+
logger_1.logger.info('Schedule trigger server listening', { host, port, auth: this.token ? 'bearer' : 'DISABLED (no token)' });
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
auth = (req, res, next) => {
|
|
70
|
+
if (!this.token) {
|
|
71
|
+
// Fail closed: never allow an unauthenticated trigger in production.
|
|
72
|
+
res.status(503).json({ status: 'error', error: 'trigger disabled: SCHEDULER_TRIGGER_TOKEN not configured' });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const header = req.headers.authorization ?? '';
|
|
76
|
+
const presented = header.startsWith('Bearer ') ? header.slice(7).trim() : '';
|
|
77
|
+
const expected = this.token;
|
|
78
|
+
let ok = presented.length === expected.length;
|
|
79
|
+
if (ok) {
|
|
80
|
+
try {
|
|
81
|
+
ok = (0, node_crypto_1.timingSafeEqual)(Buffer.from(presented), Buffer.from(expected));
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
ok = false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (!ok) {
|
|
88
|
+
res.status(401).json({ status: 'error', error: 'unauthorized' });
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
next();
|
|
92
|
+
};
|
|
93
|
+
stop() {
|
|
94
|
+
this.server?.close();
|
|
95
|
+
this.server = null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
exports.ScheduleTriggerServer = ScheduleTriggerServer;
|
|
99
|
+
//# sourceMappingURL=ScheduleTriggerServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScheduleTriggerServer.js","sourceRoot":"","sources":["../../src/scheduler/ScheduleTriggerServer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SchedulerConfig } from '../config';
|
|
2
2
|
import { Database } from '../storage/Database';
|
|
3
|
+
import { ScheduleRunOptions } from './OccurrenceResolver';
|
|
3
4
|
/**
|
|
4
5
|
* Watchdog fallback: a schedule without an explicit `timeout` still gets this
|
|
5
6
|
* cap so a wedged download run cannot occupy the scheduler queue forever.
|
|
@@ -49,12 +50,22 @@ export declare class Scheduler {
|
|
|
49
50
|
private stopped;
|
|
50
51
|
private pending;
|
|
51
52
|
constructor(config: SchedulerConfig, database?: Database | undefined, telemetry?: JobTelemetry | undefined, scheduleId?: string, admission?: JobAdmissionController | undefined, onFailure?: ((failure: JobFailure) => Promise<void> | void) | undefined);
|
|
52
|
-
start(job: () => Promise<void>): void;
|
|
53
|
+
start(job: (options?: ScheduleRunOptions) => Promise<void>): void;
|
|
53
54
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
55
|
+
* Arm the scheduler for external/manual triggers (runNow) WITHOUT registering
|
|
56
|
+
* a cron task. Used in `external` scheduler mode where an authenticated HTTP
|
|
57
|
+
* trigger — not the in-process clock — fires runs.
|
|
56
58
|
*/
|
|
57
|
-
|
|
59
|
+
init(job: (options?: ScheduleRunOptions) => Promise<void>): void;
|
|
60
|
+
private arm;
|
|
61
|
+
/**
|
|
62
|
+
* Trigger one execution immediately (external HTTP trigger / catch-up /
|
|
63
|
+
* run-once). Honors the same guards as a cron firing. Returns false when the
|
|
64
|
+
* run is not admitted (already running/pending, stopped, maxed) so callers
|
|
65
|
+
* can report idempotent "already in progress" instead of assuming a start.
|
|
66
|
+
* Fire-and-forget: the returned boolean only reports admission.
|
|
67
|
+
*/
|
|
68
|
+
runNow(triggerOptions?: ScheduleRunOptions): boolean;
|
|
58
69
|
private executeJob;
|
|
59
70
|
/**
|
|
60
71
|
* Execute the job. Item counting happens via JobTelemetry in executeJob,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Scheduler.d.ts","sourceRoot":"","sources":["../../src/scheduler/Scheduler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAG5C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"Scheduler.d.ts","sourceRoot":"","sources":["../../src/scheduler/Scheduler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAG5C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;;GAGG;AACH,eAAO,MAAM,2BAA2B,QAAiB,CAAC;AAE1D;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IACrC,+FAA+F;IAC/F,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IACnD,gFAAgF;IAChF,aAAa,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,8DAA8D;AAC9D,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;CACvD;AAED,qBAAa,SAAS;IAYlB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IAC3B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IAhB7B,OAAO,CAAC,IAAI,CAA8B;IAC1C,OAAO,CAAC,GAAG,CAAkE;IAC7E,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,iBAAiB,CAAa;IACtC,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,mBAAmB,CAAa;IACxC,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,OAAO,CAAkB;gBAGd,MAAM,EAAE,eAAe,EACvB,QAAQ,CAAC,EAAE,QAAQ,YAAA,EACnB,SAAS,CAAC,EAAE,YAAY,YAAA,EACxB,UAAU,GAAE,MAAkB,EAC9B,SAAS,CAAC,EAAE,sBAAsB,YAAA,EAClC,SAAS,CAAC,GAAE,CAAC,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,aAAA;IAGrE,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,kBAAkB,KAAK,OAAO,CAAC,IAAI,CAAC;IAIjE;;;;OAIG;IACI,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,kBAAkB,KAAK,OAAO,CAAC,IAAI,CAAC;IAIhE,OAAO,CAAC,GAAG;IAsCX;;;;;;OAMG;IACI,MAAM,CAAC,cAAc,CAAC,EAAE,kBAAkB,GAAG,OAAO;YAO7C,UAAU;IA4NxB;;;;OAIG;YACW,mBAAmB;IAQ1B,IAAI;IAgBJ,QAAQ;;;;;;;;;CAWhB"}
|