mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-27 18:22:41 -08:00
refactor(main): normalize runtime composer contracts
This commit is contained in:
@@ -0,0 +1,79 @@
|
|||||||
|
---
|
||||||
|
id: TASK-97
|
||||||
|
title: Normalize runtime composer contracts
|
||||||
|
status: Done
|
||||||
|
assignee:
|
||||||
|
- opencode
|
||||||
|
created_date: '2026-02-21 07:15'
|
||||||
|
updated_date: '2026-02-21 10:07'
|
||||||
|
labels:
|
||||||
|
- architecture
|
||||||
|
- type-safety
|
||||||
|
- maintainability
|
||||||
|
dependencies:
|
||||||
|
- TASK-94
|
||||||
|
- TASK-71
|
||||||
|
priority: high
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Runtime composer interfaces currently allow optional/null drift. Standardize contracts via shared context types and stricter per-composer dependency interfaces.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Action Steps
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Inventory all composer entrypoints in `src/main/runtime/composers/` and classify current optional/nullable fields.
|
||||||
|
2. Introduce shared `RuntimeContext` types in `src/main/runtime/` with explicit non-null guarantees where required.
|
||||||
|
3. Narrow each composer input type to only required fields; remove permissive `any`/optional drift.
|
||||||
|
4. Add compile-time contract tests (type assertions) and runtime seam tests for wiring regressions.
|
||||||
|
5. Update composition root adapters in `src/main.ts` and domain registries to satisfy strict contracts.
|
||||||
|
6. Run verification gate: `bun run build`, `bun run check:main-fanin`, `bun run test:core:dist`.
|
||||||
|
7. Document contract rules in architecture/development docs.
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 Shared runtime context/types exist and are consumed by all composers.
|
||||||
|
- [x] #2 Composer inputs reject missing required dependencies at compile time.
|
||||||
|
- [x] #3 No behavior regression in runtime wiring tests.
|
||||||
|
- [x] #4 Main fan-in guard remains within configured threshold.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Add shared composer contract helpers (`ComposerInputs`, `ComposerOutputs`, `BuiltMainDeps`) in `src/main/runtime/composers/contracts.ts` and consume them across all runtime composers.
|
||||||
|
2. Tighten high-risk composer boundaries (`mpv-runtime-composer.ts`, `jellyfin-remote-composer.ts`, `ipc-runtime-composer.ts`) and align `src/main.ts` callsites with normalized contract types.
|
||||||
|
3. Add compile-time contract checks in `src/main/runtime/composers/composer-contracts.type-test.ts` for required composer deps.
|
||||||
|
4. Update composer contract conventions in docs (`docs/architecture.md`, `docs/development.md`) and validate gates (`bun run build`, `bun run check:main-fanin`, `bun run test:core:dist`).
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
2026-02-21: started execution pass in current session; loaded task context and scanning composer contracts/tests before writing implementation plan.
|
||||||
|
|
||||||
|
Implemented shared runtime composer contract module and rewired all composer option/result types to use shared contract helpers.
|
||||||
|
|
||||||
|
Normalized MPV/Jellyfin/IPC composer boundaries, removed composer-level cast escape hatches where possible, and aligned `src/main.ts` runtime composer callsites to compile against stricter contracts.
|
||||||
|
|
||||||
|
Added compile-only type contract coverage in `src/main/runtime/composers/composer-contracts.type-test.ts` to assert required dependency keys fail when omitted.
|
||||||
|
|
||||||
|
Verification: `bun run build` PASS; focused composer tests PASS (`dist/main/runtime/composers/{mpv,jellyfin,ipc}-runtime-composer.test.js`); `bun run check:main-fanin` PASS (86 import lines, 10 runtime paths); `bun run test:core:dist` PASS (204 pass, 10 skipped).
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
Normalized runtime composer contracts by introducing shared contract helpers (`ComposerInputs`, `ComposerOutputs`, `BuiltMainDeps`) and applying them across all composer modules. Tightened MPV/Jellyfin/IPC composer interfaces and main callsites to enforce required dependency surfaces at compile time, added compile-only contract assertions, updated architecture/development conventions, and revalidated build/fan-in/core-runtime test gates with no behavior regressions.
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
|
|
||||||
|
## Definition of Done
|
||||||
|
<!-- DOD:BEGIN -->
|
||||||
|
- [x] #1 Type-level contract tests added for composer boundaries.
|
||||||
|
- [x] #2 `bun run build`, `bun run check:main-fanin`, and `bun run test:core:dist` pass.
|
||||||
|
- [x] #3 Docs updated with composer contract conventions.
|
||||||
|
<!-- DOD:END -->
|
||||||
@@ -170,6 +170,12 @@ The composition root (`src/main.ts`) delegates to focused modules in `src/main/`
|
|||||||
- `runtime/composers/anilist-tracking-composer.ts` — AniList media tracking/probe/retry wiring
|
- `runtime/composers/anilist-tracking-composer.ts` — AniList media tracking/probe/retry wiring
|
||||||
- `runtime/composers/mpv-runtime-composer.ts` — MPV event/factory/tokenizer/warmup wiring
|
- `runtime/composers/mpv-runtime-composer.ts` — MPV event/factory/tokenizer/warmup wiring
|
||||||
|
|
||||||
|
Composer modules share contract conventions via `src/main/runtime/composers/contracts.ts`:
|
||||||
|
|
||||||
|
- composer input surfaces are declared with `ComposerInputs<T>` so required dependencies cannot be omitted at compile time
|
||||||
|
- composer outputs are declared with `ComposerOutputs<T>` to keep result contracts explicit and stable
|
||||||
|
- builder return payload extraction should use shared type helpers instead of inline ad-hoc inference
|
||||||
|
|
||||||
This keeps side effects explicit and makes behavior easy to unit-test with fakes.
|
This keeps side effects explicit and makes behavior easy to unit-test with fakes.
|
||||||
|
|
||||||
## Program Lifecycle
|
## Program Lifecycle
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# Agent Session: opencode-task97-runtime-composer-20260221T094150Z-r8k3
|
||||||
|
|
||||||
|
- alias: `opencode-task97-runtime-composer`
|
||||||
|
- mission: `Execute TASK-97 normalize runtime composer contracts end-to-end without commit`
|
||||||
|
- status: `done`
|
||||||
|
- started_utc: `2026-02-21T09:42:20Z`
|
||||||
|
- backlog_task: `TASK-97`
|
||||||
|
|
||||||
|
## Intent
|
||||||
|
|
||||||
|
- Load TASK-97 context from Backlog MCP.
|
||||||
|
- Build execution plan via `writing-plans` skill.
|
||||||
|
- Execute with `executing-plans` skill.
|
||||||
|
- Use parallel subagents for independent slices.
|
||||||
|
|
||||||
|
## Planned Files
|
||||||
|
|
||||||
|
- `src/main/runtime/composers/*`
|
||||||
|
- `src/main/runtime/*`
|
||||||
|
- `src/main.ts`
|
||||||
|
- `src/**/*.test.ts`
|
||||||
|
- `docs/**/*.md`
|
||||||
|
|
||||||
|
## Assumptions
|
||||||
|
|
||||||
|
- Existing TASK-94/TASK-71 composer extraction is baseline.
|
||||||
|
- No commit requested.
|
||||||
|
|
||||||
|
## Heartbeat Log
|
||||||
|
|
||||||
|
- `2026-02-21T09:42:20Z` session start; context load + planning pending.
|
||||||
|
- `2026-02-21T10:06:59Z` implementation complete; TASK-97 marked Done in Backlog MCP.
|
||||||
|
|
||||||
|
## Files Touched
|
||||||
|
|
||||||
|
- `src/main/runtime/composers/contracts.ts`
|
||||||
|
- `src/main/runtime/composers/index.ts`
|
||||||
|
- `src/main/runtime/composers/anilist-setup-composer.ts`
|
||||||
|
- `src/main/runtime/composers/anilist-tracking-composer.ts`
|
||||||
|
- `src/main/runtime/composers/app-ready-composer.ts`
|
||||||
|
- `src/main/runtime/composers/ipc-runtime-composer.ts`
|
||||||
|
- `src/main/runtime/composers/jellyfin-remote-composer.ts`
|
||||||
|
- `src/main/runtime/composers/mpv-runtime-composer.ts`
|
||||||
|
- `src/main/runtime/composers/shortcuts-runtime-composer.ts`
|
||||||
|
- `src/main/runtime/composers/startup-lifecycle-composer.ts`
|
||||||
|
- `src/main/runtime/composers/ipc-runtime-composer.test.ts`
|
||||||
|
- `src/main/runtime/composers/mpv-runtime-composer.test.ts`
|
||||||
|
- `src/main/runtime/composers/composer-contracts.type-test.ts`
|
||||||
|
- `src/main/runtime/mpv-client-runtime-service.ts`
|
||||||
|
- `src/main.ts`
|
||||||
|
- `docs/architecture.md`
|
||||||
|
- `docs/development.md`
|
||||||
|
- `docs/plans/2026-02-21-task-97-normalize-runtime-composer-contracts.md`
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `bun run build` pass
|
||||||
|
- `bun run build && node --test dist/main/runtime/composers/mpv-runtime-composer.test.js dist/main/runtime/composers/jellyfin-remote-composer.test.js dist/main/runtime/composers/ipc-runtime-composer.test.js` pass
|
||||||
|
- `bun run check:main-fanin` pass (`86 import lines`, `10 unique runtime paths`)
|
||||||
|
- `bun run test:core:dist` pass (`204 pass`, `10 skipped`)
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- no commit performed (per task request)
|
||||||
|
- TASK-97 acceptance criteria + DoD checked and status set to Done in Backlog MCP
|
||||||
22
src/main.ts
22
src/main.ts
@@ -225,6 +225,7 @@ import { createBindMpvMainEventHandlersHandler } from './main/runtime/domains/mp
|
|||||||
import { createBuildBindMpvMainEventHandlersMainDepsHandler } from './main/runtime/domains/mpv';
|
import { createBuildBindMpvMainEventHandlersMainDepsHandler } from './main/runtime/domains/mpv';
|
||||||
import { createBuildMpvClientRuntimeServiceFactoryDepsHandler } from './main/runtime/domains/mpv';
|
import { createBuildMpvClientRuntimeServiceFactoryDepsHandler } from './main/runtime/domains/mpv';
|
||||||
import { createMpvClientRuntimeServiceFactory } from './main/runtime/domains/mpv';
|
import { createMpvClientRuntimeServiceFactory } from './main/runtime/domains/mpv';
|
||||||
|
import type { MpvClientRuntimeServiceOptions } from './main/runtime/domains/mpv';
|
||||||
import { createUpdateMpvSubtitleRenderMetricsHandler } from './main/runtime/domains/mpv';
|
import { createUpdateMpvSubtitleRenderMetricsHandler } from './main/runtime/domains/mpv';
|
||||||
import { createBuildUpdateMpvSubtitleRenderMetricsMainDepsHandler } from './main/runtime/domains/mpv';
|
import { createBuildUpdateMpvSubtitleRenderMetricsMainDepsHandler } from './main/runtime/domains/mpv';
|
||||||
import {
|
import {
|
||||||
@@ -2006,7 +2007,11 @@ const {
|
|||||||
createMecabTokenizerAndCheck,
|
createMecabTokenizerAndCheck,
|
||||||
prewarmSubtitleDictionaries,
|
prewarmSubtitleDictionaries,
|
||||||
startBackgroundWarmups,
|
startBackgroundWarmups,
|
||||||
} = composeMpvRuntimeHandlers<ReturnType<typeof createTokenizerDepsRuntime>, SubtitleData>({
|
} = composeMpvRuntimeHandlers<
|
||||||
|
MpvIpcClient,
|
||||||
|
ReturnType<typeof createTokenizerDepsRuntime>,
|
||||||
|
SubtitleData
|
||||||
|
>({
|
||||||
bindMpvMainEventHandlersMainDeps: {
|
bindMpvMainEventHandlersMainDeps: {
|
||||||
appState,
|
appState,
|
||||||
getQuitOnDisconnectArmed: () => jellyfinPlayQuitOnDisconnectArmed,
|
getQuitOnDisconnectArmed: () => jellyfinPlayQuitOnDisconnectArmed,
|
||||||
@@ -2055,7 +2060,10 @@ const {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
mpvClientRuntimeServiceFactoryMainDeps: {
|
mpvClientRuntimeServiceFactoryMainDeps: {
|
||||||
createClient: MpvIpcClient as unknown as new (socketPath: string, options: unknown) => unknown,
|
createClient: MpvIpcClient as unknown as new (
|
||||||
|
socketPath: string,
|
||||||
|
options: MpvClientRuntimeServiceOptions,
|
||||||
|
) => MpvIpcClient,
|
||||||
getSocketPath: () => appState.mpvSocketPath,
|
getSocketPath: () => appState.mpvSocketPath,
|
||||||
getResolvedConfig: () => getResolvedConfig(),
|
getResolvedConfig: () => getResolvedConfig(),
|
||||||
isAutoStartOverlayEnabled: () => appState.autoStartOverlay,
|
isAutoStartOverlayEnabled: () => appState.autoStartOverlay,
|
||||||
@@ -2500,10 +2508,7 @@ const {
|
|||||||
handleMpvCommandFromIpc: handleMpvCommandFromIpcHandler,
|
handleMpvCommandFromIpc: handleMpvCommandFromIpcHandler,
|
||||||
runSubsyncManualFromIpc: runSubsyncManualFromIpcHandler,
|
runSubsyncManualFromIpc: runSubsyncManualFromIpcHandler,
|
||||||
registerIpcRuntimeHandlers,
|
registerIpcRuntimeHandlers,
|
||||||
} = composeIpcRuntimeHandlers<
|
} = composeIpcRuntimeHandlers({
|
||||||
SubsyncManualRunRequest,
|
|
||||||
Awaited<ReturnType<typeof subsyncRuntime.runManualFromIpc>>
|
|
||||||
>({
|
|
||||||
mpvCommandMainDeps: {
|
mpvCommandMainDeps: {
|
||||||
triggerSubsyncFromConfig: () => triggerSubsyncFromConfig(),
|
triggerSubsyncFromConfig: () => triggerSubsyncFromConfig(),
|
||||||
openRuntimeOptionsPalette: () => openRuntimeOptionsPalette(),
|
openRuntimeOptionsPalette: () => openRuntimeOptionsPalette(),
|
||||||
@@ -2525,7 +2530,8 @@ const {
|
|||||||
hasRuntimeOptionsManager: () => appState.runtimeOptionsManager !== null,
|
hasRuntimeOptionsManager: () => appState.runtimeOptionsManager !== null,
|
||||||
},
|
},
|
||||||
handleMpvCommandFromIpcRuntime,
|
handleMpvCommandFromIpcRuntime,
|
||||||
runSubsyncManualFromIpc: (request) => subsyncRuntime.runManualFromIpc(request),
|
runSubsyncManualFromIpc: (request) =>
|
||||||
|
subsyncRuntime.runManualFromIpc(request as SubsyncManualRunRequest),
|
||||||
registration: {
|
registration: {
|
||||||
runtimeOptions: {
|
runtimeOptions: {
|
||||||
getRuntimeOptionsManager: () => appState.runtimeOptionsManager,
|
getRuntimeOptionsManager: () => appState.runtimeOptionsManager,
|
||||||
@@ -2867,7 +2873,7 @@ function handleMpvCommandFromIpc(command: (string | number)[]): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function runSubsyncManualFromIpc(request: SubsyncManualRunRequest): Promise<SubsyncResult> {
|
async function runSubsyncManualFromIpc(request: SubsyncManualRunRequest): Promise<SubsyncResult> {
|
||||||
return runSubsyncManualFromIpcHandler(request);
|
return runSubsyncManualFromIpcHandler(request) as Promise<SubsyncResult>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function appendClipboardVideoToQueue(): { ok: boolean; message: string } {
|
function appendClipboardVideoToQueue(): { ok: boolean; message: string } {
|
||||||
|
|||||||
@@ -8,27 +8,28 @@ import {
|
|||||||
createNotifyAnilistSetupHandler,
|
createNotifyAnilistSetupHandler,
|
||||||
createRegisterSubminerProtocolClientHandler,
|
createRegisterSubminerProtocolClientHandler,
|
||||||
} from '../domains/anilist';
|
} from '../domains/anilist';
|
||||||
|
import type { ComposerInputs, ComposerOutputs } from './contracts';
|
||||||
|
|
||||||
type NotifyHandler = ReturnType<typeof createNotifyAnilistSetupHandler>;
|
type NotifyHandler = ReturnType<typeof createNotifyAnilistSetupHandler>;
|
||||||
type ConsumeHandler = ReturnType<typeof createConsumeAnilistSetupTokenFromUrlHandler>;
|
type ConsumeHandler = ReturnType<typeof createConsumeAnilistSetupTokenFromUrlHandler>;
|
||||||
type HandleProtocolHandler = ReturnType<typeof createHandleAnilistSetupProtocolUrlHandler>;
|
type HandleProtocolHandler = ReturnType<typeof createHandleAnilistSetupProtocolUrlHandler>;
|
||||||
type RegisterClientHandler = ReturnType<typeof createRegisterSubminerProtocolClientHandler>;
|
type RegisterClientHandler = ReturnType<typeof createRegisterSubminerProtocolClientHandler>;
|
||||||
|
|
||||||
export type AnilistSetupComposerOptions = {
|
export type AnilistSetupComposerOptions = ComposerInputs<{
|
||||||
notifyDeps: Parameters<typeof createBuildNotifyAnilistSetupMainDepsHandler>[0];
|
notifyDeps: Parameters<typeof createBuildNotifyAnilistSetupMainDepsHandler>[0];
|
||||||
consumeTokenDeps: Parameters<typeof createBuildConsumeAnilistSetupTokenFromUrlMainDepsHandler>[0];
|
consumeTokenDeps: Parameters<typeof createBuildConsumeAnilistSetupTokenFromUrlMainDepsHandler>[0];
|
||||||
handleProtocolDeps: Parameters<typeof createBuildHandleAnilistSetupProtocolUrlMainDepsHandler>[0];
|
handleProtocolDeps: Parameters<typeof createBuildHandleAnilistSetupProtocolUrlMainDepsHandler>[0];
|
||||||
registerProtocolClientDeps: Parameters<
|
registerProtocolClientDeps: Parameters<
|
||||||
typeof createBuildRegisterSubminerProtocolClientMainDepsHandler
|
typeof createBuildRegisterSubminerProtocolClientMainDepsHandler
|
||||||
>[0];
|
>[0];
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export type AnilistSetupComposerResult = {
|
export type AnilistSetupComposerResult = ComposerOutputs<{
|
||||||
notifyAnilistSetup: NotifyHandler;
|
notifyAnilistSetup: NotifyHandler;
|
||||||
consumeAnilistSetupTokenFromUrl: ConsumeHandler;
|
consumeAnilistSetupTokenFromUrl: ConsumeHandler;
|
||||||
handleAnilistSetupProtocolUrl: HandleProtocolHandler;
|
handleAnilistSetupProtocolUrl: HandleProtocolHandler;
|
||||||
registerSubminerProtocolClient: RegisterClientHandler;
|
registerSubminerProtocolClient: RegisterClientHandler;
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export function composeAnilistSetupHandlers(
|
export function composeAnilistSetupHandlers(
|
||||||
options: AnilistSetupComposerOptions,
|
options: AnilistSetupComposerOptions,
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ import {
|
|||||||
createResetAnilistMediaTrackingHandler,
|
createResetAnilistMediaTrackingHandler,
|
||||||
createSetAnilistMediaGuessRuntimeStateHandler,
|
createSetAnilistMediaGuessRuntimeStateHandler,
|
||||||
} from '../domains/anilist';
|
} from '../domains/anilist';
|
||||||
|
import type { ComposerInputs, ComposerOutputs } from './contracts';
|
||||||
|
|
||||||
export type AnilistTrackingComposerOptions = {
|
export type AnilistTrackingComposerOptions = ComposerInputs<{
|
||||||
refreshClientSecretMainDeps: Parameters<
|
refreshClientSecretMainDeps: Parameters<
|
||||||
typeof createBuildRefreshAnilistClientSecretStateMainDepsHandler
|
typeof createBuildRefreshAnilistClientSecretStateMainDepsHandler
|
||||||
>[0];
|
>[0];
|
||||||
@@ -50,9 +51,9 @@ export type AnilistTrackingComposerOptions = {
|
|||||||
maybeRunPostWatchUpdateMainDeps: Parameters<
|
maybeRunPostWatchUpdateMainDeps: Parameters<
|
||||||
typeof createBuildMaybeRunAnilistPostWatchUpdateMainDepsHandler
|
typeof createBuildMaybeRunAnilistPostWatchUpdateMainDepsHandler
|
||||||
>[0];
|
>[0];
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export type AnilistTrackingComposerResult = {
|
export type AnilistTrackingComposerResult = ComposerOutputs<{
|
||||||
refreshAnilistClientSecretState: ReturnType<typeof createRefreshAnilistClientSecretStateHandler>;
|
refreshAnilistClientSecretState: ReturnType<typeof createRefreshAnilistClientSecretStateHandler>;
|
||||||
getCurrentAnilistMediaKey: ReturnType<typeof createGetCurrentAnilistMediaKeyHandler>;
|
getCurrentAnilistMediaKey: ReturnType<typeof createGetCurrentAnilistMediaKeyHandler>;
|
||||||
resetAnilistMediaTracking: ReturnType<typeof createResetAnilistMediaTrackingHandler>;
|
resetAnilistMediaTracking: ReturnType<typeof createResetAnilistMediaTrackingHandler>;
|
||||||
@@ -67,7 +68,7 @@ export type AnilistTrackingComposerResult = {
|
|||||||
ensureAnilistMediaGuess: ReturnType<typeof createEnsureAnilistMediaGuessHandler>;
|
ensureAnilistMediaGuess: ReturnType<typeof createEnsureAnilistMediaGuessHandler>;
|
||||||
processNextAnilistRetryUpdate: ReturnType<typeof createProcessNextAnilistRetryUpdateHandler>;
|
processNextAnilistRetryUpdate: ReturnType<typeof createProcessNextAnilistRetryUpdateHandler>;
|
||||||
maybeRunAnilistPostWatchUpdate: ReturnType<typeof createMaybeRunAnilistPostWatchUpdateHandler>;
|
maybeRunAnilistPostWatchUpdate: ReturnType<typeof createMaybeRunAnilistPostWatchUpdateHandler>;
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export function composeAnilistTrackingHandlers(
|
export function composeAnilistTrackingHandlers(
|
||||||
options: AnilistTrackingComposerOptions,
|
options: AnilistTrackingComposerOptions,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
import { createCriticalConfigErrorHandler, createReloadConfigHandler } from '../startup-config';
|
import { createCriticalConfigErrorHandler, createReloadConfigHandler } from '../startup-config';
|
||||||
import { createBuildImmersionTrackerStartupMainDepsHandler } from '../immersion-startup-main-deps';
|
import { createBuildImmersionTrackerStartupMainDepsHandler } from '../immersion-startup-main-deps';
|
||||||
import { createImmersionTrackerStartupHandler } from '../immersion-startup';
|
import { createImmersionTrackerStartupHandler } from '../immersion-startup';
|
||||||
|
import type { ComposerInputs, ComposerOutputs } from './contracts';
|
||||||
|
|
||||||
type ReloadConfigMainDeps = Parameters<typeof createBuildReloadConfigMainDepsHandler>[0];
|
type ReloadConfigMainDeps = Parameters<typeof createBuildReloadConfigMainDepsHandler>[0];
|
||||||
type CriticalConfigErrorMainDeps = Parameters<
|
type CriticalConfigErrorMainDeps = Parameters<
|
||||||
@@ -14,20 +15,20 @@ type CriticalConfigErrorMainDeps = Parameters<
|
|||||||
>[0];
|
>[0];
|
||||||
type AppReadyRuntimeMainDeps = Parameters<typeof createBuildAppReadyRuntimeMainDepsHandler>[0];
|
type AppReadyRuntimeMainDeps = Parameters<typeof createBuildAppReadyRuntimeMainDepsHandler>[0];
|
||||||
|
|
||||||
export type AppReadyComposerOptions = {
|
export type AppReadyComposerOptions = ComposerInputs<{
|
||||||
reloadConfigMainDeps: ReloadConfigMainDeps;
|
reloadConfigMainDeps: ReloadConfigMainDeps;
|
||||||
criticalConfigErrorMainDeps: CriticalConfigErrorMainDeps;
|
criticalConfigErrorMainDeps: CriticalConfigErrorMainDeps;
|
||||||
appReadyRuntimeMainDeps: Omit<AppReadyRuntimeMainDeps, 'reloadConfig' | 'onCriticalConfigErrors'>;
|
appReadyRuntimeMainDeps: Omit<AppReadyRuntimeMainDeps, 'reloadConfig' | 'onCriticalConfigErrors'>;
|
||||||
immersionTrackerStartupMainDeps: Parameters<
|
immersionTrackerStartupMainDeps: Parameters<
|
||||||
typeof createBuildImmersionTrackerStartupMainDepsHandler
|
typeof createBuildImmersionTrackerStartupMainDepsHandler
|
||||||
>[0];
|
>[0];
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export type AppReadyComposerResult = {
|
export type AppReadyComposerResult = ComposerOutputs<{
|
||||||
reloadConfig: ReturnType<typeof createReloadConfigHandler>;
|
reloadConfig: ReturnType<typeof createReloadConfigHandler>;
|
||||||
criticalConfigError: ReturnType<typeof createCriticalConfigErrorHandler>;
|
criticalConfigError: ReturnType<typeof createCriticalConfigErrorHandler>;
|
||||||
appReadyRuntimeRunner: ReturnType<typeof createAppReadyRuntimeRunner>;
|
appReadyRuntimeRunner: ReturnType<typeof createAppReadyRuntimeRunner>;
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export function composeAppReadyRuntime(options: AppReadyComposerOptions): AppReadyComposerResult {
|
export function composeAppReadyRuntime(options: AppReadyComposerOptions): AppReadyComposerResult {
|
||||||
const reloadConfig = createReloadConfigHandler(
|
const reloadConfig = createReloadConfigHandler(
|
||||||
|
|||||||
95
src/main/runtime/composers/composer-contracts.type-test.ts
Normal file
95
src/main/runtime/composers/composer-contracts.type-test.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import type { ComposerInputs } from './contracts';
|
||||||
|
import type { IpcRuntimeComposerOptions } from './ipc-runtime-composer';
|
||||||
|
import type { JellyfinRemoteComposerOptions } from './jellyfin-remote-composer';
|
||||||
|
import type { MpvRuntimeComposerOptions } from './mpv-runtime-composer';
|
||||||
|
import type { AnilistSetupComposerOptions } from './anilist-setup-composer';
|
||||||
|
|
||||||
|
type Assert<T extends true> = T;
|
||||||
|
type IsAssignable<From, To> = [From] extends [To] ? true : false;
|
||||||
|
|
||||||
|
type FakeMpvClient = {
|
||||||
|
on: (...args: unknown[]) => unknown;
|
||||||
|
connect: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type FakeTokenizerDeps = { isKnownWord: (text: string) => boolean };
|
||||||
|
type FakeTokenizedSubtitle = { text: string };
|
||||||
|
|
||||||
|
type RequiredAnilistSetupInputKeys = keyof ComposerInputs<AnilistSetupComposerOptions>;
|
||||||
|
type RequiredJellyfinInputKeys = keyof ComposerInputs<JellyfinRemoteComposerOptions>;
|
||||||
|
type RequiredIpcInputKeys = keyof ComposerInputs<IpcRuntimeComposerOptions>;
|
||||||
|
type RequiredMpvInputKeys = keyof ComposerInputs<
|
||||||
|
MpvRuntimeComposerOptions<FakeMpvClient, FakeTokenizerDeps, FakeTokenizedSubtitle>
|
||||||
|
>;
|
||||||
|
|
||||||
|
type _anilistHasNotifyDeps = Assert<IsAssignable<'notifyDeps', RequiredAnilistSetupInputKeys>>;
|
||||||
|
type _jellyfinHasGetMpvClient = Assert<IsAssignable<'getMpvClient', RequiredJellyfinInputKeys>>;
|
||||||
|
type _ipcHasRegistration = Assert<IsAssignable<'registration', RequiredIpcInputKeys>>;
|
||||||
|
type _mpvHasTokenizer = Assert<IsAssignable<'tokenizer', RequiredMpvInputKeys>>;
|
||||||
|
|
||||||
|
// @ts-expect-error missing required notifyDeps should fail compile-time contract
|
||||||
|
const anilistMissingRequired: AnilistSetupComposerOptions = {
|
||||||
|
consumeTokenDeps: {} as AnilistSetupComposerOptions['consumeTokenDeps'],
|
||||||
|
handleProtocolDeps: {} as AnilistSetupComposerOptions['handleProtocolDeps'],
|
||||||
|
registerProtocolClientDeps: {} as AnilistSetupComposerOptions['registerProtocolClientDeps'],
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error missing required getMpvClient should fail compile-time contract
|
||||||
|
const jellyfinMissingRequired: JellyfinRemoteComposerOptions = {
|
||||||
|
getConfiguredSession: {} as JellyfinRemoteComposerOptions['getConfiguredSession'],
|
||||||
|
getClientInfo: {} as JellyfinRemoteComposerOptions['getClientInfo'],
|
||||||
|
getJellyfinConfig: {} as JellyfinRemoteComposerOptions['getJellyfinConfig'],
|
||||||
|
playJellyfinItem: {} as JellyfinRemoteComposerOptions['playJellyfinItem'],
|
||||||
|
logWarn: {} as JellyfinRemoteComposerOptions['logWarn'],
|
||||||
|
sendMpvCommand: {} as JellyfinRemoteComposerOptions['sendMpvCommand'],
|
||||||
|
jellyfinTicksToSeconds: {} as JellyfinRemoteComposerOptions['jellyfinTicksToSeconds'],
|
||||||
|
getActivePlayback: {} as JellyfinRemoteComposerOptions['getActivePlayback'],
|
||||||
|
clearActivePlayback: {} as JellyfinRemoteComposerOptions['clearActivePlayback'],
|
||||||
|
getSession: {} as JellyfinRemoteComposerOptions['getSession'],
|
||||||
|
getNow: {} as JellyfinRemoteComposerOptions['getNow'],
|
||||||
|
getLastProgressAtMs: {} as JellyfinRemoteComposerOptions['getLastProgressAtMs'],
|
||||||
|
setLastProgressAtMs: {} as JellyfinRemoteComposerOptions['setLastProgressAtMs'],
|
||||||
|
progressIntervalMs: 3000,
|
||||||
|
ticksPerSecond: 10_000_000,
|
||||||
|
logDebug: {} as JellyfinRemoteComposerOptions['logDebug'],
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error missing required registration should fail compile-time contract
|
||||||
|
const ipcMissingRequired: IpcRuntimeComposerOptions = {
|
||||||
|
mpvCommandMainDeps: {} as IpcRuntimeComposerOptions['mpvCommandMainDeps'],
|
||||||
|
handleMpvCommandFromIpcRuntime: {} as IpcRuntimeComposerOptions['handleMpvCommandFromIpcRuntime'],
|
||||||
|
runSubsyncManualFromIpc: {} as IpcRuntimeComposerOptions['runSubsyncManualFromIpc'],
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error missing required tokenizer should fail compile-time contract
|
||||||
|
const mpvMissingRequired: MpvRuntimeComposerOptions<
|
||||||
|
FakeMpvClient,
|
||||||
|
FakeTokenizerDeps,
|
||||||
|
FakeTokenizedSubtitle
|
||||||
|
> = {
|
||||||
|
bindMpvMainEventHandlersMainDeps: {} as MpvRuntimeComposerOptions<
|
||||||
|
FakeMpvClient,
|
||||||
|
FakeTokenizerDeps,
|
||||||
|
FakeTokenizedSubtitle
|
||||||
|
>['bindMpvMainEventHandlersMainDeps'],
|
||||||
|
mpvClientRuntimeServiceFactoryMainDeps: {} as MpvRuntimeComposerOptions<
|
||||||
|
FakeMpvClient,
|
||||||
|
FakeTokenizerDeps,
|
||||||
|
FakeTokenizedSubtitle
|
||||||
|
>['mpvClientRuntimeServiceFactoryMainDeps'],
|
||||||
|
updateMpvSubtitleRenderMetricsMainDeps: {} as MpvRuntimeComposerOptions<
|
||||||
|
FakeMpvClient,
|
||||||
|
FakeTokenizerDeps,
|
||||||
|
FakeTokenizedSubtitle
|
||||||
|
>['updateMpvSubtitleRenderMetricsMainDeps'],
|
||||||
|
warmups: {} as MpvRuntimeComposerOptions<
|
||||||
|
FakeMpvClient,
|
||||||
|
FakeTokenizerDeps,
|
||||||
|
FakeTokenizedSubtitle
|
||||||
|
>['warmups'],
|
||||||
|
};
|
||||||
|
|
||||||
|
void anilistMissingRequired;
|
||||||
|
void jellyfinMissingRequired;
|
||||||
|
void ipcMissingRequired;
|
||||||
|
void mpvMissingRequired;
|
||||||
13
src/main/runtime/composers/contracts.ts
Normal file
13
src/main/runtime/composers/contracts.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
type ComposerShape = Record<string, unknown>;
|
||||||
|
|
||||||
|
export type ComposerInputs<T extends ComposerShape> = Readonly<Required<T>>;
|
||||||
|
|
||||||
|
export type ComposerOutputs<T extends ComposerShape> = Readonly<T>;
|
||||||
|
|
||||||
|
export type BuiltMainDeps<TFactory> = TFactory extends (
|
||||||
|
...args: infer _TFactoryArgs
|
||||||
|
) => infer TBuilder
|
||||||
|
? TBuilder extends (...args: infer _TBuilderArgs) => infer TDeps
|
||||||
|
? TDeps
|
||||||
|
: never
|
||||||
|
: never;
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
export * from './anilist-setup-composer';
|
export * from './anilist-setup-composer';
|
||||||
export * from './anilist-tracking-composer';
|
export * from './anilist-tracking-composer';
|
||||||
export * from './app-ready-composer';
|
export * from './app-ready-composer';
|
||||||
|
export * from './contracts';
|
||||||
export * from './ipc-runtime-composer';
|
export * from './ipc-runtime-composer';
|
||||||
export * from './jellyfin-remote-composer';
|
export * from './jellyfin-remote-composer';
|
||||||
export * from './mpv-runtime-composer';
|
export * from './mpv-runtime-composer';
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { composeIpcRuntimeHandlers } from './ipc-runtime-composer';
|
|||||||
test('composeIpcRuntimeHandlers returns callable IPC handlers and registration bridge', async () => {
|
test('composeIpcRuntimeHandlers returns callable IPC handlers and registration bridge', async () => {
|
||||||
let registered = false;
|
let registered = false;
|
||||||
|
|
||||||
const composed = composeIpcRuntimeHandlers<{ value: number }, { ok: boolean; received: number }>({
|
const composed = composeIpcRuntimeHandlers({
|
||||||
mpvCommandMainDeps: {
|
mpvCommandMainDeps: {
|
||||||
triggerSubsyncFromConfig: async () => {},
|
triggerSubsyncFromConfig: async () => {},
|
||||||
openRuntimeOptionsPalette: () => {},
|
openRuntimeOptionsPalette: () => {},
|
||||||
@@ -18,7 +18,10 @@ test('composeIpcRuntimeHandlers returns callable IPC handlers and registration b
|
|||||||
hasRuntimeOptionsManager: () => true,
|
hasRuntimeOptionsManager: () => true,
|
||||||
},
|
},
|
||||||
handleMpvCommandFromIpcRuntime: () => {},
|
handleMpvCommandFromIpcRuntime: () => {},
|
||||||
runSubsyncManualFromIpc: async (request) => ({ ok: true, received: request.value }),
|
runSubsyncManualFromIpc: async (request) => ({
|
||||||
|
ok: true,
|
||||||
|
received: (request as { value: number }).value,
|
||||||
|
}),
|
||||||
registration: {
|
registration: {
|
||||||
runtimeOptions: {
|
runtimeOptions: {
|
||||||
getRuntimeOptionsManager: () => null,
|
getRuntimeOptionsManager: () => null,
|
||||||
@@ -89,7 +92,10 @@ test('composeIpcRuntimeHandlers returns callable IPC handlers and registration b
|
|||||||
assert.equal(typeof composed.runSubsyncManualFromIpc, 'function');
|
assert.equal(typeof composed.runSubsyncManualFromIpc, 'function');
|
||||||
assert.equal(typeof composed.registerIpcRuntimeHandlers, 'function');
|
assert.equal(typeof composed.registerIpcRuntimeHandlers, 'function');
|
||||||
|
|
||||||
const result = await composed.runSubsyncManualFromIpc({ value: 7 });
|
const result = (await composed.runSubsyncManualFromIpc({ value: 7 })) as {
|
||||||
|
ok: boolean;
|
||||||
|
received: number;
|
||||||
|
};
|
||||||
assert.deepEqual(result, { ok: true, received: 7 });
|
assert.deepEqual(result, { ok: true, received: 7 });
|
||||||
|
|
||||||
composed.registerIpcRuntimeHandlers();
|
composed.registerIpcRuntimeHandlers();
|
||||||
|
|||||||
@@ -3,48 +3,43 @@ import {
|
|||||||
createBuildMpvCommandFromIpcRuntimeMainDepsHandler,
|
createBuildMpvCommandFromIpcRuntimeMainDepsHandler,
|
||||||
createIpcRuntimeHandlers,
|
createIpcRuntimeHandlers,
|
||||||
} from '../domains/ipc';
|
} from '../domains/ipc';
|
||||||
|
import type { ComposerInputs, ComposerOutputs } from './contracts';
|
||||||
|
|
||||||
type MpvCommand = (string | number)[];
|
type MpvCommand = (string | number)[];
|
||||||
|
|
||||||
type IpcMainDepsWithoutHandlers = Omit<
|
type IpcMainDeps = RegisterIpcRuntimeServicesParams['mainDeps'];
|
||||||
RegisterIpcRuntimeServicesParams['mainDeps'],
|
type IpcMainDepsWithoutHandlers = Omit<IpcMainDeps, 'handleMpvCommand' | 'runSubsyncManual'>;
|
||||||
'handleMpvCommand' | 'runSubsyncManual'
|
type RunSubsyncManual = IpcMainDeps['runSubsyncManual'];
|
||||||
>;
|
|
||||||
|
|
||||||
type IpcRuntimeDeps<TRequest, TResult> = Parameters<
|
type IpcRuntimeDeps = Parameters<typeof createIpcRuntimeHandlers<unknown, unknown>>[0];
|
||||||
typeof createIpcRuntimeHandlers<TRequest, TResult>
|
|
||||||
>[0];
|
|
||||||
|
|
||||||
export type IpcRuntimeComposerOptions<TRequest, TResult> = {
|
export type IpcRuntimeComposerOptions = ComposerInputs<{
|
||||||
mpvCommandMainDeps: Parameters<typeof createBuildMpvCommandFromIpcRuntimeMainDepsHandler>[0];
|
mpvCommandMainDeps: Parameters<typeof createBuildMpvCommandFromIpcRuntimeMainDepsHandler>[0];
|
||||||
handleMpvCommandFromIpcRuntime: IpcRuntimeDeps<
|
handleMpvCommandFromIpcRuntime: IpcRuntimeDeps['handleMpvCommandFromIpcDeps']['handleMpvCommandFromIpcRuntime'];
|
||||||
TRequest,
|
runSubsyncManualFromIpc: RunSubsyncManual;
|
||||||
TResult
|
|
||||||
>['handleMpvCommandFromIpcDeps']['handleMpvCommandFromIpcRuntime'];
|
|
||||||
runSubsyncManualFromIpc: (request: TRequest) => Promise<TResult>;
|
|
||||||
registration: {
|
registration: {
|
||||||
runtimeOptions: RegisterIpcRuntimeServicesParams['runtimeOptions'];
|
runtimeOptions: RegisterIpcRuntimeServicesParams['runtimeOptions'];
|
||||||
mainDeps: IpcMainDepsWithoutHandlers;
|
mainDeps: IpcMainDepsWithoutHandlers;
|
||||||
ankiJimakuDeps: RegisterIpcRuntimeServicesParams['ankiJimakuDeps'];
|
ankiJimakuDeps: RegisterIpcRuntimeServicesParams['ankiJimakuDeps'];
|
||||||
registerIpcRuntimeServices: (params: RegisterIpcRuntimeServicesParams) => void;
|
registerIpcRuntimeServices: (params: RegisterIpcRuntimeServicesParams) => void;
|
||||||
};
|
};
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export type IpcRuntimeComposerResult<TRequest, TResult> = {
|
export type IpcRuntimeComposerResult = ComposerOutputs<{
|
||||||
handleMpvCommandFromIpc: (command: MpvCommand) => void;
|
handleMpvCommandFromIpc: (command: MpvCommand) => void;
|
||||||
runSubsyncManualFromIpc: (request: TRequest) => Promise<TResult>;
|
runSubsyncManualFromIpc: RunSubsyncManual;
|
||||||
registerIpcRuntimeHandlers: () => void;
|
registerIpcRuntimeHandlers: () => void;
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export function composeIpcRuntimeHandlers<TRequest, TResult>(
|
export function composeIpcRuntimeHandlers(
|
||||||
options: IpcRuntimeComposerOptions<TRequest, TResult>,
|
options: IpcRuntimeComposerOptions,
|
||||||
): IpcRuntimeComposerResult<TRequest, TResult> {
|
): IpcRuntimeComposerResult {
|
||||||
const mpvCommandFromIpcRuntimeMainDeps = createBuildMpvCommandFromIpcRuntimeMainDepsHandler(
|
const mpvCommandFromIpcRuntimeMainDeps = createBuildMpvCommandFromIpcRuntimeMainDepsHandler(
|
||||||
options.mpvCommandMainDeps,
|
options.mpvCommandMainDeps,
|
||||||
)();
|
)();
|
||||||
const { handleMpvCommandFromIpc, runSubsyncManualFromIpc } = createIpcRuntimeHandlers<
|
const { handleMpvCommandFromIpc, runSubsyncManualFromIpc } = createIpcRuntimeHandlers<
|
||||||
TRequest,
|
unknown,
|
||||||
TResult
|
unknown
|
||||||
>({
|
>({
|
||||||
handleMpvCommandFromIpcDeps: {
|
handleMpvCommandFromIpcDeps: {
|
||||||
handleMpvCommandFromIpcRuntime: options.handleMpvCommandFromIpcRuntime,
|
handleMpvCommandFromIpcRuntime: options.handleMpvCommandFromIpcRuntime,
|
||||||
@@ -61,7 +56,7 @@ export function composeIpcRuntimeHandlers<TRequest, TResult>(
|
|||||||
mainDeps: {
|
mainDeps: {
|
||||||
...options.registration.mainDeps,
|
...options.registration.mainDeps,
|
||||||
handleMpvCommand: (command) => handleMpvCommandFromIpc(command),
|
handleMpvCommand: (command) => handleMpvCommandFromIpc(command),
|
||||||
runSubsyncManual: (request) => runSubsyncManualFromIpc(request as TRequest),
|
runSubsyncManual: (request: unknown) => runSubsyncManualFromIpc(request),
|
||||||
},
|
},
|
||||||
ankiJimakuDeps: options.registration.ankiJimakuDeps,
|
ankiJimakuDeps: options.registration.ankiJimakuDeps,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,26 +10,41 @@ import {
|
|||||||
createReportJellyfinRemoteProgressHandler,
|
createReportJellyfinRemoteProgressHandler,
|
||||||
createReportJellyfinRemoteStoppedHandler,
|
createReportJellyfinRemoteStoppedHandler,
|
||||||
} from '../domains/jellyfin';
|
} from '../domains/jellyfin';
|
||||||
|
import type { ComposerInputs, ComposerOutputs } from './contracts';
|
||||||
|
|
||||||
type RemotePlayPayload = Parameters<ReturnType<typeof createHandleJellyfinRemotePlay>>[0];
|
type RemotePlayPayload = Parameters<ReturnType<typeof createHandleJellyfinRemotePlay>>[0];
|
||||||
type RemotePlaystatePayload = Parameters<ReturnType<typeof createHandleJellyfinRemotePlaystate>>[0];
|
type RemotePlaystatePayload = Parameters<ReturnType<typeof createHandleJellyfinRemotePlaystate>>[0];
|
||||||
type RemoteGeneralPayload = Parameters<ReturnType<typeof createHandleJellyfinRemoteGeneralCommand>>[0];
|
type RemoteGeneralPayload = Parameters<
|
||||||
|
ReturnType<typeof createHandleJellyfinRemoteGeneralCommand>
|
||||||
|
>[0];
|
||||||
|
type JellyfinRemotePlayMainDeps = Parameters<
|
||||||
|
typeof createBuildHandleJellyfinRemotePlayMainDepsHandler
|
||||||
|
>[0];
|
||||||
|
type JellyfinRemotePlaystateMainDeps = Parameters<
|
||||||
|
typeof createBuildHandleJellyfinRemotePlaystateMainDepsHandler
|
||||||
|
>[0];
|
||||||
|
type JellyfinRemoteGeneralMainDeps = Parameters<
|
||||||
|
typeof createBuildHandleJellyfinRemoteGeneralCommandMainDepsHandler
|
||||||
|
>[0];
|
||||||
|
type JellyfinRemoteProgressMainDeps = Parameters<
|
||||||
|
typeof createBuildReportJellyfinRemoteProgressMainDepsHandler
|
||||||
|
>[0];
|
||||||
|
|
||||||
export type JellyfinRemoteComposerOptions = {
|
export type JellyfinRemoteComposerOptions = ComposerInputs<{
|
||||||
getConfiguredSession: Parameters<typeof createBuildHandleJellyfinRemotePlayMainDepsHandler>[0]['getConfiguredSession'];
|
getConfiguredSession: JellyfinRemotePlayMainDeps['getConfiguredSession'];
|
||||||
getClientInfo: Parameters<typeof createBuildHandleJellyfinRemotePlayMainDepsHandler>[0]['getClientInfo'];
|
getClientInfo: JellyfinRemotePlayMainDeps['getClientInfo'];
|
||||||
getJellyfinConfig: Parameters<typeof createBuildHandleJellyfinRemotePlayMainDepsHandler>[0]['getJellyfinConfig'];
|
getJellyfinConfig: JellyfinRemotePlayMainDeps['getJellyfinConfig'];
|
||||||
playJellyfinItem: Parameters<typeof createBuildHandleJellyfinRemotePlayMainDepsHandler>[0]['playJellyfinItem'];
|
playJellyfinItem: JellyfinRemotePlayMainDeps['playJellyfinItem'];
|
||||||
logWarn: Parameters<typeof createBuildHandleJellyfinRemotePlayMainDepsHandler>[0]['logWarn'];
|
logWarn: JellyfinRemotePlayMainDeps['logWarn'];
|
||||||
getMpvClient: Parameters<typeof createBuildReportJellyfinRemoteProgressMainDepsHandler>[0]['getMpvClient'];
|
getMpvClient: JellyfinRemoteProgressMainDeps['getMpvClient'];
|
||||||
sendMpvCommand: Parameters<typeof createBuildHandleJellyfinRemotePlaystateMainDepsHandler>[0]['sendMpvCommand'];
|
sendMpvCommand: JellyfinRemotePlaystateMainDeps['sendMpvCommand'];
|
||||||
jellyfinTicksToSeconds: Parameters<
|
jellyfinTicksToSeconds: Parameters<
|
||||||
typeof createBuildHandleJellyfinRemotePlaystateMainDepsHandler
|
typeof createBuildHandleJellyfinRemotePlaystateMainDepsHandler
|
||||||
>[0]['jellyfinTicksToSeconds'];
|
>[0]['jellyfinTicksToSeconds'];
|
||||||
getActivePlayback: Parameters<typeof createBuildHandleJellyfinRemoteGeneralCommandMainDepsHandler>[0]['getActivePlayback'];
|
getActivePlayback: JellyfinRemoteGeneralMainDeps['getActivePlayback'];
|
||||||
clearActivePlayback: Parameters<typeof createBuildReportJellyfinRemoteProgressMainDepsHandler>[0]['clearActivePlayback'];
|
clearActivePlayback: JellyfinRemoteProgressMainDeps['clearActivePlayback'];
|
||||||
getSession: Parameters<typeof createBuildReportJellyfinRemoteProgressMainDepsHandler>[0]['getSession'];
|
getSession: JellyfinRemoteProgressMainDeps['getSession'];
|
||||||
getNow: Parameters<typeof createBuildReportJellyfinRemoteProgressMainDepsHandler>[0]['getNow'];
|
getNow: JellyfinRemoteProgressMainDeps['getNow'];
|
||||||
getLastProgressAtMs: Parameters<
|
getLastProgressAtMs: Parameters<
|
||||||
typeof createBuildReportJellyfinRemoteProgressMainDepsHandler
|
typeof createBuildReportJellyfinRemoteProgressMainDepsHandler
|
||||||
>[0]['getLastProgressAtMs'];
|
>[0]['getLastProgressAtMs'];
|
||||||
@@ -38,16 +53,18 @@ export type JellyfinRemoteComposerOptions = {
|
|||||||
>[0]['setLastProgressAtMs'];
|
>[0]['setLastProgressAtMs'];
|
||||||
progressIntervalMs: number;
|
progressIntervalMs: number;
|
||||||
ticksPerSecond: number;
|
ticksPerSecond: number;
|
||||||
logDebug: Parameters<typeof createBuildReportJellyfinRemoteProgressMainDepsHandler>[0]['logDebug'];
|
logDebug: Parameters<
|
||||||
};
|
typeof createBuildReportJellyfinRemoteProgressMainDepsHandler
|
||||||
|
>[0]['logDebug'];
|
||||||
|
}>;
|
||||||
|
|
||||||
export type JellyfinRemoteComposerResult = {
|
export type JellyfinRemoteComposerResult = ComposerOutputs<{
|
||||||
reportJellyfinRemoteProgress: ReturnType<typeof createReportJellyfinRemoteProgressHandler>;
|
reportJellyfinRemoteProgress: ReturnType<typeof createReportJellyfinRemoteProgressHandler>;
|
||||||
reportJellyfinRemoteStopped: ReturnType<typeof createReportJellyfinRemoteStoppedHandler>;
|
reportJellyfinRemoteStopped: ReturnType<typeof createReportJellyfinRemoteStoppedHandler>;
|
||||||
handleJellyfinRemotePlay: (payload: RemotePlayPayload) => Promise<void>;
|
handleJellyfinRemotePlay: (payload: RemotePlayPayload) => Promise<void>;
|
||||||
handleJellyfinRemotePlaystate: (payload: RemotePlaystatePayload) => Promise<void>;
|
handleJellyfinRemotePlaystate: (payload: RemotePlaystatePayload) => Promise<void>;
|
||||||
handleJellyfinRemoteGeneralCommand: (payload: RemoteGeneralPayload) => Promise<void>;
|
handleJellyfinRemoteGeneralCommand: (payload: RemoteGeneralPayload) => Promise<void>;
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export function composeJellyfinRemoteHandlers(
|
export function composeJellyfinRemoteHandlers(
|
||||||
options: JellyfinRemoteComposerOptions,
|
options: JellyfinRemoteComposerOptions,
|
||||||
@@ -89,9 +106,7 @@ export function composeJellyfinRemoteHandlers(
|
|||||||
});
|
});
|
||||||
const buildHandleJellyfinRemotePlaystateMainDepsHandler =
|
const buildHandleJellyfinRemotePlaystateMainDepsHandler =
|
||||||
createBuildHandleJellyfinRemotePlaystateMainDepsHandler({
|
createBuildHandleJellyfinRemotePlaystateMainDepsHandler({
|
||||||
getMpvClient: options.getMpvClient as Parameters<
|
getMpvClient: options.getMpvClient,
|
||||||
typeof createBuildHandleJellyfinRemotePlaystateMainDepsHandler
|
|
||||||
>[0]['getMpvClient'],
|
|
||||||
sendMpvCommand: options.sendMpvCommand,
|
sendMpvCommand: options.sendMpvCommand,
|
||||||
reportJellyfinRemoteProgress: (force) => reportJellyfinRemoteProgress(force),
|
reportJellyfinRemoteProgress: (force) => reportJellyfinRemoteProgress(force),
|
||||||
reportJellyfinRemoteStopped: () => reportJellyfinRemoteStopped(),
|
reportJellyfinRemoteStopped: () => reportJellyfinRemoteStopped(),
|
||||||
@@ -99,9 +114,7 @@ export function composeJellyfinRemoteHandlers(
|
|||||||
});
|
});
|
||||||
const buildHandleJellyfinRemoteGeneralCommandMainDepsHandler =
|
const buildHandleJellyfinRemoteGeneralCommandMainDepsHandler =
|
||||||
createBuildHandleJellyfinRemoteGeneralCommandMainDepsHandler({
|
createBuildHandleJellyfinRemoteGeneralCommandMainDepsHandler({
|
||||||
getMpvClient: options.getMpvClient as Parameters<
|
getMpvClient: options.getMpvClient,
|
||||||
typeof createBuildHandleJellyfinRemoteGeneralCommandMainDepsHandler
|
|
||||||
>[0]['getMpvClient'],
|
|
||||||
sendMpvCommand: options.sendMpvCommand,
|
sendMpvCommand: options.sendMpvCommand,
|
||||||
getActivePlayback: options.getActivePlayback,
|
getActivePlayback: options.getActivePlayback,
|
||||||
reportJellyfinRemoteProgress: (force) => reportJellyfinRemoteProgress(force),
|
reportJellyfinRemoteProgress: (force) => reportJellyfinRemoteProgress(force),
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ test('composeMpvRuntimeHandlers returns callable handlers and forwards to inject
|
|||||||
}
|
}
|
||||||
|
|
||||||
const composed = composeMpvRuntimeHandlers<
|
const composed = composeMpvRuntimeHandlers<
|
||||||
|
FakeMpvClient,
|
||||||
{ isKnownWord: (text: string) => boolean },
|
{ isKnownWord: (text: string) => boolean },
|
||||||
{ text: string }
|
{ text: string }
|
||||||
>({
|
>({
|
||||||
@@ -189,7 +190,7 @@ test('composeMpvRuntimeHandlers returns callable handlers and forwards to inject
|
|||||||
assert.equal(typeof composed.launchBackgroundWarmupTask, 'function');
|
assert.equal(typeof composed.launchBackgroundWarmupTask, 'function');
|
||||||
assert.equal(typeof composed.startBackgroundWarmups, 'function');
|
assert.equal(typeof composed.startBackgroundWarmups, 'function');
|
||||||
|
|
||||||
const client = composed.createMpvClientRuntimeService() as FakeMpvClient;
|
const client = composed.createMpvClientRuntimeService();
|
||||||
assert.equal(client.connected, true);
|
assert.equal(client.connected, true);
|
||||||
|
|
||||||
composed.updateMpvSubtitleRenderMetrics({ subPos: 90 });
|
composed.updateMpvSubtitleRenderMetrics({ subPos: 90 });
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { createBindMpvMainEventHandlersHandler } from '../mpv-main-event-binding
|
|||||||
import { createBuildBindMpvMainEventHandlersMainDepsHandler } from '../mpv-main-event-main-deps';
|
import { createBuildBindMpvMainEventHandlersMainDepsHandler } from '../mpv-main-event-main-deps';
|
||||||
import { createBuildMpvClientRuntimeServiceFactoryDepsHandler } from '../mpv-client-runtime-service-main-deps';
|
import { createBuildMpvClientRuntimeServiceFactoryDepsHandler } from '../mpv-client-runtime-service-main-deps';
|
||||||
import { createMpvClientRuntimeServiceFactory } from '../mpv-client-runtime-service';
|
import { createMpvClientRuntimeServiceFactory } from '../mpv-client-runtime-service';
|
||||||
|
import type { MpvClientRuntimeServiceOptions } from '../mpv-client-runtime-service';
|
||||||
import { createBuildUpdateMpvSubtitleRenderMetricsMainDepsHandler } from '../mpv-subtitle-render-metrics-main-deps';
|
import { createBuildUpdateMpvSubtitleRenderMetricsMainDepsHandler } from '../mpv-subtitle-render-metrics-main-deps';
|
||||||
import { createUpdateMpvSubtitleRenderMetricsHandler } from '../mpv-subtitle-render-metrics';
|
import { createUpdateMpvSubtitleRenderMetricsHandler } from '../mpv-subtitle-render-metrics';
|
||||||
import {
|
import {
|
||||||
@@ -17,19 +18,29 @@ import {
|
|||||||
createLaunchBackgroundWarmupTaskHandler as createLaunchBackgroundWarmupTaskFromStartup,
|
createLaunchBackgroundWarmupTaskHandler as createLaunchBackgroundWarmupTaskFromStartup,
|
||||||
createStartBackgroundWarmupsHandler as createStartBackgroundWarmupsFromStartup,
|
createStartBackgroundWarmupsHandler as createStartBackgroundWarmupsFromStartup,
|
||||||
} from '../startup-warmups';
|
} from '../startup-warmups';
|
||||||
|
import type { BuiltMainDeps, ComposerInputs, ComposerOutputs } from './contracts';
|
||||||
|
|
||||||
type BindMpvMainEventHandlersMainDeps = Parameters<
|
type BindMpvMainEventHandlersMainDeps = Parameters<
|
||||||
typeof createBuildBindMpvMainEventHandlersMainDepsHandler
|
typeof createBuildBindMpvMainEventHandlersMainDepsHandler
|
||||||
>[0];
|
>[0];
|
||||||
type MpvClientRuntimeServiceFactoryMainDeps = Omit<
|
type BindMpvMainEventHandlers = ReturnType<typeof createBindMpvMainEventHandlersHandler>;
|
||||||
Parameters<typeof createBuildMpvClientRuntimeServiceFactoryDepsHandler>[0],
|
type BoundMpvClient = Parameters<BindMpvMainEventHandlers>[0];
|
||||||
|
type RuntimeMpvClient = BoundMpvClient & { connect: () => void };
|
||||||
|
type MpvClientRuntimeServiceFactoryMainDeps<TMpvClient extends RuntimeMpvClient> = Omit<
|
||||||
|
Parameters<
|
||||||
|
typeof createBuildMpvClientRuntimeServiceFactoryDepsHandler<
|
||||||
|
TMpvClient,
|
||||||
|
unknown,
|
||||||
|
MpvClientRuntimeServiceOptions
|
||||||
|
>
|
||||||
|
>[0],
|
||||||
'bindEventHandlers'
|
'bindEventHandlers'
|
||||||
>;
|
>;
|
||||||
type UpdateMpvSubtitleRenderMetricsMainDeps = Parameters<
|
type UpdateMpvSubtitleRenderMetricsMainDeps = Parameters<
|
||||||
typeof createBuildUpdateMpvSubtitleRenderMetricsMainDepsHandler
|
typeof createBuildUpdateMpvSubtitleRenderMetricsMainDepsHandler
|
||||||
>[0];
|
>[0];
|
||||||
type BuildTokenizerDepsMainDeps = Parameters<typeof createBuildTokenizerDepsMainHandler>[0];
|
type BuildTokenizerDepsMainDeps = Parameters<typeof createBuildTokenizerDepsMainHandler>[0];
|
||||||
type TokenizerMainDeps = ReturnType<ReturnType<typeof createBuildTokenizerDepsMainHandler>>;
|
type TokenizerMainDeps = BuiltMainDeps<typeof createBuildTokenizerDepsMainHandler>;
|
||||||
type CreateMecabTokenizerAndCheckMainDeps = Parameters<
|
type CreateMecabTokenizerAndCheckMainDeps = Parameters<
|
||||||
typeof createCreateMecabTokenizerAndCheckMainHandler
|
typeof createCreateMecabTokenizerAndCheckMainHandler
|
||||||
>[0];
|
>[0];
|
||||||
@@ -44,9 +55,13 @@ type StartBackgroundWarmupsMainDeps = Omit<
|
|||||||
'launchTask' | 'createMecabTokenizerAndCheck' | 'prewarmSubtitleDictionaries'
|
'launchTask' | 'createMecabTokenizerAndCheck' | 'prewarmSubtitleDictionaries'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export type MpvRuntimeComposerOptions<TTokenizerRuntimeDeps, TTokenizedSubtitle> = {
|
export type MpvRuntimeComposerOptions<
|
||||||
|
TMpvClient extends RuntimeMpvClient,
|
||||||
|
TTokenizerRuntimeDeps,
|
||||||
|
TTokenizedSubtitle,
|
||||||
|
> = ComposerInputs<{
|
||||||
bindMpvMainEventHandlersMainDeps: BindMpvMainEventHandlersMainDeps;
|
bindMpvMainEventHandlersMainDeps: BindMpvMainEventHandlersMainDeps;
|
||||||
mpvClientRuntimeServiceFactoryMainDeps: MpvClientRuntimeServiceFactoryMainDeps;
|
mpvClientRuntimeServiceFactoryMainDeps: MpvClientRuntimeServiceFactoryMainDeps<TMpvClient>;
|
||||||
updateMpvSubtitleRenderMetricsMainDeps: UpdateMpvSubtitleRenderMetricsMainDeps;
|
updateMpvSubtitleRenderMetricsMainDeps: UpdateMpvSubtitleRenderMetricsMainDeps;
|
||||||
tokenizer: {
|
tokenizer: {
|
||||||
buildTokenizerDepsMainDeps: BuildTokenizerDepsMainDeps;
|
buildTokenizerDepsMainDeps: BuildTokenizerDepsMainDeps;
|
||||||
@@ -59,22 +74,29 @@ export type MpvRuntimeComposerOptions<TTokenizerRuntimeDeps, TTokenizedSubtitle>
|
|||||||
launchBackgroundWarmupTaskMainDeps: LaunchBackgroundWarmupTaskMainDeps;
|
launchBackgroundWarmupTaskMainDeps: LaunchBackgroundWarmupTaskMainDeps;
|
||||||
startBackgroundWarmupsMainDeps: StartBackgroundWarmupsMainDeps;
|
startBackgroundWarmupsMainDeps: StartBackgroundWarmupsMainDeps;
|
||||||
};
|
};
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export type MpvRuntimeComposerResult<TTokenizedSubtitle> = {
|
export type MpvRuntimeComposerResult<
|
||||||
bindMpvClientEventHandlers: ReturnType<typeof createBindMpvMainEventHandlersHandler>;
|
TMpvClient extends RuntimeMpvClient,
|
||||||
createMpvClientRuntimeService: () => unknown;
|
TTokenizedSubtitle,
|
||||||
|
> = ComposerOutputs<{
|
||||||
|
bindMpvClientEventHandlers: BindMpvMainEventHandlers;
|
||||||
|
createMpvClientRuntimeService: () => TMpvClient;
|
||||||
updateMpvSubtitleRenderMetrics: ReturnType<typeof createUpdateMpvSubtitleRenderMetricsHandler>;
|
updateMpvSubtitleRenderMetrics: ReturnType<typeof createUpdateMpvSubtitleRenderMetricsHandler>;
|
||||||
tokenizeSubtitle: (text: string) => Promise<TTokenizedSubtitle>;
|
tokenizeSubtitle: (text: string) => Promise<TTokenizedSubtitle>;
|
||||||
createMecabTokenizerAndCheck: () => Promise<void>;
|
createMecabTokenizerAndCheck: () => Promise<void>;
|
||||||
prewarmSubtitleDictionaries: () => Promise<void>;
|
prewarmSubtitleDictionaries: () => Promise<void>;
|
||||||
launchBackgroundWarmupTask: ReturnType<typeof createLaunchBackgroundWarmupTaskFromStartup>;
|
launchBackgroundWarmupTask: ReturnType<typeof createLaunchBackgroundWarmupTaskFromStartup>;
|
||||||
startBackgroundWarmups: ReturnType<typeof createStartBackgroundWarmupsFromStartup>;
|
startBackgroundWarmups: ReturnType<typeof createStartBackgroundWarmupsFromStartup>;
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export function composeMpvRuntimeHandlers<TTokenizerRuntimeDeps, TTokenizedSubtitle>(
|
export function composeMpvRuntimeHandlers<
|
||||||
options: MpvRuntimeComposerOptions<TTokenizerRuntimeDeps, TTokenizedSubtitle>,
|
TMpvClient extends RuntimeMpvClient,
|
||||||
): MpvRuntimeComposerResult<TTokenizedSubtitle> {
|
TTokenizerRuntimeDeps,
|
||||||
|
TTokenizedSubtitle,
|
||||||
|
>(
|
||||||
|
options: MpvRuntimeComposerOptions<TMpvClient, TTokenizerRuntimeDeps, TTokenizedSubtitle>,
|
||||||
|
): MpvRuntimeComposerResult<TMpvClient, TTokenizedSubtitle> {
|
||||||
const bindMpvMainEventHandlersMainDeps = createBuildBindMpvMainEventHandlersMainDepsHandler(
|
const bindMpvMainEventHandlersMainDeps = createBuildBindMpvMainEventHandlersMainDepsHandler(
|
||||||
options.bindMpvMainEventHandlersMainDeps,
|
options.bindMpvMainEventHandlersMainDeps,
|
||||||
)();
|
)();
|
||||||
@@ -83,14 +105,16 @@ export function composeMpvRuntimeHandlers<TTokenizerRuntimeDeps, TTokenizedSubti
|
|||||||
);
|
);
|
||||||
|
|
||||||
const buildMpvClientRuntimeServiceFactoryMainDepsHandler =
|
const buildMpvClientRuntimeServiceFactoryMainDepsHandler =
|
||||||
createBuildMpvClientRuntimeServiceFactoryDepsHandler({
|
createBuildMpvClientRuntimeServiceFactoryDepsHandler<
|
||||||
|
TMpvClient,
|
||||||
|
unknown,
|
||||||
|
MpvClientRuntimeServiceOptions
|
||||||
|
>({
|
||||||
...options.mpvClientRuntimeServiceFactoryMainDeps,
|
...options.mpvClientRuntimeServiceFactoryMainDeps,
|
||||||
bindEventHandlers: (client) => bindMpvClientEventHandlers(client as never),
|
bindEventHandlers: (client) => bindMpvClientEventHandlers(client),
|
||||||
});
|
});
|
||||||
const createMpvClientRuntimeService = (): unknown =>
|
const createMpvClientRuntimeService = (): TMpvClient =>
|
||||||
createMpvClientRuntimeServiceFactory(
|
createMpvClientRuntimeServiceFactory(buildMpvClientRuntimeServiceFactoryMainDepsHandler())();
|
||||||
buildMpvClientRuntimeServiceFactoryMainDepsHandler() as never,
|
|
||||||
)();
|
|
||||||
|
|
||||||
const updateMpvSubtitleRenderMetrics = createUpdateMpvSubtitleRenderMetricsHandler(
|
const updateMpvSubtitleRenderMetrics = createUpdateMpvSubtitleRenderMetricsHandler(
|
||||||
createBuildUpdateMpvSubtitleRenderMetricsMainDepsHandler(
|
createBuildUpdateMpvSubtitleRenderMetricsMainDepsHandler(
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
createNumericShortcutSessionRuntimeHandlers,
|
createNumericShortcutSessionRuntimeHandlers,
|
||||||
createOverlayShortcutsRuntimeHandlers,
|
createOverlayShortcutsRuntimeHandlers,
|
||||||
} from '../domains/shortcuts';
|
} from '../domains/shortcuts';
|
||||||
|
import type { ComposerInputs, ComposerOutputs } from './contracts';
|
||||||
|
|
||||||
type GlobalShortcutsOptions = Parameters<typeof createGlobalShortcutsRuntimeHandlers>[0];
|
type GlobalShortcutsOptions = Parameters<typeof createGlobalShortcutsRuntimeHandlers>[0];
|
||||||
type NumericShortcutRuntimeMainDeps = Parameters<
|
type NumericShortcutRuntimeMainDeps = Parameters<
|
||||||
@@ -18,18 +19,18 @@ type OverlayShortcutsMainDeps = Parameters<
|
|||||||
typeof createOverlayShortcutsRuntimeHandlers
|
typeof createOverlayShortcutsRuntimeHandlers
|
||||||
>[0]['overlayShortcutsRuntimeMainDeps'];
|
>[0]['overlayShortcutsRuntimeMainDeps'];
|
||||||
|
|
||||||
export type ShortcutsRuntimeComposerOptions = {
|
export type ShortcutsRuntimeComposerOptions = ComposerInputs<{
|
||||||
globalShortcuts: GlobalShortcutsOptions;
|
globalShortcuts: GlobalShortcutsOptions;
|
||||||
numericShortcutRuntimeMainDeps: NumericShortcutRuntimeMainDeps;
|
numericShortcutRuntimeMainDeps: NumericShortcutRuntimeMainDeps;
|
||||||
numericSessions: NumericSessionOptions;
|
numericSessions: NumericSessionOptions;
|
||||||
overlayShortcutsRuntimeMainDeps: OverlayShortcutsMainDeps;
|
overlayShortcutsRuntimeMainDeps: OverlayShortcutsMainDeps;
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export type ShortcutsRuntimeComposerResult = ReturnType<
|
export type ShortcutsRuntimeComposerResult = ComposerOutputs<
|
||||||
typeof createGlobalShortcutsRuntimeHandlers
|
ReturnType<typeof createGlobalShortcutsRuntimeHandlers> &
|
||||||
> &
|
|
||||||
ReturnType<typeof createNumericShortcutSessionRuntimeHandlers> &
|
ReturnType<typeof createNumericShortcutSessionRuntimeHandlers> &
|
||||||
ReturnType<typeof createOverlayShortcutsRuntimeHandlers>;
|
ReturnType<typeof createOverlayShortcutsRuntimeHandlers>
|
||||||
|
>;
|
||||||
|
|
||||||
export function composeShortcutRuntimes(
|
export function composeShortcutRuntimes(
|
||||||
options: ShortcutsRuntimeComposerOptions,
|
options: ShortcutsRuntimeComposerOptions,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
} from '../app-lifecycle-main-activate';
|
} from '../app-lifecycle-main-activate';
|
||||||
import { createBuildRegisterProtocolUrlHandlersMainDepsHandler } from '../protocol-url-handlers-main-deps';
|
import { createBuildRegisterProtocolUrlHandlersMainDepsHandler } from '../protocol-url-handlers-main-deps';
|
||||||
import { registerProtocolUrlHandlers } from '../protocol-url-handlers';
|
import { registerProtocolUrlHandlers } from '../protocol-url-handlers';
|
||||||
|
import type { ComposerInputs, ComposerOutputs } from './contracts';
|
||||||
|
|
||||||
type RegisterProtocolUrlHandlersMainDeps = Parameters<
|
type RegisterProtocolUrlHandlersMainDeps = Parameters<
|
||||||
typeof createBuildRegisterProtocolUrlHandlersMainDepsHandler
|
typeof createBuildRegisterProtocolUrlHandlersMainDepsHandler
|
||||||
@@ -22,19 +23,19 @@ type RestoreWindowsOnActivateMainDeps = Parameters<
|
|||||||
typeof createBuildRestoreWindowsOnActivateMainDepsHandler
|
typeof createBuildRestoreWindowsOnActivateMainDepsHandler
|
||||||
>[0];
|
>[0];
|
||||||
|
|
||||||
export type StartupLifecycleComposerOptions = {
|
export type StartupLifecycleComposerOptions = ComposerInputs<{
|
||||||
registerProtocolUrlHandlersMainDeps: RegisterProtocolUrlHandlersMainDeps;
|
registerProtocolUrlHandlersMainDeps: RegisterProtocolUrlHandlersMainDeps;
|
||||||
onWillQuitCleanupMainDeps: OnWillQuitCleanupDeps;
|
onWillQuitCleanupMainDeps: OnWillQuitCleanupDeps;
|
||||||
shouldRestoreWindowsOnActivateMainDeps: ShouldRestoreWindowsOnActivateMainDeps;
|
shouldRestoreWindowsOnActivateMainDeps: ShouldRestoreWindowsOnActivateMainDeps;
|
||||||
restoreWindowsOnActivateMainDeps: RestoreWindowsOnActivateMainDeps;
|
restoreWindowsOnActivateMainDeps: RestoreWindowsOnActivateMainDeps;
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export type StartupLifecycleComposerResult = {
|
export type StartupLifecycleComposerResult = ComposerOutputs<{
|
||||||
registerProtocolUrlHandlers: () => void;
|
registerProtocolUrlHandlers: () => void;
|
||||||
onWillQuitCleanup: () => void;
|
onWillQuitCleanup: () => void;
|
||||||
shouldRestoreWindowsOnActivate: () => boolean;
|
shouldRestoreWindowsOnActivate: () => boolean;
|
||||||
restoreWindowsOnActivate: () => void;
|
restoreWindowsOnActivate: () => void;
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export function composeStartupLifecycleHandlers(
|
export function composeStartupLifecycleHandlers(
|
||||||
options: StartupLifecycleComposerOptions,
|
options: StartupLifecycleComposerOptions,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
type MpvClientCtorBaseOptions = {
|
export type MpvClientRuntimeServiceOptions = {
|
||||||
getResolvedConfig: () => unknown;
|
getResolvedConfig: () => unknown;
|
||||||
autoStartOverlay: boolean;
|
autoStartOverlay: boolean;
|
||||||
setOverlayVisible: (visible: boolean) => void;
|
setOverlayVisible: (visible: boolean) => void;
|
||||||
@@ -12,14 +12,14 @@ type MpvClientLike = {
|
|||||||
connect: () => void;
|
connect: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type MpvClientCtor<TClient extends MpvClientLike, TOptions extends MpvClientCtorBaseOptions> = new (
|
type MpvClientCtor<
|
||||||
socketPath: string,
|
TClient extends MpvClientLike,
|
||||||
options: TOptions,
|
TOptions extends MpvClientRuntimeServiceOptions,
|
||||||
) => TClient;
|
> = new (socketPath: string, options: TOptions) => TClient;
|
||||||
|
|
||||||
export function createMpvClientRuntimeServiceFactory<
|
export function createMpvClientRuntimeServiceFactory<
|
||||||
TClient extends MpvClientLike,
|
TClient extends MpvClientLike,
|
||||||
TOptions extends MpvClientCtorBaseOptions,
|
TOptions extends MpvClientRuntimeServiceOptions,
|
||||||
>(deps: {
|
>(deps: {
|
||||||
createClient: MpvClientCtor<TClient, TOptions>;
|
createClient: MpvClientCtor<TClient, TOptions>;
|
||||||
socketPath: string;
|
socketPath: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user