pixivflow 3.2.0 → 3.4.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 +26 -1
- package/dist/commands/SchedulerCommand.js +31 -51
- package/dist/commands/scheduler-runtime.js +47 -7
- package/dist/config/defaults.d.ts +2 -0
- package/dist/config/defaults.js +6 -0
- package/dist/config/types.d.ts +20 -0
- package/dist/config/validation.js +11 -0
- package/dist/delivery/DeliveryDispatcher.d.ts +14 -0
- package/dist/delivery/DeliveryDispatcher.js +14 -0
- package/dist/delivery/EventCallbackDelivery.d.ts +54 -0
- package/dist/delivery/EventCallbackDelivery.js +89 -0
- package/dist/delivery/OutboxWorker.d.ts +11 -0
- package/dist/delivery/OutboxWorker.js +51 -1
- package/dist/package.json +1 -1
- package/dist/scheduler/CandidateSearchParams.d.ts +92 -0
- package/dist/scheduler/CandidateSearchParams.js +103 -0
- package/dist/scheduler/JobCancellation.d.ts +36 -0
- package/dist/scheduler/JobCancellation.js +73 -0
- package/dist/scheduler/JobEventStream.d.ts +94 -0
- package/dist/scheduler/JobEventStream.js +251 -0
- package/dist/scheduler/JobFacade.d.ts +305 -0
- package/dist/scheduler/JobFacade.js +670 -0
- package/dist/scheduler/JobProjection.d.ts +67 -0
- package/dist/scheduler/JobProjection.js +32 -0
- package/dist/scheduler/JobView.d.ts +34 -0
- package/dist/scheduler/JobView.js +82 -0
- package/dist/scheduler/ManualJobAdmission.d.ts +134 -0
- package/dist/scheduler/ManualJobAdmission.js +316 -0
- package/dist/scheduler/ManualJobService.d.ts +57 -0
- package/dist/scheduler/ManualJobService.js +92 -0
- package/dist/scheduler/ManualRefetchAdapter.d.ts +26 -0
- package/dist/scheduler/ManualRefetchAdapter.js +41 -0
- package/dist/scheduler/MultiScheduleManager.d.ts +17 -0
- package/dist/scheduler/MultiScheduleManager.js +68 -6
- package/dist/scheduler/ProtocolErrors.d.ts +68 -0
- package/dist/scheduler/ProtocolErrors.js +94 -0
- package/dist/scheduler/ScheduleTriggerServer.d.ts +56 -7
- package/dist/scheduler/ScheduleTriggerServer.js +166 -1
- package/dist/scheduler/SlotBusinessStatus.d.ts +5 -1
- package/dist/scheduler/SlotBusinessStatus.js +6 -0
- package/dist/scheduler/SlotCoordinator.d.ts +49 -5
- package/dist/scheduler/SlotCoordinator.js +89 -22
- package/dist/scheduler/StallSweep.d.ts +65 -0
- package/dist/scheduler/StallSweep.js +105 -0
- package/dist/scheduler/TargetOutcome.d.ts +39 -1
- package/dist/scheduler/TargetOutcome.js +51 -1
- package/dist/scheduler/ledger-time.d.ts +23 -0
- package/dist/scheduler/ledger-time.js +37 -0
- package/dist/storage/DatabaseMigration.js +48 -0
- package/dist/storage/repositories/DeliveryRepository.d.ts +6 -0
- package/dist/storage/repositories/DeliveryRepository.js +13 -0
- package/dist/storage/repositories/OutboxRepository.d.ts +73 -1
- package/dist/storage/repositories/OutboxRepository.js +142 -0
- package/dist/storage/repositories/SlotRepository.d.ts +34 -0
- package/dist/storage/repositories/SlotRepository.js +61 -2
- package/dist/version.js +1 -1
- package/dist/webui/package.json +1 -1
- package/package.json +1 -1
|
@@ -38,6 +38,11 @@ export interface SlotRecord {
|
|
|
38
38
|
recoveryRequestId: string | null;
|
|
39
39
|
/** Recovery policy preset ('normal'|'relaxed'); null for non-recovery slots. */
|
|
40
40
|
recoveryMode: string | null;
|
|
41
|
+
/**
|
|
42
|
+
* Occurrence-scoped retrieval view of a generic `candidate_search` job (JSON,
|
|
43
|
+
* §6); null means "run the plan exactly as configured".
|
|
44
|
+
*/
|
|
45
|
+
paramsJson: string | null;
|
|
41
46
|
}
|
|
42
47
|
export interface SlotItemRecord {
|
|
43
48
|
id: number;
|
|
@@ -83,6 +88,16 @@ export interface SlotItemRecord {
|
|
|
83
88
|
export declare class SlotRepository extends BaseRepository {
|
|
84
89
|
/** Exact manual request/target lookup for authenticated convergence checks. */
|
|
85
90
|
findManualSlot(requestId: string, targetId: string): SlotRecord | null;
|
|
91
|
+
/**
|
|
92
|
+
* Manual request lookup WITHOUT a target id — the generic job surface resolves
|
|
93
|
+
* a job from its consumer-supplied idempotency key alone.
|
|
94
|
+
*
|
|
95
|
+
* A non-empty `manual_request_id` is unique per slot (partial unique index
|
|
96
|
+
* `idx_slots_manual_request`), so the first row IS the job; the ordering only
|
|
97
|
+
* makes the answer deterministic on a ledger that still holds historical
|
|
98
|
+
* duplicates.
|
|
99
|
+
*/
|
|
100
|
+
findManualSlotByKey(requestId: string): SlotRecord | null;
|
|
86
101
|
/** Exact manual RECOVERY request/target lookup (§manual-recovery). */
|
|
87
102
|
findRecoverySlot(requestId: string, targetId: string): SlotRecord | null;
|
|
88
103
|
/**
|
|
@@ -109,6 +124,8 @@ export declare class SlotRepository extends BaseRepository {
|
|
|
109
124
|
recoveryRequestId?: string | null;
|
|
110
125
|
/** Manual recovery policy preset ('normal' | 'relaxed'). */
|
|
111
126
|
recoveryMode?: string | null;
|
|
127
|
+
/** Generic-job retrieval view (§6), stored as JSON; null = as configured. */
|
|
128
|
+
paramsJson?: string | null;
|
|
112
129
|
}): {
|
|
113
130
|
slot: SlotRecord;
|
|
114
131
|
created: boolean;
|
|
@@ -220,6 +237,23 @@ export declare class SlotRepository extends BaseRepository {
|
|
|
220
237
|
* a read-then-clear race.
|
|
221
238
|
*/
|
|
222
239
|
recoverableSlots(now?: number): SlotRecord[];
|
|
240
|
+
/**
|
|
241
|
+
* Bounded liveness sweep candidates (§liveness): slots that are still
|
|
242
|
+
* `pending` (nothing ever claimed them) long after they were recorded. A live
|
|
243
|
+
* lease is never a candidate — a claimed slot is not stuck.
|
|
244
|
+
*
|
|
245
|
+
* `createdBefore` is a SQLite UTC datetime; the comparison stays in SQL so a
|
|
246
|
+
* large ledger never has to be loaded into memory to be filtered.
|
|
247
|
+
*/
|
|
248
|
+
agedPendingSlots(createdBefore: string, now: number, limit: number): SlotRecord[];
|
|
249
|
+
/**
|
|
250
|
+
* Bounded liveness sweep candidates (§liveness): slots left `running` whose
|
|
251
|
+
* owner stopped heartbeating. The lease must be expired or absent (a live
|
|
252
|
+
* lease means a live worker, however long the work takes), and progress must
|
|
253
|
+
* be older than the stall budget — falling back to `started_at`, then
|
|
254
|
+
* `created_at`, for rows from before heartbeats were recorded.
|
|
255
|
+
*/
|
|
256
|
+
stalledRunningSlots(progressBefore: string, now: number, limit: number): SlotRecord[];
|
|
223
257
|
/**
|
|
224
258
|
* Non-terminal Slots (pending/running): work this ledger still owes, whether
|
|
225
259
|
* or not a live worker currently holds their lease.
|
|
@@ -17,6 +17,21 @@ class SlotRepository extends BaseRepository_1.BaseRepository {
|
|
|
17
17
|
const rows = this.db.prepare(`SELECT * FROM schedule_slots WHERE manual_request_id = ?`).all(requestId);
|
|
18
18
|
return rows.map((row) => this.toSlot(row)).find((slot) => slot.targetIds.includes(targetId)) ?? null;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Manual request lookup WITHOUT a target id — the generic job surface resolves
|
|
22
|
+
* a job from its consumer-supplied idempotency key alone.
|
|
23
|
+
*
|
|
24
|
+
* A non-empty `manual_request_id` is unique per slot (partial unique index
|
|
25
|
+
* `idx_slots_manual_request`), so the first row IS the job; the ordering only
|
|
26
|
+
* makes the answer deterministic on a ledger that still holds historical
|
|
27
|
+
* duplicates.
|
|
28
|
+
*/
|
|
29
|
+
findManualSlotByKey(requestId) {
|
|
30
|
+
const row = this.db
|
|
31
|
+
.prepare(`SELECT * FROM schedule_slots WHERE manual_request_id = ? ORDER BY created_at, id LIMIT 1`)
|
|
32
|
+
.get(requestId);
|
|
33
|
+
return row ? this.toSlot(row) : null;
|
|
34
|
+
}
|
|
20
35
|
/** Exact manual RECOVERY request/target lookup (§manual-recovery). */
|
|
21
36
|
findRecoverySlot(requestId, targetId) {
|
|
22
37
|
const rows = this.db.prepare(`SELECT * FROM schedule_slots WHERE recovery_request_id = ?`).all(requestId);
|
|
@@ -32,11 +47,11 @@ class SlotRepository extends BaseRepository_1.BaseRepository {
|
|
|
32
47
|
const insert = this.db.prepare(`INSERT INTO schedule_slots
|
|
33
48
|
(id, schedule_id, occurrence_at, occurrence_date, occurrence_label, timezone, target_ids,
|
|
34
49
|
status, trigger_source, slot_date, slot_name, manual_request_id, correlation_id,
|
|
35
|
-
recovery_request_id, recovery_mode)
|
|
50
|
+
recovery_request_id, recovery_mode, params_json)
|
|
36
51
|
VALUES
|
|
37
52
|
(@id, @scheduleId, @occurrenceAt, @occurrenceDate, @occurrenceLabel, @timezone, @targetIds,
|
|
38
53
|
'pending', @triggerSource, @slotDate, @slotName, @manualRequestId, @correlationId,
|
|
39
|
-
@recoveryRequestId, @recoveryMode)
|
|
54
|
+
@recoveryRequestId, @recoveryMode, @paramsJson)
|
|
40
55
|
ON CONFLICT(id) DO NOTHING`);
|
|
41
56
|
const info = insert.run({
|
|
42
57
|
id,
|
|
@@ -53,6 +68,7 @@ class SlotRepository extends BaseRepository_1.BaseRepository {
|
|
|
53
68
|
correlationId: data.correlationId ?? null,
|
|
54
69
|
recoveryRequestId: data.recoveryRequestId ?? null,
|
|
55
70
|
recoveryMode: data.recoveryMode ?? null,
|
|
71
|
+
paramsJson: data.paramsJson ?? null,
|
|
56
72
|
});
|
|
57
73
|
const created = info.changes > 0;
|
|
58
74
|
return { slot: this.getSlot(id), created };
|
|
@@ -364,6 +380,48 @@ class SlotRepository extends BaseRepository_1.BaseRepository {
|
|
|
364
380
|
.all({ now });
|
|
365
381
|
return rows.map((r) => this.toSlot(r));
|
|
366
382
|
}
|
|
383
|
+
/**
|
|
384
|
+
* Bounded liveness sweep candidates (§liveness): slots that are still
|
|
385
|
+
* `pending` (nothing ever claimed them) long after they were recorded. A live
|
|
386
|
+
* lease is never a candidate — a claimed slot is not stuck.
|
|
387
|
+
*
|
|
388
|
+
* `createdBefore` is a SQLite UTC datetime; the comparison stays in SQL so a
|
|
389
|
+
* large ledger never has to be loaded into memory to be filtered.
|
|
390
|
+
*/
|
|
391
|
+
agedPendingSlots(createdBefore, now, limit) {
|
|
392
|
+
const rows = this.db
|
|
393
|
+
.prepare(`SELECT * FROM schedule_slots
|
|
394
|
+
WHERE status = 'pending'
|
|
395
|
+
AND (lease_until IS NULL OR lease_until <= @now)
|
|
396
|
+
AND created_at IS NOT NULL
|
|
397
|
+
AND created_at <= @createdBefore
|
|
398
|
+
ORDER BY created_at ASC
|
|
399
|
+
LIMIT @limit`)
|
|
400
|
+
.all({ createdBefore, now, limit });
|
|
401
|
+
return rows.map((r) => this.toSlot(r));
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Bounded liveness sweep candidates (§liveness): slots left `running` whose
|
|
405
|
+
* owner stopped heartbeating. The lease must be expired or absent (a live
|
|
406
|
+
* lease means a live worker, however long the work takes), and progress must
|
|
407
|
+
* be older than the stall budget — falling back to `started_at`, then
|
|
408
|
+
* `created_at`, for rows from before heartbeats were recorded.
|
|
409
|
+
*/
|
|
410
|
+
stalledRunningSlots(progressBefore, now, limit) {
|
|
411
|
+
const rows = this.db
|
|
412
|
+
.prepare(`SELECT * FROM schedule_slots
|
|
413
|
+
WHERE status = 'running'
|
|
414
|
+
AND (lease_until IS NULL OR lease_until <= @now)
|
|
415
|
+
AND COALESCE(
|
|
416
|
+
CASE WHEN heartbeat_at IS NOT NULL THEN datetime(heartbeat_at / 1000, 'unixepoch') END,
|
|
417
|
+
started_at,
|
|
418
|
+
created_at
|
|
419
|
+
) <= @progressBefore
|
|
420
|
+
ORDER BY COALESCE(started_at, created_at) ASC
|
|
421
|
+
LIMIT @limit`)
|
|
422
|
+
.all({ progressBefore, now, limit });
|
|
423
|
+
return rows.map((r) => this.toSlot(r));
|
|
424
|
+
}
|
|
367
425
|
/**
|
|
368
426
|
* Non-terminal Slots (pending/running): work this ledger still owes, whether
|
|
369
427
|
* or not a live worker currently holds their lease.
|
|
@@ -442,6 +500,7 @@ class SlotRepository extends BaseRepository_1.BaseRepository {
|
|
|
442
500
|
correlationId: row.correlation_id ?? null,
|
|
443
501
|
recoveryRequestId: row.recovery_request_id ?? null,
|
|
444
502
|
recoveryMode: row.recovery_mode ?? null,
|
|
503
|
+
paramsJson: row.params_json ?? null,
|
|
445
504
|
};
|
|
446
505
|
}
|
|
447
506
|
toItem(row) {
|
package/dist/version.js
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BUILD = void 0;
|
|
4
4
|
// GENERATED by scripts/write-version.js — do not edit manually.
|
|
5
|
-
exports.BUILD = { version: '3.
|
|
5
|
+
exports.BUILD = { version: '3.4.0', commit: '5d231179a9b3' };
|
|
6
6
|
//# sourceMappingURL=version.js.map
|
package/dist/webui/package.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pixivflow",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "🎨 Pixiv 下载、筛选与自动收集工具 - 批量下载插画和小说、按标签/热度/日期筛选、定时任务与可靠 HTTP 交付 | Pixiv downloader and automation toolkit with filtering, scheduling and reliable HTTP delivery",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|