pixivflow 2.18.0 → 2.19.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/dist/commands/SchedulerCommand.js +105 -45
- package/dist/commands/SchedulerIdleLifecycle.d.ts +82 -0
- package/dist/commands/SchedulerIdleLifecycle.js +135 -0
- package/dist/commands/scheduler-runtime.d.ts +6 -0
- package/dist/commands/scheduler-runtime.js +77 -9
- package/dist/config/types.d.ts +33 -0
- package/dist/config/validation.js +10 -1
- package/dist/download/DownloadManager.d.ts +9 -0
- package/dist/download/DownloadManager.js +18 -1
- package/dist/package.json +1 -1
- package/dist/pixiv-client/PixivFlowPixivClient.d.ts +7 -1
- package/dist/pixiv-client/PixivFlowPixivClient.js +14 -8
- package/dist/pixiv-client/TargetSearchRunner.d.ts +2 -2
- package/dist/pixiv-client/TargetSearchRunner.js +21 -14
- package/dist/scheduler/MultiScheduleManager.d.ts +33 -1
- package/dist/scheduler/MultiScheduleManager.js +115 -2
- package/dist/scheduler/ScheduleTriggerServer.d.ts +32 -9
- package/dist/scheduler/ScheduleTriggerServer.js +34 -6
- package/dist/scheduler/Scheduler.d.ts +28 -1
- package/dist/scheduler/Scheduler.js +82 -7
- package/dist/scheduler/SlotCoordinator.d.ts +28 -3
- package/dist/scheduler/SlotCoordinator.js +31 -5
- package/dist/storage/repositories/SlotRepository.d.ts +28 -0
- package/dist/storage/repositories/SlotRepository.js +42 -4
- package/dist/topic/TopicPipeline.d.ts +15 -1
- package/dist/topic/TopicPipeline.js +18 -2
- package/dist/topic/TopicResolver.d.ts +5 -1
- package/dist/topic/TopicResolver.js +32 -6
- package/dist/topic/createTopicPipeline.d.ts +2 -2
- package/dist/topic/createTopicPipeline.js +5 -5
- package/dist/topic/types.d.ts +5 -1
- package/dist/utils/errors.d.ts +18 -0
- package/dist/utils/errors.js +30 -0
- package/dist/version.js +1 -1
- package/dist/webui/package.json +1 -1
- package/package.json +2 -1
- package/dist/auth/PixivAuth.d.ts.map +0 -1
- package/dist/auth/PixivAuth.js.map +0 -1
- package/dist/batch/executionResult.d.ts.map +0 -1
- package/dist/batch/executionResult.js.map +0 -1
- package/dist/batch/runDiagnostics.d.ts.map +0 -1
- package/dist/batch/runDiagnostics.js.map +0 -1
- package/dist/cli/ArgumentParser.d.ts.map +0 -1
- package/dist/cli/ArgumentParser.js.map +0 -1
- package/dist/cli/InteractivePrompt.d.ts.map +0 -1
- package/dist/cli/InteractivePrompt.js.map +0 -1
- package/dist/commands/BackupCommand.d.ts.map +0 -1
- package/dist/commands/BackupCommand.js.map +0 -1
- package/dist/commands/Command.d.ts.map +0 -1
- package/dist/commands/Command.js.map +0 -1
- package/dist/commands/CommandRegistry.d.ts.map +0 -1
- package/dist/commands/CommandRegistry.js.map +0 -1
- package/dist/commands/ConfigCommand.d.ts.map +0 -1
- package/dist/commands/ConfigCommand.js.map +0 -1
- package/dist/commands/DiagnoseEgressCommand.d.ts.map +0 -1
- package/dist/commands/DiagnoseEgressCommand.js.map +0 -1
- package/dist/commands/DirsCommand.d.ts.map +0 -1
- package/dist/commands/DirsCommand.js.map +0 -1
- package/dist/commands/DoctorCommand.d.ts.map +0 -1
- package/dist/commands/DoctorCommand.js.map +0 -1
- package/dist/commands/DownloadCommand.d.ts.map +0 -1
- package/dist/commands/DownloadCommand.js.map +0 -1
- package/dist/commands/ExecuteSlotCommand.d.ts.map +0 -1
- package/dist/commands/ExecuteSlotCommand.js.map +0 -1
- package/dist/commands/HealthCommand.d.ts.map +0 -1
- package/dist/commands/HealthCommand.js.map +0 -1
- package/dist/commands/HelpCommand.d.ts.map +0 -1
- package/dist/commands/HelpCommand.js.map +0 -1
- package/dist/commands/LoginCommand.d.ts.map +0 -1
- package/dist/commands/LoginCommand.js.map +0 -1
- package/dist/commands/LoginHeadlessCommand.d.ts.map +0 -1
- package/dist/commands/LoginHeadlessCommand.js.map +0 -1
- package/dist/commands/LogsCommand.d.ts.map +0 -1
- package/dist/commands/LogsCommand.js.map +0 -1
- package/dist/commands/MaintainCommand.d.ts.map +0 -1
- package/dist/commands/MaintainCommand.js.map +0 -1
- package/dist/commands/MigrateConfigCommand.d.ts.map +0 -1
- package/dist/commands/MigrateConfigCommand.js.map +0 -1
- package/dist/commands/MonitorCommand.d.ts.map +0 -1
- package/dist/commands/MonitorCommand.js.map +0 -1
- package/dist/commands/NormalizeCommand.d.ts.map +0 -1
- package/dist/commands/NormalizeCommand.js.map +0 -1
- package/dist/commands/OutboxCommand.d.ts.map +0 -1
- package/dist/commands/OutboxCommand.js.map +0 -1
- package/dist/commands/RandomDownloadCommand.d.ts.map +0 -1
- package/dist/commands/RandomDownloadCommand.js.map +0 -1
- package/dist/commands/ReconcileCommand.d.ts.map +0 -1
- package/dist/commands/ReconcileCommand.js.map +0 -1
- package/dist/commands/RefreshCommand.d.ts.map +0 -1
- package/dist/commands/RefreshCommand.js.map +0 -1
- package/dist/commands/RunsCommand.d.ts.map +0 -1
- package/dist/commands/RunsCommand.js.map +0 -1
- package/dist/commands/SchedulerCommand.d.ts.map +0 -1
- package/dist/commands/SchedulerCommand.js.map +0 -1
- package/dist/commands/SchedulerRunOnceCommand.d.ts.map +0 -1
- package/dist/commands/SchedulerRunOnceCommand.js.map +0 -1
- package/dist/commands/SetupCommand.d.ts.map +0 -1
- package/dist/commands/SetupCommand.js.map +0 -1
- package/dist/commands/StatusCommand.d.ts.map +0 -1
- package/dist/commands/StatusCommand.js.map +0 -1
- package/dist/commands/TagsCommand.d.ts.map +0 -1
- package/dist/commands/TagsCommand.js.map +0 -1
- package/dist/commands/TopicCommand.d.ts.map +0 -1
- package/dist/commands/TopicCommand.js.map +0 -1
- package/dist/commands/VersionCommand.d.ts.map +0 -1
- package/dist/commands/VersionCommand.js.map +0 -1
- package/dist/commands/WebUICommand.d.ts.map +0 -1
- package/dist/commands/WebUICommand.js.map +0 -1
- package/dist/commands/index.d.ts.map +0 -1
- package/dist/commands/index.js.map +0 -1
- package/dist/commands/metadata.d.ts.map +0 -1
- package/dist/commands/metadata.js.map +0 -1
- package/dist/commands/scheduler-runtime.d.ts.map +0 -1
- package/dist/commands/scheduler-runtime.js.map +0 -1
- package/dist/commands/suggestions.d.ts.map +0 -1
- package/dist/commands/suggestions.js.map +0 -1
- package/dist/commands/types.d.ts.map +0 -1
- package/dist/commands/types.js.map +0 -1
- package/dist/config/defaults.d.ts.map +0 -1
- package/dist/config/defaults.js.map +0 -1
- package/dist/config/environment.d.ts.map +0 -1
- package/dist/config/environment.js.map +0 -1
- package/dist/config/loader.d.ts.map +0 -1
- package/dist/config/loader.js.map +0 -1
- package/dist/config/path-resolution.d.ts.map +0 -1
- package/dist/config/path-resolution.js.map +0 -1
- package/dist/config/placeholders.d.ts.map +0 -1
- package/dist/config/placeholders.js.map +0 -1
- package/dist/config/types.d.ts.map +0 -1
- package/dist/config/types.js.map +0 -1
- package/dist/config/validation.d.ts.map +0 -1
- package/dist/config/validation.js.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/delivery/DeliveryAck.d.ts.map +0 -1
- package/dist/delivery/DeliveryAck.js.map +0 -1
- package/dist/delivery/DeliveryDispatcher.d.ts.map +0 -1
- package/dist/delivery/DeliveryDispatcher.js.map +0 -1
- package/dist/delivery/DeliveryService.d.ts.map +0 -1
- package/dist/delivery/DeliveryService.js.map +0 -1
- package/dist/delivery/HttpMultipartDelivery.d.ts.map +0 -1
- package/dist/delivery/HttpMultipartDelivery.js.map +0 -1
- package/dist/delivery/LegacyOutboxMigration.d.ts.map +0 -1
- package/dist/delivery/LegacyOutboxMigration.js.map +0 -1
- package/dist/delivery/OutboxWorker.d.ts.map +0 -1
- package/dist/delivery/OutboxWorker.js.map +0 -1
- package/dist/delivery/TelegramReviewDelivery.d.ts.map +0 -1
- package/dist/delivery/TelegramReviewDelivery.js.map +0 -1
- package/dist/delivery/errorClass.d.ts.map +0 -1
- package/dist/delivery/errorClass.js.map +0 -1
- package/dist/delivery/types.d.ts.map +0 -1
- package/dist/delivery/types.js.map +0 -1
- package/dist/di/Container.d.ts.map +0 -1
- package/dist/di/Container.js.map +0 -1
- package/dist/di/index.d.ts.map +0 -1
- package/dist/di/index.js.map +0 -1
- package/dist/download/DownloadManager.d.ts.map +0 -1
- package/dist/download/DownloadManager.js.map +0 -1
- package/dist/download/FileNormalizationService.d.ts.map +0 -1
- package/dist/download/FileNormalizationService.js.map +0 -1
- package/dist/download/FileService.d.ts.map +0 -1
- package/dist/download/FileService.js.map +0 -1
- package/dist/download/IllustrationDownloader.d.ts.map +0 -1
- package/dist/download/IllustrationDownloader.js.map +0 -1
- package/dist/download/NovelDownloader.d.ts.map +0 -1
- package/dist/download/NovelDownloader.js.map +0 -1
- package/dist/download/RankingService.d.ts.map +0 -1
- package/dist/download/RankingService.js.map +0 -1
- package/dist/download/UgoiraConverter.d.ts.map +0 -1
- package/dist/download/UgoiraConverter.js.map +0 -1
- package/dist/download/exec/DownloadExecutor.d.ts.map +0 -1
- package/dist/download/exec/DownloadExecutor.js.map +0 -1
- package/dist/download/handlers/IllustrationTargetHandler.d.ts.map +0 -1
- package/dist/download/handlers/IllustrationTargetHandler.js.map +0 -1
- package/dist/download/handlers/NovelTargetHandler.d.ts.map +0 -1
- package/dist/download/handlers/NovelTargetHandler.js.map +0 -1
- package/dist/download/normalization/FileNameNormalizer.d.ts.map +0 -1
- package/dist/download/normalization/FileNameNormalizer.js.map +0 -1
- package/dist/download/normalization/FileOperationUtils.d.ts.map +0 -1
- package/dist/download/normalization/FileOperationUtils.js.map +0 -1
- package/dist/download/normalization/MetadataLoader.d.ts.map +0 -1
- package/dist/download/normalization/MetadataLoader.js.map +0 -1
- package/dist/download/pipeline/DownloadPipeline.d.ts.map +0 -1
- package/dist/download/pipeline/DownloadPipeline.js.map +0 -1
- package/dist/download/plan/DownloadPlanner.d.ts.map +0 -1
- package/dist/download/plan/DownloadPlanner.js.map +0 -1
- package/dist/download/recovery/ErrorRecovery.d.ts.map +0 -1
- package/dist/download/recovery/ErrorRecovery.js.map +0 -1
- package/dist/download/report/ProgressReporter.d.ts.map +0 -1
- package/dist/download/report/ProgressReporter.js.map +0 -1
- package/dist/download-specific.d.ts.map +0 -1
- package/dist/download-specific.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/interfaces/IDatabase.d.ts.map +0 -1
- package/dist/interfaces/IDatabase.js.map +0 -1
- package/dist/interfaces/IDownloadManager.d.ts.map +0 -1
- package/dist/interfaces/IDownloadManager.js.map +0 -1
- package/dist/interfaces/IFileService.d.ts.map +0 -1
- package/dist/interfaces/IFileService.js.map +0 -1
- package/dist/interfaces/IPixivAuth.d.ts.map +0 -1
- package/dist/interfaces/IPixivAuth.js.map +0 -1
- package/dist/interfaces/IPixivClient.d.ts.map +0 -1
- package/dist/interfaces/IPixivClient.js.map +0 -1
- package/dist/interfaces/index.d.ts.map +0 -1
- package/dist/interfaces/index.js.map +0 -1
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js.map +0 -1
- package/dist/notification/NotificationPolicy.d.ts.map +0 -1
- package/dist/notification/NotificationPolicy.js.map +0 -1
- package/dist/pixiv/AuthClient.d.ts.map +0 -1
- package/dist/pixiv/AuthClient.js.map +0 -1
- package/dist/pixiv/PixivClient.d.ts.map +0 -1
- package/dist/pixiv/PixivClient.js.map +0 -1
- package/dist/pixiv-client/PixivAuthTokenProvider.d.ts.map +0 -1
- package/dist/pixiv-client/PixivAuthTokenProvider.js.map +0 -1
- package/dist/pixiv-client/PixivFlowPixivClient.d.ts.map +0 -1
- package/dist/pixiv-client/PixivFlowPixivClient.js.map +0 -1
- package/dist/pixiv-client/TargetSearchRunner.d.ts.map +0 -1
- package/dist/pixiv-client/TargetSearchRunner.js.map +0 -1
- package/dist/pixiv-client/createPixivFlowClient.d.ts.map +0 -1
- package/dist/pixiv-client/createPixivFlowClient.js.map +0 -1
- package/dist/pixiv-client/query-mapper.d.ts.map +0 -1
- package/dist/pixiv-client/query-mapper.js.map +0 -1
- package/dist/pixiv-token-getter-adapter.d.ts.map +0 -1
- package/dist/pixiv-token-getter-adapter.js.map +0 -1
- package/dist/puppeteer-login-adapter/auth-code.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/auth-code.js.map +0 -1
- package/dist/puppeteer-login-adapter/browser-launch.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/browser-launch.js.map +0 -1
- package/dist/puppeteer-login-adapter/constants.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/constants.js.map +0 -1
- package/dist/puppeteer-login-adapter/environment.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/environment.js.map +0 -1
- package/dist/puppeteer-login-adapter/login-headless.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/login-headless.js.map +0 -1
- package/dist/puppeteer-login-adapter/login-interactive.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/login-interactive.js.map +0 -1
- package/dist/puppeteer-login-adapter/pkce.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/pkce.js.map +0 -1
- package/dist/puppeteer-login-adapter/proxy.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/proxy.js.map +0 -1
- package/dist/puppeteer-login-adapter/token-exchange.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/token-exchange.js.map +0 -1
- package/dist/puppeteer-login-adapter.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter.js.map +0 -1
- package/dist/python-login-adapter/login-scripts.d.ts.map +0 -1
- package/dist/python-login-adapter/login-scripts.js.map +0 -1
- package/dist/python-login-adapter/proxy.d.ts.map +0 -1
- package/dist/python-login-adapter/proxy.js.map +0 -1
- package/dist/python-login-adapter/python-environment.d.ts.map +0 -1
- package/dist/python-login-adapter/python-environment.js.map +0 -1
- package/dist/python-login-adapter/response-converter.d.ts.map +0 -1
- package/dist/python-login-adapter/response-converter.js.map +0 -1
- package/dist/python-login-adapter.d.ts.map +0 -1
- package/dist/python-login-adapter.js.map +0 -1
- package/dist/random-download.d.ts.map +0 -1
- package/dist/random-download.js.map +0 -1
- package/dist/scheduler/MultiScheduleManager.d.ts.map +0 -1
- package/dist/scheduler/MultiScheduleManager.js.map +0 -1
- package/dist/scheduler/OccurrenceResolver.d.ts.map +0 -1
- package/dist/scheduler/OccurrenceResolver.js.map +0 -1
- package/dist/scheduler/ScheduleTriggerServer.d.ts.map +0 -1
- package/dist/scheduler/ScheduleTriggerServer.js.map +0 -1
- package/dist/scheduler/Scheduler.d.ts.map +0 -1
- package/dist/scheduler/Scheduler.js.map +0 -1
- package/dist/scheduler/SlotCoordinator.d.ts.map +0 -1
- package/dist/scheduler/SlotCoordinator.js.map +0 -1
- package/dist/scheduler/SlotStateMachine.d.ts.map +0 -1
- package/dist/scheduler/SlotStateMachine.js.map +0 -1
- package/dist/scheduler/TargetOutcome.d.ts.map +0 -1
- package/dist/scheduler/TargetOutcome.js.map +0 -1
- package/dist/scheduler/schedules.d.ts.map +0 -1
- package/dist/scheduler/schedules.js.map +0 -1
- package/dist/setup-wizard.d.ts.map +0 -1
- package/dist/setup-wizard.js.map +0 -1
- package/dist/storage/Database.d.ts.map +0 -1
- package/dist/storage/Database.js.map +0 -1
- package/dist/storage/DatabaseMigration.d.ts.map +0 -1
- package/dist/storage/DatabaseMigration.js.map +0 -1
- package/dist/storage/drivers/NodeSqliteDriver.d.ts.map +0 -1
- package/dist/storage/drivers/NodeSqliteDriver.js.map +0 -1
- package/dist/storage/drivers/SqliteDriver.d.ts.map +0 -1
- package/dist/storage/drivers/SqliteDriver.js.map +0 -1
- package/dist/storage/repositories/BaseRepository.d.ts.map +0 -1
- package/dist/storage/repositories/BaseRepository.js.map +0 -1
- package/dist/storage/repositories/ConfigHistoryRepository.d.ts.map +0 -1
- package/dist/storage/repositories/ConfigHistoryRepository.js.map +0 -1
- package/dist/storage/repositories/DeliveryRepository.d.ts.map +0 -1
- package/dist/storage/repositories/DeliveryRepository.js.map +0 -1
- package/dist/storage/repositories/DownloadQueryRepository.d.ts.map +0 -1
- package/dist/storage/repositories/DownloadQueryRepository.js.map +0 -1
- package/dist/storage/repositories/DownloadRepository.d.ts.map +0 -1
- package/dist/storage/repositories/DownloadRepository.js.map +0 -1
- package/dist/storage/repositories/DownloadStatsRepository.d.ts.map +0 -1
- package/dist/storage/repositories/DownloadStatsRepository.js.map +0 -1
- package/dist/storage/repositories/DownloadWriteRepository.d.ts.map +0 -1
- package/dist/storage/repositories/DownloadWriteRepository.js.map +0 -1
- package/dist/storage/repositories/ExecutionRepository.d.ts.map +0 -1
- package/dist/storage/repositories/ExecutionRepository.js.map +0 -1
- package/dist/storage/repositories/MetadataRepository.d.ts.map +0 -1
- package/dist/storage/repositories/MetadataRepository.js.map +0 -1
- package/dist/storage/repositories/OutboxRepository.d.ts.map +0 -1
- package/dist/storage/repositories/OutboxRepository.js.map +0 -1
- package/dist/storage/repositories/RateLimitStateRepository.d.ts.map +0 -1
- package/dist/storage/repositories/RateLimitStateRepository.js.map +0 -1
- package/dist/storage/repositories/SchedulerRepository.d.ts.map +0 -1
- package/dist/storage/repositories/SchedulerRepository.js.map +0 -1
- package/dist/storage/repositories/SlotRepository.d.ts.map +0 -1
- package/dist/storage/repositories/SlotRepository.js.map +0 -1
- package/dist/storage/repositories/TaskHistoryRepository.d.ts.map +0 -1
- package/dist/storage/repositories/TaskHistoryRepository.js.map +0 -1
- package/dist/storage/repositories/TokenRepository.d.ts.map +0 -1
- package/dist/storage/repositories/TokenRepository.js.map +0 -1
- package/dist/tags/TagDiscoveryService.d.ts.map +0 -1
- package/dist/tags/TagDiscoveryService.js.map +0 -1
- package/dist/tags/TagDiscoveryStore.d.ts.map +0 -1
- package/dist/tags/TagDiscoveryStore.js.map +0 -1
- package/dist/tags/TagPlanApplier.d.ts.map +0 -1
- package/dist/tags/TagPlanApplier.js.map +0 -1
- package/dist/tags/types.d.ts.map +0 -1
- package/dist/tags/types.js.map +0 -1
- package/dist/terminal-login/adapter-selector.d.ts.map +0 -1
- package/dist/terminal-login/adapter-selector.js.map +0 -1
- package/dist/terminal-login/constants.d.ts.map +0 -1
- package/dist/terminal-login/constants.js.map +0 -1
- package/dist/terminal-login/credentials.d.ts.map +0 -1
- package/dist/terminal-login/credentials.js.map +0 -1
- package/dist/terminal-login/output.d.ts.map +0 -1
- package/dist/terminal-login/output.js.map +0 -1
- package/dist/terminal-login/pixiv-terminal-auth.d.ts.map +0 -1
- package/dist/terminal-login/pixiv-terminal-auth.js.map +0 -1
- package/dist/terminal-login/terminal-login.d.ts.map +0 -1
- package/dist/terminal-login/terminal-login.js.map +0 -1
- package/dist/terminal-login/token-refresh.d.ts.map +0 -1
- package/dist/terminal-login/token-refresh.js.map +0 -1
- package/dist/terminal-login/types.d.ts.map +0 -1
- package/dist/terminal-login/types.js.map +0 -1
- package/dist/terminal-login.d.ts.map +0 -1
- package/dist/terminal-login.js.map +0 -1
- package/dist/topic/TopicCache.d.ts.map +0 -1
- package/dist/topic/TopicCache.js.map +0 -1
- package/dist/topic/TopicPipeline.d.ts.map +0 -1
- package/dist/topic/TopicPipeline.js.map +0 -1
- package/dist/topic/TopicResolver.d.ts.map +0 -1
- package/dist/topic/TopicResolver.js.map +0 -1
- package/dist/topic/TopicTagScorer.d.ts.map +0 -1
- package/dist/topic/TopicTagScorer.js.map +0 -1
- package/dist/topic/createTopicPipeline.d.ts.map +0 -1
- package/dist/topic/createTopicPipeline.js.map +0 -1
- package/dist/topic/types.d.ts.map +0 -1
- package/dist/topic/types.js.map +0 -1
- package/dist/utils/ai-detection.d.ts.map +0 -1
- package/dist/utils/ai-detection.js.map +0 -1
- package/dist/utils/concurrency.d.ts.map +0 -1
- package/dist/utils/concurrency.js.map +0 -1
- package/dist/utils/config-manager/config-normalization.d.ts.map +0 -1
- package/dist/utils/config-manager/config-normalization.js.map +0 -1
- package/dist/utils/config-manager/config-persistence.d.ts.map +0 -1
- package/dist/utils/config-manager/config-persistence.js.map +0 -1
- package/dist/utils/config-manager/config-repair.d.ts.map +0 -1
- package/dist/utils/config-manager/config-repair.js.map +0 -1
- package/dist/utils/config-manager/file-discovery.d.ts.map +0 -1
- package/dist/utils/config-manager/file-discovery.js.map +0 -1
- package/dist/utils/config-manager/file-operations.d.ts.map +0 -1
- package/dist/utils/config-manager/file-operations.js.map +0 -1
- package/dist/utils/config-manager.d.ts.map +0 -1
- package/dist/utils/config-manager.js.map +0 -1
- package/dist/utils/config-parser/field-definitions.d.ts.map +0 -1
- package/dist/utils/config-parser/field-definitions.js.map +0 -1
- package/dist/utils/config-parser/parser.d.ts.map +0 -1
- package/dist/utils/config-parser/parser.js.map +0 -1
- package/dist/utils/config-parser/reporter.d.ts.map +0 -1
- package/dist/utils/config-parser/reporter.js.map +0 -1
- package/dist/utils/config-parser/validator.d.ts.map +0 -1
- package/dist/utils/config-parser/validator.js.map +0 -1
- package/dist/utils/config-parser.d.ts.map +0 -1
- package/dist/utils/config-parser.js.map +0 -1
- package/dist/utils/config-path-migrator/auto-fix.d.ts.map +0 -1
- package/dist/utils/config-path-migrator/auto-fix.js.map +0 -1
- package/dist/utils/config-path-migrator/path-migration-core.d.ts.map +0 -1
- package/dist/utils/config-path-migrator/path-migration-core.js.map +0 -1
- package/dist/utils/config-path-migrator/storage-path-migration.d.ts.map +0 -1
- package/dist/utils/config-path-migrator/storage-path-migration.js.map +0 -1
- package/dist/utils/config-path-migrator.d.ts.map +0 -1
- package/dist/utils/config-path-migrator.js.map +0 -1
- package/dist/utils/config-validator-unified.d.ts.map +0 -1
- package/dist/utils/config-validator-unified.js.map +0 -1
- package/dist/utils/date-utils.d.ts.map +0 -1
- package/dist/utils/date-utils.js.map +0 -1
- package/dist/utils/directory-info.d.ts.map +0 -1
- package/dist/utils/directory-info.js.map +0 -1
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/fs.d.ts.map +0 -1
- package/dist/utils/fs.js.map +0 -1
- package/dist/utils/language-detection.d.ts.map +0 -1
- package/dist/utils/language-detection.js.map +0 -1
- package/dist/utils/login-helper.d.ts.map +0 -1
- package/dist/utils/login-helper.js.map +0 -1
- package/dist/utils/pixiv-date-utils.d.ts.map +0 -1
- package/dist/utils/pixiv-date-utils.js.map +0 -1
- package/dist/utils/pixiv-sort.d.ts.map +0 -1
- package/dist/utils/pixiv-sort.js.map +0 -1
- package/dist/utils/pixiv-url-parser.d.ts.map +0 -1
- package/dist/utils/pixiv-url-parser.js.map +0 -1
- package/dist/utils/pixiv-utils.d.ts.map +0 -1
- package/dist/utils/pixiv-utils.js.map +0 -1
- package/dist/utils/project-root.d.ts.map +0 -1
- package/dist/utils/project-root.js.map +0 -1
- package/dist/utils/redact.d.ts.map +0 -1
- package/dist/utils/redact.js.map +0 -1
- package/dist/utils/resource-manager.d.ts.map +0 -1
- package/dist/utils/resource-manager.js.map +0 -1
- package/dist/utils/stdin.d.ts.map +0 -1
- package/dist/utils/stdin.js.map +0 -1
- package/dist/utils/target-label.d.ts.map +0 -1
- package/dist/utils/target-label.js.map +0 -1
- package/dist/utils/token-maintenance.d.ts.map +0 -1
- package/dist/utils/token-maintenance.js.map +0 -1
- package/dist/utils/token-manager.d.ts.map +0 -1
- package/dist/utils/token-manager.js.map +0 -1
- package/dist/utils/token-revocation.d.ts.map +0 -1
- package/dist/utils/token-revocation.js.map +0 -1
- package/dist/utils/type-guards.d.ts.map +0 -1
- package/dist/utils/type-guards.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
- package/dist/webui/index.d.ts.map +0 -1
- package/dist/webui/index.js.map +0 -1
- package/dist/webui/ports.d.ts.map +0 -1
- package/dist/webui/ports.js.map +0 -1
- package/dist/webui/routes/auth.d.ts.map +0 -1
- package/dist/webui/routes/auth.js.map +0 -1
- package/dist/webui/routes/config-utils.d.ts.map +0 -1
- package/dist/webui/routes/config-utils.js.map +0 -1
- package/dist/webui/routes/config.d.ts.map +0 -1
- package/dist/webui/routes/config.js.map +0 -1
- package/dist/webui/routes/download.d.ts.map +0 -1
- package/dist/webui/routes/download.js.map +0 -1
- package/dist/webui/routes/files.d.ts.map +0 -1
- package/dist/webui/routes/files.js.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-login-handler.d.ts.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-login-handler.js.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-logout-handler.d.ts.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-logout-handler.js.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-status-handler.d.ts.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-status-handler.js.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-token-handler.d.ts.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-token-handler.js.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-utils.d.ts.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-utils.js.map +0 -1
- package/dist/webui/routes/handlers/auth-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/auth-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/config-backup-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/config-backup-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/config-basic-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/config-basic-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/config-diagnostic-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/config-diagnostic-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/config-files-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/config-files-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/config-history-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/config-history-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/download-basic-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/download-basic-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/download-batch-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/download-batch-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/download-incomplete-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/download-incomplete-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/download-url-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/download-url-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/files-list-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/files-list-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/files-operations-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/files-operations-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/logs-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/logs-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/stats-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/stats-handlers.js.map +0 -1
- package/dist/webui/routes/logs.d.ts.map +0 -1
- package/dist/webui/routes/logs.js.map +0 -1
- package/dist/webui/routes/stats.d.ts.map +0 -1
- package/dist/webui/routes/stats.js.map +0 -1
- package/dist/webui/server/auth-middleware.d.ts.map +0 -1
- package/dist/webui/server/auth-middleware.js.map +0 -1
- package/dist/webui/server/server-middleware.d.ts.map +0 -1
- package/dist/webui/server/server-middleware.js.map +0 -1
- package/dist/webui/server/server-routes.d.ts.map +0 -1
- package/dist/webui/server/server-routes.js.map +0 -1
- package/dist/webui/server/server-static.d.ts.map +0 -1
- package/dist/webui/server/server-static.js.map +0 -1
- package/dist/webui/server/server-utils.d.ts.map +0 -1
- package/dist/webui/server/server-utils.js.map +0 -1
- package/dist/webui/server/server.d.ts.map +0 -1
- package/dist/webui/server/server.js.map +0 -1
- package/dist/webui/server/types.d.ts.map +0 -1
- package/dist/webui/server/types.js.map +0 -1
- package/dist/webui/services/DownloadTaskManager.d.ts.map +0 -1
- package/dist/webui/services/DownloadTaskManager.js.map +0 -1
- package/dist/webui/utils/config-validator.d.ts.map +0 -1
- package/dist/webui/utils/config-validator.js.map +0 -1
- package/dist/webui/utils/error-codes.d.ts.map +0 -1
- package/dist/webui/utils/error-codes.js.map +0 -1
- package/dist/webui/utils/task-serialize.d.ts.map +0 -1
- package/dist/webui/utils/task-serialize.js.map +0 -1
- package/dist/webui/websocket/DownloadStatus.d.ts.map +0 -1
- package/dist/webui/websocket/DownloadStatus.js.map +0 -1
- package/dist/webui/websocket/LogStream.d.ts.map +0 -1
- package/dist/webui/websocket/LogStream.js.map +0 -1
|
@@ -6,6 +6,19 @@ import { ScheduleRunOptions } from './OccurrenceResolver';
|
|
|
6
6
|
* cap so a wedged download run cannot occupy the scheduler queue forever.
|
|
7
7
|
*/
|
|
8
8
|
export declare const DEFAULT_SCHEDULE_TIMEOUT_MS: number;
|
|
9
|
+
/**
|
|
10
|
+
* How long a run may keep draining after its timeout fired before the scheduler
|
|
11
|
+
* gives up on it.
|
|
12
|
+
*
|
|
13
|
+
* Cancellation is cooperative, so a request that never returns (or a retry loop
|
|
14
|
+
* that ignores the abort) can outlive the timeout indefinitely. Awaiting that
|
|
15
|
+
* forever is what kept a timed-out run holding its Slot lease: the scheduler
|
|
16
|
+
* stayed `running`, the runtime kept heart-beating, and the occurrence could
|
|
17
|
+
* never be recovered. After this window the run is declared abandoned — the
|
|
18
|
+
* scheduler slot is freed and the runtime takes the Slot terminal — so a wedged
|
|
19
|
+
* job degrades into one recorded failure instead of a permanent hang.
|
|
20
|
+
*/
|
|
21
|
+
export declare const ABORT_DRAIN_MS: number;
|
|
9
22
|
/**
|
|
10
23
|
* Optional integration hooks allowing the host command to provide real
|
|
11
24
|
* accounting data and cooperative cancellation to the scheduler.
|
|
@@ -29,6 +42,18 @@ export interface JobFailure {
|
|
|
29
42
|
consecutiveFailures: number;
|
|
30
43
|
stopped: boolean;
|
|
31
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Reported when a run was still executing after its timeout AND the drain window
|
|
47
|
+
* expired, so it can never be awaited to completion. The host must treat the run
|
|
48
|
+
* as finished: stop renewing any lease it owns and take its Slot terminal, or a
|
|
49
|
+
* recovery sweep will re-dispatch the same occurrence while this run is alive.
|
|
50
|
+
*/
|
|
51
|
+
export interface JobAbandoned {
|
|
52
|
+
scheduleId: string;
|
|
53
|
+
executionNumber: number;
|
|
54
|
+
errorMessage: string | null;
|
|
55
|
+
drainWindowMs: number;
|
|
56
|
+
}
|
|
32
57
|
/** Admission is acquired before timeout/accounting starts. */
|
|
33
58
|
export interface JobAdmissionController {
|
|
34
59
|
acquire(scheduleId: string): Promise<JobLease | null>;
|
|
@@ -40,6 +65,7 @@ export declare class Scheduler {
|
|
|
40
65
|
private readonly scheduleId;
|
|
41
66
|
private readonly admission?;
|
|
42
67
|
private readonly onFailure?;
|
|
68
|
+
private readonly onAbandoned?;
|
|
43
69
|
private task;
|
|
44
70
|
private job;
|
|
45
71
|
private running;
|
|
@@ -47,9 +73,10 @@ export declare class Scheduler {
|
|
|
47
73
|
private executionCount;
|
|
48
74
|
private consecutiveFailures;
|
|
49
75
|
private timeoutHandle;
|
|
76
|
+
private drainHandle;
|
|
50
77
|
private stopped;
|
|
51
78
|
private pending;
|
|
52
|
-
constructor(config: SchedulerConfig, database?: Database | undefined, telemetry?: JobTelemetry | undefined, scheduleId?: string, admission?: JobAdmissionController | undefined, onFailure?: ((failure: JobFailure) => Promise<void> | void) | undefined);
|
|
79
|
+
constructor(config: SchedulerConfig, database?: Database | undefined, telemetry?: JobTelemetry | undefined, scheduleId?: string, admission?: JobAdmissionController | undefined, onFailure?: ((failure: JobFailure) => Promise<void> | void) | undefined, onAbandoned?: ((abandoned: JobAbandoned) => Promise<void> | void) | undefined);
|
|
53
80
|
start(job: (options?: ScheduleRunOptions) => Promise<void>): void;
|
|
54
81
|
/**
|
|
55
82
|
* Arm the scheduler for external/manual triggers (runNow) WITHOUT registering
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.Scheduler = exports.DEFAULT_SCHEDULE_TIMEOUT_MS = void 0;
|
|
6
|
+
exports.Scheduler = exports.ABORT_DRAIN_MS = exports.DEFAULT_SCHEDULE_TIMEOUT_MS = void 0;
|
|
7
7
|
const node_cron_1 = __importDefault(require("node-cron"));
|
|
8
8
|
const logger_1 = require("../logger");
|
|
9
9
|
const errors_1 = require("../utils/errors");
|
|
@@ -12,6 +12,19 @@ const errors_1 = require("../utils/errors");
|
|
|
12
12
|
* cap so a wedged download run cannot occupy the scheduler queue forever.
|
|
13
13
|
*/
|
|
14
14
|
exports.DEFAULT_SCHEDULE_TIMEOUT_MS = 30 * 60 * 1000;
|
|
15
|
+
/**
|
|
16
|
+
* How long a run may keep draining after its timeout fired before the scheduler
|
|
17
|
+
* gives up on it.
|
|
18
|
+
*
|
|
19
|
+
* Cancellation is cooperative, so a request that never returns (or a retry loop
|
|
20
|
+
* that ignores the abort) can outlive the timeout indefinitely. Awaiting that
|
|
21
|
+
* forever is what kept a timed-out run holding its Slot lease: the scheduler
|
|
22
|
+
* stayed `running`, the runtime kept heart-beating, and the occurrence could
|
|
23
|
+
* never be recovered. After this window the run is declared abandoned — the
|
|
24
|
+
* scheduler slot is freed and the runtime takes the Slot terminal — so a wedged
|
|
25
|
+
* job degrades into one recorded failure instead of a permanent hang.
|
|
26
|
+
*/
|
|
27
|
+
exports.ABORT_DRAIN_MS = 30 * 1000;
|
|
15
28
|
class Scheduler {
|
|
16
29
|
config;
|
|
17
30
|
database;
|
|
@@ -19,6 +32,7 @@ class Scheduler {
|
|
|
19
32
|
scheduleId;
|
|
20
33
|
admission;
|
|
21
34
|
onFailure;
|
|
35
|
+
onAbandoned;
|
|
22
36
|
task = null;
|
|
23
37
|
job = null;
|
|
24
38
|
running = false;
|
|
@@ -26,15 +40,17 @@ class Scheduler {
|
|
|
26
40
|
executionCount = 0;
|
|
27
41
|
consecutiveFailures = 0;
|
|
28
42
|
timeoutHandle = null;
|
|
43
|
+
drainHandle = null;
|
|
29
44
|
stopped = false;
|
|
30
45
|
pending = false;
|
|
31
|
-
constructor(config, database, telemetry, scheduleId = 'default', admission, onFailure) {
|
|
46
|
+
constructor(config, database, telemetry, scheduleId = 'default', admission, onFailure, onAbandoned) {
|
|
32
47
|
this.config = config;
|
|
33
48
|
this.database = database;
|
|
34
49
|
this.telemetry = telemetry;
|
|
35
50
|
this.scheduleId = scheduleId;
|
|
36
51
|
this.admission = admission;
|
|
37
52
|
this.onFailure = onFailure;
|
|
53
|
+
this.onAbandoned = onAbandoned;
|
|
38
54
|
}
|
|
39
55
|
start(job) {
|
|
40
56
|
this.arm(job, true);
|
|
@@ -181,6 +197,11 @@ class Scheduler {
|
|
|
181
197
|
scheduleId: this.scheduleId,
|
|
182
198
|
});
|
|
183
199
|
// Set up timeout if configured
|
|
200
|
+
let abandoned = false;
|
|
201
|
+
let resolveDrainExpired = null;
|
|
202
|
+
const drainExpired = new Promise((resolve) => {
|
|
203
|
+
resolveDrainExpired = resolve;
|
|
204
|
+
});
|
|
184
205
|
if (this.config.timeout) {
|
|
185
206
|
this.timeoutHandle = setTimeout(() => {
|
|
186
207
|
if (this.running) {
|
|
@@ -194,15 +215,33 @@ class Scheduler {
|
|
|
194
215
|
catch (cancelError) {
|
|
195
216
|
logger_1.logger.warn('Failed to request job cancellation', { error: cancelError });
|
|
196
217
|
}
|
|
197
|
-
//
|
|
198
|
-
//
|
|
218
|
+
// Bounded drain. requestCancel is cooperative, so this run is only
|
|
219
|
+
// guaranteed to be *asked* to stop; a request that ignores the abort
|
|
220
|
+
// would otherwise hold `running` (and, through the host, the Slot
|
|
221
|
+
// lease) for good. After the drain window the run is abandoned.
|
|
222
|
+
this.drainHandle = setTimeout(() => {
|
|
223
|
+
abandoned = true;
|
|
224
|
+
errorMessage =
|
|
225
|
+
`Execution timeout after ${this.config.timeout}ms; the job was still running after the ` +
|
|
226
|
+
`${exports.ABORT_DRAIN_MS}ms drain window and has been abandoned`;
|
|
227
|
+
logger_1.logger.error('Job did not settle within the drain window; abandoning it', {
|
|
228
|
+
scheduleId: this.scheduleId,
|
|
229
|
+
drainWindowMs: exports.ABORT_DRAIN_MS,
|
|
230
|
+
});
|
|
231
|
+
resolveDrainExpired?.();
|
|
232
|
+
}, exports.ABORT_DRAIN_MS);
|
|
233
|
+
this.drainHandle.unref?.();
|
|
234
|
+
// Keep running=true until the aborted job settles (or the drain
|
|
235
|
+
// expires) so the concurrent-run guard stays correct.
|
|
199
236
|
}
|
|
200
237
|
}, this.config.timeout);
|
|
238
|
+
this.timeoutHandle.unref?.();
|
|
201
239
|
}
|
|
240
|
+
const jobPromise = this.executeWithTracking(job, (count) => {
|
|
241
|
+
itemsDownloaded = count;
|
|
242
|
+
}, triggerOptions);
|
|
202
243
|
try {
|
|
203
|
-
await this.
|
|
204
|
-
itemsDownloaded = count;
|
|
205
|
-
});
|
|
244
|
+
await (this.config.timeout ? Promise.race([jobPromise, drainExpired]) : jobPromise);
|
|
206
245
|
if (timeoutOccurred) {
|
|
207
246
|
status = 'timeout';
|
|
208
247
|
this.consecutiveFailures++;
|
|
@@ -245,6 +284,38 @@ class Scheduler {
|
|
|
245
284
|
clearTimeout(this.timeoutHandle);
|
|
246
285
|
this.timeoutHandle = null;
|
|
247
286
|
}
|
|
287
|
+
if (this.drainHandle) {
|
|
288
|
+
clearTimeout(this.drainHandle);
|
|
289
|
+
this.drainHandle = null;
|
|
290
|
+
}
|
|
291
|
+
if (abandoned) {
|
|
292
|
+
// The job is still running somewhere. Its abort signal has been fired, so
|
|
293
|
+
// it unwinds at the next checkpoint, but we stop waiting for it: capture
|
|
294
|
+
// its eventual rejection (nothing else can) and let the host finish the
|
|
295
|
+
// bookkeeping it owns — stop the lease heartbeat and take the Slot
|
|
296
|
+
// terminal — so a recovery sweep cannot re-dispatch the same occurrence
|
|
297
|
+
// while this run is still alive.
|
|
298
|
+
void jobPromise.catch((error) => {
|
|
299
|
+
logger_1.logger.warn('Abandoned job settled after the drain window', {
|
|
300
|
+
scheduleId: this.scheduleId,
|
|
301
|
+
error: error instanceof Error ? error.message : String(error),
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
try {
|
|
305
|
+
await this.onAbandoned?.({
|
|
306
|
+
scheduleId: this.scheduleId,
|
|
307
|
+
executionNumber,
|
|
308
|
+
errorMessage,
|
|
309
|
+
drainWindowMs: exports.ABORT_DRAIN_MS,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
logger_1.logger.warn('Failed to report an abandoned job', {
|
|
314
|
+
scheduleId: this.scheduleId,
|
|
315
|
+
error: error instanceof Error ? error.message : String(error),
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
}
|
|
248
319
|
const endTime = new Date();
|
|
249
320
|
const duration = endTime.getTime() - startTime.getTime();
|
|
250
321
|
// Log to database if available
|
|
@@ -298,6 +369,10 @@ class Scheduler {
|
|
|
298
369
|
clearTimeout(this.timeoutHandle);
|
|
299
370
|
this.timeoutHandle = null;
|
|
300
371
|
}
|
|
372
|
+
if (this.drainHandle) {
|
|
373
|
+
clearTimeout(this.drainHandle);
|
|
374
|
+
this.drainHandle = null;
|
|
375
|
+
}
|
|
301
376
|
logger_1.logger.info('Scheduler stopped', {
|
|
302
377
|
totalExecutions: this.executionCount,
|
|
303
378
|
consecutiveFailures: this.consecutiveFailures,
|
|
@@ -3,6 +3,19 @@ import { Database } from '../storage/Database';
|
|
|
3
3
|
import { CellStatus, SlotItemRecord, SlotRecord, SlotStatus } from '../storage/repositories/SlotRepository';
|
|
4
4
|
import { TriggerSource } from './OccurrenceResolver';
|
|
5
5
|
import { TargetOutcome } from './TargetOutcome';
|
|
6
|
+
/**
|
|
7
|
+
* Execution-lease TTL and heartbeat cadence.
|
|
8
|
+
*
|
|
9
|
+
* These describe how fast a DEAD worker is noticed — they are deliberately
|
|
10
|
+
* unrelated to `schedule.timeout` (which caps how long a run may take). Tying
|
|
11
|
+
* the two together made a crashed worker hold its slot for up to 31 minutes
|
|
12
|
+
* before any other trigger could resume it. A live worker renews its lease every
|
|
13
|
+
* SLOT_HEARTBEAT_MS, so the worst-case recovery delay after a crash/restart is
|
|
14
|
+
* about SLOT_LEASE_TTL_MS. The TTL stays comfortably above the heartbeat so an
|
|
15
|
+
* occasional stalled event loop cannot make a healthy run look dead.
|
|
16
|
+
*/
|
|
17
|
+
export declare const SLOT_LEASE_TTL_MS: number;
|
|
18
|
+
export declare const SLOT_HEARTBEAT_MS: number;
|
|
6
19
|
/**
|
|
7
20
|
* Durable execution context attached to a run. A scheduled occurrence always
|
|
8
21
|
* has a slotId; an ad-hoc/manual run has none (it never touches the slot ledger).
|
|
@@ -62,13 +75,25 @@ export declare class SlotCoordinator {
|
|
|
62
75
|
*/
|
|
63
76
|
resolveOccurrence(schedule: ScheduleConfig, config: StandaloneConfig, triggerSource: TriggerSource, at?: Date, requestedSlotName?: string): SlotResolveResult;
|
|
64
77
|
/**
|
|
65
|
-
*
|
|
66
|
-
* creation. A later config reload cannot add/remove cells for this
|
|
78
|
+
* Durably open (or resume) an occurrence and snapshot its target membership
|
|
79
|
+
* on first creation. A later config reload cannot add/remove cells for this
|
|
80
|
+
* occurrence.
|
|
81
|
+
*
|
|
82
|
+
* This deliberately does NOT mark the slot `running`: the HTTP trigger calls
|
|
83
|
+
* it before dispatch so that a crash between "accepted" and "claimed" still
|
|
84
|
+
* leaves a durable `pending` row for reconciliation to pick up (see
|
|
85
|
+
* `markRunning` and MultiScheduleManager's recovery loop).
|
|
67
86
|
*/
|
|
68
|
-
|
|
87
|
+
prepare(slot: SlotContext, schedule: ScheduleConfig, targets: TargetConfig[]): {
|
|
69
88
|
slotRec: SlotRecord;
|
|
70
89
|
alreadyCompleted: boolean;
|
|
71
90
|
};
|
|
91
|
+
/**
|
|
92
|
+
* Mark the occurrence as actually executing. Called by the worker that has
|
|
93
|
+
* already won the cross-process lease (never by the accepting adapter), so a
|
|
94
|
+
* slot is never reported `running` by a process that is not running it.
|
|
95
|
+
*/
|
|
96
|
+
markRunning(slotId: string): void;
|
|
72
97
|
/**
|
|
73
98
|
* The targets that still need to run for this occurrence. Membership comes
|
|
74
99
|
* from the materialized snapshot (stable), intersected with the targets the
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SlotCoordinator = void 0;
|
|
3
|
+
exports.SlotCoordinator = exports.SLOT_HEARTBEAT_MS = exports.SLOT_LEASE_TTL_MS = void 0;
|
|
4
4
|
exports.timezoneForSchedule = timezoneForSchedule;
|
|
5
5
|
const logger_1 = require("../logger");
|
|
6
6
|
const OccurrenceResolver_1 = require("./OccurrenceResolver");
|
|
7
|
+
/**
|
|
8
|
+
* Execution-lease TTL and heartbeat cadence.
|
|
9
|
+
*
|
|
10
|
+
* These describe how fast a DEAD worker is noticed — they are deliberately
|
|
11
|
+
* unrelated to `schedule.timeout` (which caps how long a run may take). Tying
|
|
12
|
+
* the two together made a crashed worker hold its slot for up to 31 minutes
|
|
13
|
+
* before any other trigger could resume it. A live worker renews its lease every
|
|
14
|
+
* SLOT_HEARTBEAT_MS, so the worst-case recovery delay after a crash/restart is
|
|
15
|
+
* about SLOT_LEASE_TTL_MS. The TTL stays comfortably above the heartbeat so an
|
|
16
|
+
* occasional stalled event loop cannot make a healthy run look dead.
|
|
17
|
+
*/
|
|
18
|
+
exports.SLOT_LEASE_TTL_MS = 3 * 60 * 1000;
|
|
19
|
+
exports.SLOT_HEARTBEAT_MS = 30 * 1000;
|
|
7
20
|
/**
|
|
8
21
|
* Owns the Schedule Slot ledger for one scheduler run. A Slot is one durable
|
|
9
22
|
* execution occurrence of a Schedule (NOT a morning/evening row). It ensures
|
|
@@ -58,10 +71,16 @@ class SlotCoordinator {
|
|
|
58
71
|
};
|
|
59
72
|
}
|
|
60
73
|
/**
|
|
61
|
-
*
|
|
62
|
-
* creation. A later config reload cannot add/remove cells for this
|
|
74
|
+
* Durably open (or resume) an occurrence and snapshot its target membership
|
|
75
|
+
* on first creation. A later config reload cannot add/remove cells for this
|
|
76
|
+
* occurrence.
|
|
77
|
+
*
|
|
78
|
+
* This deliberately does NOT mark the slot `running`: the HTTP trigger calls
|
|
79
|
+
* it before dispatch so that a crash between "accepted" and "claimed" still
|
|
80
|
+
* leaves a durable `pending` row for reconciliation to pick up (see
|
|
81
|
+
* `markRunning` and MultiScheduleManager's recovery loop).
|
|
63
82
|
*/
|
|
64
|
-
|
|
83
|
+
prepare(slot, schedule, targets) {
|
|
65
84
|
const targetIds = targets.map((t) => t.id).filter((id) => Boolean(id));
|
|
66
85
|
const { slot: slotRec, created } = this.database.slots.getOrCreateSlot(slot.slotId, {
|
|
67
86
|
scheduleId: slot.scheduleId,
|
|
@@ -86,9 +105,16 @@ class SlotCoordinator {
|
|
|
86
105
|
if (slotRec.status === 'success' || slotRec.status === 'partial') {
|
|
87
106
|
return { slotRec, alreadyCompleted: true };
|
|
88
107
|
}
|
|
89
|
-
this.database.slots.markSlotStatus(slot.slotId, 'running');
|
|
90
108
|
return { slotRec, alreadyCompleted: false };
|
|
91
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Mark the occurrence as actually executing. Called by the worker that has
|
|
112
|
+
* already won the cross-process lease (never by the accepting adapter), so a
|
|
113
|
+
* slot is never reported `running` by a process that is not running it.
|
|
114
|
+
*/
|
|
115
|
+
markRunning(slotId) {
|
|
116
|
+
this.database.slots.markSlotStatus(slotId, 'running');
|
|
117
|
+
}
|
|
92
118
|
/**
|
|
93
119
|
* The targets that still need to run for this occurrence. Membership comes
|
|
94
120
|
* from the materialized snapshot (stable), intersected with the targets the
|
|
@@ -115,6 +115,34 @@ export declare class SlotRepository extends BaseRepository {
|
|
|
115
115
|
};
|
|
116
116
|
/** Slots whose lease expired while still non-terminal (crashed workers). */
|
|
117
117
|
slotsWithStaleLease(now?: number): string[];
|
|
118
|
+
/**
|
|
119
|
+
* Non-terminal slots no live worker currently owns, and which therefore should
|
|
120
|
+
* be re-dispatched.
|
|
121
|
+
*
|
|
122
|
+
* Two distinct situations qualify, and both must be recovered:
|
|
123
|
+
* - `lease_until <= now`: a worker claimed the slot and then died (crash, OOM,
|
|
124
|
+
* machine stop, redeploy). Its heartbeat stopped, so the lease expired.
|
|
125
|
+
* - `lease_until IS NULL`: a durable slot was recorded (accepted) but no worker
|
|
126
|
+
* ever claimed it — e.g. the process died between "occurrence recorded" and
|
|
127
|
+
* "lease claimed". Nothing else will ever pick that up.
|
|
128
|
+
*
|
|
129
|
+
* Callers must NOT clear the lease from here. Recovery re-DISPATCHES the slot
|
|
130
|
+
* and the atomic CAS in `claimSlotLease` still elects the single winner, so a
|
|
131
|
+
* merely slow (but healthy) owner that heartbeats mid-scan cannot be robbed by
|
|
132
|
+
* a read-then-clear race.
|
|
133
|
+
*/
|
|
134
|
+
recoverableSlots(now?: number): SlotRecord[];
|
|
135
|
+
/**
|
|
136
|
+
* Non-terminal Slots (pending/running): work this ledger still owes, whether
|
|
137
|
+
* or not a live worker currently holds their lease.
|
|
138
|
+
*
|
|
139
|
+
* Deliberately NOT `recoverableSlots()`. That query excludes slots with a live
|
|
140
|
+
* lease — i.e. exactly the slots being executed right now — so using it as an
|
|
141
|
+
* "is anything still running?" probe reports idle in the middle of a run. The
|
|
142
|
+
* external-worker lifecycle needs to answer the opposite question, so it asks
|
|
143
|
+
* this one instead.
|
|
144
|
+
*/
|
|
145
|
+
countActiveSlots(): number;
|
|
118
146
|
/** Record an error without changing terminality (retryable failure keeps the cell resumable). */
|
|
119
147
|
setCellError(slotId: string, targetId: string, error: string): void;
|
|
120
148
|
/** Work ids already locked in THIS slot (to stop two cells taking the same work). */
|
|
@@ -211,12 +211,50 @@ class SlotRepository extends BaseRepository_1.BaseRepository {
|
|
|
211
211
|
}
|
|
212
212
|
/** Slots whose lease expired while still non-terminal (crashed workers). */
|
|
213
213
|
slotsWithStaleLease(now = Date.now()) {
|
|
214
|
+
return this.recoverableSlots(now)
|
|
215
|
+
.filter((slot) => slot.leaseUntil !== null)
|
|
216
|
+
.map((slot) => slot.id);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Non-terminal slots no live worker currently owns, and which therefore should
|
|
220
|
+
* be re-dispatched.
|
|
221
|
+
*
|
|
222
|
+
* Two distinct situations qualify, and both must be recovered:
|
|
223
|
+
* - `lease_until <= now`: a worker claimed the slot and then died (crash, OOM,
|
|
224
|
+
* machine stop, redeploy). Its heartbeat stopped, so the lease expired.
|
|
225
|
+
* - `lease_until IS NULL`: a durable slot was recorded (accepted) but no worker
|
|
226
|
+
* ever claimed it — e.g. the process died between "occurrence recorded" and
|
|
227
|
+
* "lease claimed". Nothing else will ever pick that up.
|
|
228
|
+
*
|
|
229
|
+
* Callers must NOT clear the lease from here. Recovery re-DISPATCHES the slot
|
|
230
|
+
* and the atomic CAS in `claimSlotLease` still elects the single winner, so a
|
|
231
|
+
* merely slow (but healthy) owner that heartbeats mid-scan cannot be robbed by
|
|
232
|
+
* a read-then-clear race.
|
|
233
|
+
*/
|
|
234
|
+
recoverableSlots(now = Date.now()) {
|
|
214
235
|
const rows = this.db
|
|
215
|
-
.prepare(`SELECT
|
|
216
|
-
WHERE
|
|
217
|
-
AND
|
|
236
|
+
.prepare(`SELECT * FROM schedule_slots
|
|
237
|
+
WHERE status IN ('pending','running')
|
|
238
|
+
AND (lease_until IS NULL OR lease_until <= @now)
|
|
239
|
+
ORDER BY COALESCE(occurrence_at, 0) ASC`)
|
|
218
240
|
.all({ now });
|
|
219
|
-
return rows.map((r) => r
|
|
241
|
+
return rows.map((r) => this.toSlot(r));
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Non-terminal Slots (pending/running): work this ledger still owes, whether
|
|
245
|
+
* or not a live worker currently holds their lease.
|
|
246
|
+
*
|
|
247
|
+
* Deliberately NOT `recoverableSlots()`. That query excludes slots with a live
|
|
248
|
+
* lease — i.e. exactly the slots being executed right now — so using it as an
|
|
249
|
+
* "is anything still running?" probe reports idle in the middle of a run. The
|
|
250
|
+
* external-worker lifecycle needs to answer the opposite question, so it asks
|
|
251
|
+
* this one instead.
|
|
252
|
+
*/
|
|
253
|
+
countActiveSlots() {
|
|
254
|
+
const row = this.db
|
|
255
|
+
.prepare(`SELECT COUNT(*) n FROM schedule_slots WHERE status IN ('pending','running')`)
|
|
256
|
+
.get();
|
|
257
|
+
return row.n;
|
|
220
258
|
}
|
|
221
259
|
/** Record an error without changing terminality (retryable failure keeps the cell resumable). */
|
|
222
260
|
setCellError(slotId, targetId, error) {
|
|
@@ -21,7 +21,21 @@ export declare class TopicPipeline {
|
|
|
21
21
|
private readonly client;
|
|
22
22
|
private readonly resolver;
|
|
23
23
|
private readonly requestDelayMs;
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Run-scoped cancellation. Candidate acquisition is the longest stretch of
|
|
26
|
+
* network work in a run, so it must observe the same abort as the download
|
|
27
|
+
* pipeline; otherwise a cancelled run keeps searching tags and can never
|
|
28
|
+
* settle, which is what let a timed-out run hold its slot lease forever.
|
|
29
|
+
*/
|
|
30
|
+
private readonly signal?;
|
|
31
|
+
constructor(client: TopicClient, resolver: TopicResolver, requestDelayMs?: number,
|
|
32
|
+
/**
|
|
33
|
+
* Run-scoped cancellation. Candidate acquisition is the longest stretch of
|
|
34
|
+
* network work in a run, so it must observe the same abort as the download
|
|
35
|
+
* pipeline; otherwise a cancelled run keeps searching tags and can never
|
|
36
|
+
* settle, which is what let a timed-out run hold its slot lease forever.
|
|
37
|
+
*/
|
|
38
|
+
signal?: AbortSignal | undefined);
|
|
25
39
|
selectWorks<T extends WorkLike>(target: TargetConfig, contentType: TopicContentType, day: string, limit: number, discovery: TopicDiscoveryOptions, collect: TopicCollectOptions): Promise<{
|
|
26
40
|
works: T[];
|
|
27
41
|
selection: TopicSelection;
|
|
@@ -5,6 +5,7 @@ const promises_1 = require("node:timers/promises");
|
|
|
5
5
|
const logger_1 = require("../logger");
|
|
6
6
|
const pixiv_utils_1 = require("../utils/pixiv-utils");
|
|
7
7
|
const ai_detection_1 = require("../utils/ai-detection");
|
|
8
|
+
const errors_1 = require("../utils/errors");
|
|
8
9
|
const COLLECT_DEFAULTS = { maxPerTag: 40, maxCandidates: 250, minMetadataScore: 0.35 };
|
|
9
10
|
/** Small, platform-level tags that are never evidence of topic membership. */
|
|
10
11
|
const STOP_TAGS = new Set([
|
|
@@ -24,10 +25,19 @@ class TopicPipeline {
|
|
|
24
25
|
client;
|
|
25
26
|
resolver;
|
|
26
27
|
requestDelayMs;
|
|
27
|
-
|
|
28
|
+
signal;
|
|
29
|
+
constructor(client, resolver, requestDelayMs = 500,
|
|
30
|
+
/**
|
|
31
|
+
* Run-scoped cancellation. Candidate acquisition is the longest stretch of
|
|
32
|
+
* network work in a run, so it must observe the same abort as the download
|
|
33
|
+
* pipeline; otherwise a cancelled run keeps searching tags and can never
|
|
34
|
+
* settle, which is what let a timed-out run hold its slot lease forever.
|
|
35
|
+
*/
|
|
36
|
+
signal) {
|
|
28
37
|
this.client = client;
|
|
29
38
|
this.resolver = resolver;
|
|
30
39
|
this.requestDelayMs = requestDelayMs;
|
|
40
|
+
this.signal = signal;
|
|
31
41
|
}
|
|
32
42
|
async selectWorks(target, contentType, day, limit, discovery, collect) {
|
|
33
43
|
const topic = (target.topic ?? '').trim();
|
|
@@ -44,6 +54,9 @@ class TopicPipeline {
|
|
|
44
54
|
for (let i = 0; i < tagNames.length; i++) {
|
|
45
55
|
if (byId.size >= maxCandidates)
|
|
46
56
|
break;
|
|
57
|
+
// Cancellation is checked between tags, so a cancelled run stops issuing
|
|
58
|
+
// new searches even when the aborted request itself had already returned.
|
|
59
|
+
(0, errors_1.throwIfAborted)(this.signal, 'topic collection cancelled');
|
|
47
60
|
const tag = tagNames[i];
|
|
48
61
|
const works = await this.searchDay(contentType, tag, day, maxPerTag, includeR18);
|
|
49
62
|
rawCount += works.length;
|
|
@@ -99,7 +112,7 @@ class TopicPipeline {
|
|
|
99
112
|
}
|
|
100
113
|
async searchDay(contentType, tag, day, limit, includeR18) {
|
|
101
114
|
try {
|
|
102
|
-
const opts = { startDate: day, endDate: day, includeR18 };
|
|
115
|
+
const opts = { startDate: day, endDate: day, includeR18, signal: this.signal };
|
|
103
116
|
const works = contentType === 'illustration'
|
|
104
117
|
? await this.client.searchIllustrationsForTags(tag, limit, opts)
|
|
105
118
|
: await this.client.searchNovelsForTags(tag, limit, opts);
|
|
@@ -107,6 +120,9 @@ class TopicPipeline {
|
|
|
107
120
|
return works.filter((w) => this.onDay(w.create_date, day));
|
|
108
121
|
}
|
|
109
122
|
catch (error) {
|
|
123
|
+
// A cancellation must not be degraded into "no results for this tag": that
|
|
124
|
+
// would silently continue the cancelled run across the remaining tags.
|
|
125
|
+
(0, errors_1.rethrowIfCancelled)(error, this.signal);
|
|
110
126
|
logger_1.logger.warn('[TopicCollector] search failed tag=' + tag + ' type=' + contentType + ': ' + (error instanceof Error ? error.message : String(error)));
|
|
111
127
|
return [];
|
|
112
128
|
}
|
|
@@ -12,8 +12,12 @@ export declare class TopicResolver {
|
|
|
12
12
|
private readonly client;
|
|
13
13
|
private readonly cache;
|
|
14
14
|
private readonly requestDelayMs;
|
|
15
|
+
/** Run-scoped cancellation, forwarded to every discovery request. */
|
|
16
|
+
private readonly signal?;
|
|
15
17
|
private readonly scorer;
|
|
16
|
-
constructor(client: TopicClient, cache: TopicCache, requestDelayMs?: number
|
|
18
|
+
constructor(client: TopicClient, cache: TopicCache, requestDelayMs?: number,
|
|
19
|
+
/** Run-scoped cancellation, forwarded to every discovery request. */
|
|
20
|
+
signal?: AbortSignal | undefined);
|
|
17
21
|
/** Resolve tags for a topic+type, using cache when fresh. Never throws for
|
|
18
22
|
* discovery errors: it always returns at least the seed tag. */
|
|
19
23
|
resolve(topic: string, contentType: TopicContentType, options?: TopicDiscoveryOptions): Promise<{
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.TopicResolver = void 0;
|
|
4
4
|
const promises_1 = require("node:timers/promises");
|
|
5
5
|
const logger_1 = require("../logger");
|
|
6
|
+
const errors_1 = require("../utils/errors");
|
|
6
7
|
const TopicTagScorer_1 = require("./TopicTagScorer");
|
|
7
8
|
/** Platform-wide tags never worth searching as topic keys or scoring metadata. */
|
|
8
9
|
const STOP_TAGS = new Set([
|
|
@@ -29,11 +30,15 @@ class TopicResolver {
|
|
|
29
30
|
client;
|
|
30
31
|
cache;
|
|
31
32
|
requestDelayMs;
|
|
33
|
+
signal;
|
|
32
34
|
scorer = new TopicTagScorer_1.TopicTagScorer();
|
|
33
|
-
constructor(client, cache, requestDelayMs = 500
|
|
35
|
+
constructor(client, cache, requestDelayMs = 500,
|
|
36
|
+
/** Run-scoped cancellation, forwarded to every discovery request. */
|
|
37
|
+
signal) {
|
|
34
38
|
this.client = client;
|
|
35
39
|
this.cache = cache;
|
|
36
40
|
this.requestDelayMs = requestDelayMs;
|
|
41
|
+
this.signal = signal;
|
|
37
42
|
}
|
|
38
43
|
/** Resolve tags for a topic+type, using cache when fresh. Never throws for
|
|
39
44
|
* discovery errors: it always returns at least the seed tag. */
|
|
@@ -117,19 +122,40 @@ class TopicResolver {
|
|
|
117
122
|
}
|
|
118
123
|
/** Serial, bounded fetches: autocomplete → topic sample → small background. */
|
|
119
124
|
async gatherSamples(seed, contentType, sampleWorks, includeR18) {
|
|
120
|
-
|
|
125
|
+
(0, errors_1.throwIfAborted)(this.signal, 'topic resolution cancelled');
|
|
126
|
+
// Discovery degrades to "no suggestions" / "no background" on ordinary
|
|
127
|
+
// failures, but a cancellation must abort the run rather than quietly
|
|
128
|
+
// continue against a stopped pipeline.
|
|
129
|
+
const suggested = await this.client
|
|
130
|
+
.getTagAutocomplete(seed, { signal: this.signal })
|
|
131
|
+
.catch((error) => {
|
|
132
|
+
(0, errors_1.rethrowIfCancelled)(error, this.signal);
|
|
133
|
+
return [];
|
|
134
|
+
});
|
|
121
135
|
if (this.requestDelayMs > 0)
|
|
122
136
|
await (0, promises_1.setTimeout)(this.requestDelayMs);
|
|
137
|
+
(0, errors_1.throwIfAborted)(this.signal, 'topic resolution cancelled');
|
|
123
138
|
const topicWorks = contentType === 'illustration'
|
|
124
|
-
? await this.client.searchIllustrationsForTags(seed, sampleWorks, { includeR18 })
|
|
125
|
-
: await this.client.searchNovelsForTags(seed, sampleWorks, { includeR18 });
|
|
139
|
+
? await this.client.searchIllustrationsForTags(seed, sampleWorks, { includeR18, signal: this.signal })
|
|
140
|
+
: await this.client.searchNovelsForTags(seed, sampleWorks, { includeR18, signal: this.signal });
|
|
126
141
|
// A small, cheap background sample (a common platform tag) estimates how
|
|
127
142
|
// generic a co-occurring tag is. Bounded to keep requests/memory low.
|
|
128
143
|
if (this.requestDelayMs > 0)
|
|
129
144
|
await (0, promises_1.setTimeout)(this.requestDelayMs);
|
|
145
|
+
(0, errors_1.throwIfAborted)(this.signal, 'topic resolution cancelled');
|
|
130
146
|
const backgroundWorks = contentType === 'illustration'
|
|
131
|
-
? await this.client
|
|
132
|
-
|
|
147
|
+
? await this.client
|
|
148
|
+
.searchIllustrationsForTags('イラスト', 40, { includeR18, signal: this.signal })
|
|
149
|
+
.catch((error) => {
|
|
150
|
+
(0, errors_1.rethrowIfCancelled)(error, this.signal);
|
|
151
|
+
return [];
|
|
152
|
+
})
|
|
153
|
+
: await this.client
|
|
154
|
+
.searchNovelsForTags('小説', 40, { includeR18, signal: this.signal })
|
|
155
|
+
.catch((error) => {
|
|
156
|
+
(0, errors_1.rethrowIfCancelled)(error, this.signal);
|
|
157
|
+
return [];
|
|
158
|
+
});
|
|
133
159
|
return [suggested, topicWorks, backgroundWorks];
|
|
134
160
|
}
|
|
135
161
|
seedOnly(seed, contentType, cacheDays) {
|
|
@@ -9,8 +9,8 @@ export type TopicPipelineFactory = () => TopicPipeline;
|
|
|
9
9
|
type DbPathSource = string | {
|
|
10
10
|
getDatabasePath(): string;
|
|
11
11
|
} | undefined;
|
|
12
|
-
export declare function createTopicPipeline(client: IPixivClient, databasePath: DbPathSource, requestDelayMs?: number): TopicPipeline;
|
|
12
|
+
export declare function createTopicPipeline(client: IPixivClient, databasePath: DbPathSource, requestDelayMs?: number, signal?: AbortSignal): TopicPipeline;
|
|
13
13
|
/** Lazily builds one shared pipeline per download run (cache + resolver). */
|
|
14
|
-
export declare function createTopicPipelineFactory(client: IPixivClient, databasePath: DbPathSource, requestDelayMs?: number): TopicPipelineFactory;
|
|
14
|
+
export declare function createTopicPipelineFactory(client: IPixivClient, databasePath: DbPathSource, requestDelayMs?: number, signal?: AbortSignal): TopicPipelineFactory;
|
|
15
15
|
export {};
|
|
16
16
|
//# sourceMappingURL=createTopicPipeline.d.ts.map
|
|
@@ -15,18 +15,18 @@ function resolveDbPath(source) {
|
|
|
15
15
|
const absolute = raw && (0, node_path_1.isAbsolute)(raw) ? raw : (0, node_path_1.resolve)(process.cwd(), raw ?? 'data/pixiv-downloader.db');
|
|
16
16
|
return absolute;
|
|
17
17
|
}
|
|
18
|
-
function createTopicPipeline(client, databasePath, requestDelayMs = 500) {
|
|
18
|
+
function createTopicPipeline(client, databasePath, requestDelayMs = 500, signal) {
|
|
19
19
|
const resolved = resolveDbPath(databasePath);
|
|
20
20
|
const cache = new TopicCache_1.TopicCache((0, node_path_1.resolve)((0, node_path_1.dirname)(resolved), 'topic-cache'));
|
|
21
|
-
const resolver = new TopicResolver_1.TopicResolver(client, cache, requestDelayMs);
|
|
22
|
-
return new TopicPipeline_1.TopicPipeline(client, resolver, requestDelayMs);
|
|
21
|
+
const resolver = new TopicResolver_1.TopicResolver(client, cache, requestDelayMs, signal);
|
|
22
|
+
return new TopicPipeline_1.TopicPipeline(client, resolver, requestDelayMs, signal);
|
|
23
23
|
}
|
|
24
24
|
/** Lazily builds one shared pipeline per download run (cache + resolver). */
|
|
25
|
-
function createTopicPipelineFactory(client, databasePath, requestDelayMs = 500) {
|
|
25
|
+
function createTopicPipelineFactory(client, databasePath, requestDelayMs = 500, signal) {
|
|
26
26
|
let instance;
|
|
27
27
|
return () => {
|
|
28
28
|
if (!instance)
|
|
29
|
-
instance = createTopicPipeline(client, databasePath, requestDelayMs);
|
|
29
|
+
instance = createTopicPipeline(client, databasePath, requestDelayMs, signal);
|
|
30
30
|
return instance;
|
|
31
31
|
};
|
|
32
32
|
}
|
package/dist/topic/types.d.ts
CHANGED
|
@@ -67,7 +67,9 @@ export interface TopicCandidate {
|
|
|
67
67
|
}
|
|
68
68
|
/** Minimal surface the resolver/collector need from the Pixiv client. */
|
|
69
69
|
export interface TopicClient {
|
|
70
|
-
getTagAutocomplete(seed: string
|
|
70
|
+
getTagAutocomplete(seed: string, options?: {
|
|
71
|
+
signal?: AbortSignal;
|
|
72
|
+
}): Promise<Array<{
|
|
71
73
|
name: string;
|
|
72
74
|
translated_name?: string;
|
|
73
75
|
}>>;
|
|
@@ -75,11 +77,13 @@ export interface TopicClient {
|
|
|
75
77
|
startDate?: string;
|
|
76
78
|
endDate?: string;
|
|
77
79
|
includeR18?: boolean;
|
|
80
|
+
signal?: AbortSignal;
|
|
78
81
|
}): Promise<Array<WorkLike>>;
|
|
79
82
|
searchNovelsForTags(seed: string, limit: number, options?: {
|
|
80
83
|
startDate?: string;
|
|
81
84
|
endDate?: string;
|
|
82
85
|
includeR18?: boolean;
|
|
86
|
+
signal?: AbortSignal;
|
|
83
87
|
}): Promise<Array<WorkLike>>;
|
|
84
88
|
}
|
|
85
89
|
export interface WorkLike {
|