mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-28 06:22:45 -08:00
refactor: extract cli command deps runtime service
This commit is contained in:
@@ -16,7 +16,7 @@
|
|||||||
"docs:build": "vitepress build docs",
|
"docs:build": "vitepress build docs",
|
||||||
"docs:preview": "vitepress preview docs --host 0.0.0.0 --port 4173 --strictPort",
|
"docs:preview": "vitepress preview docs --host 0.0.0.0 --port 4173 --strictPort",
|
||||||
"test:config": "pnpm run build && node --test dist/config/config.test.js",
|
"test:config": "pnpm run build && node --test dist/config/config.test.js",
|
||||||
"test:core": "pnpm run build && node --test dist/cli/args.test.js dist/cli/help.test.js dist/core/services/cli-command-service.test.js dist/core/services/numeric-shortcut-session-service.test.js dist/core/services/secondary-subtitle-service.test.js dist/core/services/mpv-render-metrics-service.test.js dist/core/services/mpv-runtime-service.test.js dist/core/services/runtime-options-runtime-service.test.js dist/core/services/overlay-modal-restore-service.test.js dist/core/services/runtime-config-service.test.js dist/core/services/overlay-bridge-runtime-service.test.js dist/core/services/overlay-visibility-facade-service.test.js dist/core/services/overlay-broadcast-runtime-service.test.js dist/core/services/app-ready-runtime-service.test.js dist/core/services/app-shutdown-runtime-service.test.js dist/core/services/mpv-client-deps-runtime-service.test.js dist/core/services/app-lifecycle-deps-runtime-service.test.js dist/core/services/runtime-options-manager-runtime-service.test.js dist/core/services/config-warning-runtime-service.test.js dist/core/services/app-logging-runtime-service.test.js dist/core/services/startup-resource-runtime-service.test.js dist/core/services/config-generation-runtime-service.test.js",
|
"test:core": "pnpm run build && node --test dist/cli/args.test.js dist/cli/help.test.js dist/core/services/cli-command-service.test.js dist/core/services/cli-command-deps-runtime-service.test.js dist/core/services/numeric-shortcut-session-service.test.js dist/core/services/secondary-subtitle-service.test.js dist/core/services/mpv-render-metrics-service.test.js dist/core/services/mpv-runtime-service.test.js dist/core/services/runtime-options-runtime-service.test.js dist/core/services/overlay-modal-restore-service.test.js dist/core/services/runtime-config-service.test.js dist/core/services/overlay-bridge-runtime-service.test.js dist/core/services/overlay-visibility-facade-service.test.js dist/core/services/overlay-broadcast-runtime-service.test.js dist/core/services/app-ready-runtime-service.test.js dist/core/services/app-shutdown-runtime-service.test.js dist/core/services/mpv-client-deps-runtime-service.test.js dist/core/services/app-lifecycle-deps-runtime-service.test.js dist/core/services/runtime-options-manager-runtime-service.test.js dist/core/services/config-warning-runtime-service.test.js dist/core/services/app-logging-runtime-service.test.js dist/core/services/startup-resource-runtime-service.test.js dist/core/services/config-generation-runtime-service.test.js",
|
||||||
"test:subtitle": "pnpm run build && node --test dist/subtitle/stages.test.js dist/subtitle/pipeline.test.js",
|
"test:subtitle": "pnpm run build && node --test dist/subtitle/stages.test.js dist/subtitle/pipeline.test.js",
|
||||||
"generate:config-example": "pnpm run build && node dist/generate-config-example.js",
|
"generate:config-example": "pnpm run build && node dist/generate-config-example.js",
|
||||||
"start": "pnpm run build && electron . --start",
|
"start": "pnpm run build && electron . --start",
|
||||||
|
|||||||
110
src/core/services/cli-command-deps-runtime-service.test.ts
Normal file
110
src/core/services/cli-command-deps-runtime-service.test.ts
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import test from "node:test";
|
||||||
|
import assert from "node:assert/strict";
|
||||||
|
import { createCliCommandDepsRuntimeService } from "./cli-command-deps-runtime-service";
|
||||||
|
|
||||||
|
test("createCliCommandDepsRuntimeService wires runtime helpers", () => {
|
||||||
|
let socketPath = "/tmp/mpv";
|
||||||
|
let setClientSocketPath: string | null = null;
|
||||||
|
let connectCalls = 0;
|
||||||
|
let texthookerPort = 7000;
|
||||||
|
let texthookerRunning = false;
|
||||||
|
let texthookerStartPort: number | null = null;
|
||||||
|
let overlayVisible = false;
|
||||||
|
let overlayInvisible = false;
|
||||||
|
let openYomitanAfterDelay: number | null = null;
|
||||||
|
|
||||||
|
const deps = createCliCommandDepsRuntimeService({
|
||||||
|
mpv: {
|
||||||
|
getSocketPath: () => socketPath,
|
||||||
|
setSocketPath: (next) => {
|
||||||
|
socketPath = next;
|
||||||
|
},
|
||||||
|
getClient: () => ({
|
||||||
|
setSocketPath: (next) => {
|
||||||
|
setClientSocketPath = next;
|
||||||
|
},
|
||||||
|
connect: () => {
|
||||||
|
connectCalls += 1;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
showOsd: () => {},
|
||||||
|
},
|
||||||
|
texthooker: {
|
||||||
|
service: {
|
||||||
|
isRunning: () => texthookerRunning,
|
||||||
|
start: (port) => {
|
||||||
|
texthookerRunning = true;
|
||||||
|
texthookerStartPort = port;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
getPort: () => texthookerPort,
|
||||||
|
setPort: (port) => {
|
||||||
|
texthookerPort = port;
|
||||||
|
},
|
||||||
|
shouldOpenBrowser: () => true,
|
||||||
|
openInBrowser: () => {},
|
||||||
|
},
|
||||||
|
overlay: {
|
||||||
|
isInitialized: () => false,
|
||||||
|
initialize: () => {},
|
||||||
|
toggleVisible: () => {
|
||||||
|
overlayVisible = !overlayVisible;
|
||||||
|
},
|
||||||
|
toggleInvisible: () => {
|
||||||
|
overlayInvisible = !overlayInvisible;
|
||||||
|
},
|
||||||
|
setVisible: (visible) => {
|
||||||
|
overlayVisible = visible;
|
||||||
|
},
|
||||||
|
setInvisible: (visible) => {
|
||||||
|
overlayInvisible = visible;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mining: {
|
||||||
|
copyCurrentSubtitle: () => {},
|
||||||
|
startPendingMultiCopy: () => {},
|
||||||
|
mineSentenceCard: async () => {},
|
||||||
|
startPendingMineSentenceMultiple: () => {},
|
||||||
|
updateLastCardFromClipboard: async () => {},
|
||||||
|
triggerFieldGrouping: async () => {},
|
||||||
|
triggerSubsyncFromConfig: async () => {},
|
||||||
|
markLastCardAsAudioCard: async () => {},
|
||||||
|
},
|
||||||
|
ui: {
|
||||||
|
openYomitanSettings: () => {},
|
||||||
|
cycleSecondarySubMode: () => {},
|
||||||
|
openRuntimeOptionsPalette: () => {},
|
||||||
|
printHelp: () => {},
|
||||||
|
},
|
||||||
|
app: {
|
||||||
|
stop: () => {},
|
||||||
|
hasMainWindow: () => true,
|
||||||
|
},
|
||||||
|
getMultiCopyTimeoutMs: () => 2500,
|
||||||
|
schedule: (_fn, delayMs) => {
|
||||||
|
openYomitanAfterDelay = delayMs;
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
log: () => {},
|
||||||
|
warn: () => {},
|
||||||
|
error: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
deps.setMpvSocketPath("/tmp/new");
|
||||||
|
deps.setMpvClientSocketPath("/tmp/new");
|
||||||
|
deps.connectMpvClient();
|
||||||
|
deps.ensureTexthookerRunning(9000);
|
||||||
|
deps.openYomitanSettingsDelayed(1000);
|
||||||
|
deps.toggleVisibleOverlay();
|
||||||
|
deps.toggleInvisibleOverlay();
|
||||||
|
|
||||||
|
assert.equal(deps.getMpvSocketPath(), "/tmp/new");
|
||||||
|
assert.equal(setClientSocketPath, "/tmp/new");
|
||||||
|
assert.equal(connectCalls, 1);
|
||||||
|
assert.equal(texthookerStartPort, 9000);
|
||||||
|
assert.equal(texthookerPort, 7000);
|
||||||
|
assert.equal(openYomitanAfterDelay, 1000);
|
||||||
|
assert.equal(overlayVisible, true);
|
||||||
|
assert.equal(overlayInvisible, true);
|
||||||
|
assert.equal(deps.getMultiCopyTimeoutMs(), 2500);
|
||||||
|
});
|
||||||
132
src/core/services/cli-command-deps-runtime-service.ts
Normal file
132
src/core/services/cli-command-deps-runtime-service.ts
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import { CliCommandServiceDeps } from "./cli-command-service";
|
||||||
|
|
||||||
|
interface MpvClientLike {
|
||||||
|
setSocketPath: (socketPath: string) => void;
|
||||||
|
connect: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TexthookerServiceLike {
|
||||||
|
isRunning: () => boolean;
|
||||||
|
start: (port: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MpvCliRuntime {
|
||||||
|
getSocketPath: () => string;
|
||||||
|
setSocketPath: (socketPath: string) => void;
|
||||||
|
getClient: () => MpvClientLike | null;
|
||||||
|
showOsd: (text: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TexthookerCliRuntime {
|
||||||
|
service: TexthookerServiceLike;
|
||||||
|
getPort: () => number;
|
||||||
|
setPort: (port: number) => void;
|
||||||
|
shouldOpenBrowser: () => boolean;
|
||||||
|
openInBrowser: (url: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OverlayCliRuntime {
|
||||||
|
isInitialized: () => boolean;
|
||||||
|
initialize: () => void;
|
||||||
|
toggleVisible: () => void;
|
||||||
|
toggleInvisible: () => void;
|
||||||
|
setVisible: (visible: boolean) => void;
|
||||||
|
setInvisible: (visible: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MiningCliRuntime {
|
||||||
|
copyCurrentSubtitle: () => void;
|
||||||
|
startPendingMultiCopy: (timeoutMs: number) => void;
|
||||||
|
mineSentenceCard: () => Promise<void>;
|
||||||
|
startPendingMineSentenceMultiple: (timeoutMs: number) => void;
|
||||||
|
updateLastCardFromClipboard: () => Promise<void>;
|
||||||
|
triggerFieldGrouping: () => Promise<void>;
|
||||||
|
triggerSubsyncFromConfig: () => Promise<void>;
|
||||||
|
markLastCardAsAudioCard: () => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UiCliRuntime {
|
||||||
|
openYomitanSettings: () => void;
|
||||||
|
cycleSecondarySubMode: () => void;
|
||||||
|
openRuntimeOptionsPalette: () => void;
|
||||||
|
printHelp: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AppCliRuntime {
|
||||||
|
stop: () => void;
|
||||||
|
hasMainWindow: () => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CliCommandDepsRuntimeOptions {
|
||||||
|
mpv: MpvCliRuntime;
|
||||||
|
texthooker: TexthookerCliRuntime;
|
||||||
|
overlay: OverlayCliRuntime;
|
||||||
|
mining: MiningCliRuntime;
|
||||||
|
ui: UiCliRuntime;
|
||||||
|
app: AppCliRuntime;
|
||||||
|
getMultiCopyTimeoutMs: () => number;
|
||||||
|
schedule: (fn: () => void, delayMs: number) => unknown;
|
||||||
|
log: (message: string) => void;
|
||||||
|
warn: (message: string) => void;
|
||||||
|
error: (message: string, err: unknown) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCliCommandDepsRuntimeService(
|
||||||
|
options: CliCommandDepsRuntimeOptions,
|
||||||
|
): CliCommandServiceDeps {
|
||||||
|
return {
|
||||||
|
getMpvSocketPath: options.mpv.getSocketPath,
|
||||||
|
setMpvSocketPath: options.mpv.setSocketPath,
|
||||||
|
setMpvClientSocketPath: (socketPath) => {
|
||||||
|
const client = options.mpv.getClient();
|
||||||
|
if (!client) return;
|
||||||
|
client.setSocketPath(socketPath);
|
||||||
|
},
|
||||||
|
hasMpvClient: () => Boolean(options.mpv.getClient()),
|
||||||
|
connectMpvClient: () => {
|
||||||
|
const client = options.mpv.getClient();
|
||||||
|
if (!client) return;
|
||||||
|
client.connect();
|
||||||
|
},
|
||||||
|
isTexthookerRunning: () => options.texthooker.service.isRunning(),
|
||||||
|
setTexthookerPort: options.texthooker.setPort,
|
||||||
|
getTexthookerPort: options.texthooker.getPort,
|
||||||
|
shouldOpenTexthookerBrowser: options.texthooker.shouldOpenBrowser,
|
||||||
|
ensureTexthookerRunning: (port) => {
|
||||||
|
if (!options.texthooker.service.isRunning()) {
|
||||||
|
options.texthooker.service.start(port);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openTexthookerInBrowser: options.texthooker.openInBrowser,
|
||||||
|
stopApp: options.app.stop,
|
||||||
|
isOverlayRuntimeInitialized: options.overlay.isInitialized,
|
||||||
|
initializeOverlayRuntime: options.overlay.initialize,
|
||||||
|
toggleVisibleOverlay: options.overlay.toggleVisible,
|
||||||
|
toggleInvisibleOverlay: options.overlay.toggleInvisible,
|
||||||
|
openYomitanSettingsDelayed: (delayMs) => {
|
||||||
|
options.schedule(() => {
|
||||||
|
options.ui.openYomitanSettings();
|
||||||
|
}, delayMs);
|
||||||
|
},
|
||||||
|
setVisibleOverlayVisible: options.overlay.setVisible,
|
||||||
|
setInvisibleOverlayVisible: options.overlay.setInvisible,
|
||||||
|
copyCurrentSubtitle: options.mining.copyCurrentSubtitle,
|
||||||
|
startPendingMultiCopy: options.mining.startPendingMultiCopy,
|
||||||
|
mineSentenceCard: options.mining.mineSentenceCard,
|
||||||
|
startPendingMineSentenceMultiple:
|
||||||
|
options.mining.startPendingMineSentenceMultiple,
|
||||||
|
updateLastCardFromClipboard: options.mining.updateLastCardFromClipboard,
|
||||||
|
cycleSecondarySubMode: options.ui.cycleSecondarySubMode,
|
||||||
|
triggerFieldGrouping: options.mining.triggerFieldGrouping,
|
||||||
|
triggerSubsyncFromConfig: options.mining.triggerSubsyncFromConfig,
|
||||||
|
markLastCardAsAudioCard: options.mining.markLastCardAsAudioCard,
|
||||||
|
openRuntimeOptionsPalette: options.ui.openRuntimeOptionsPalette,
|
||||||
|
printHelp: options.ui.printHelp,
|
||||||
|
hasMainWindow: options.app.hasMainWindow,
|
||||||
|
getMultiCopyTimeoutMs: options.getMultiCopyTimeoutMs,
|
||||||
|
showMpvOsd: options.mpv.showOsd,
|
||||||
|
log: options.log,
|
||||||
|
warn: options.warn,
|
||||||
|
error: options.error,
|
||||||
|
};
|
||||||
|
}
|
||||||
96
src/main.ts
96
src/main.ts
@@ -205,6 +205,7 @@ import { runAppReadyRuntimeService } from "./core/services/app-ready-runtime-ser
|
|||||||
import { runAppShutdownRuntimeService } from "./core/services/app-shutdown-runtime-service";
|
import { runAppShutdownRuntimeService } from "./core/services/app-shutdown-runtime-service";
|
||||||
import { createMpvIpcClientDepsRuntimeService } from "./core/services/mpv-client-deps-runtime-service";
|
import { createMpvIpcClientDepsRuntimeService } from "./core/services/mpv-client-deps-runtime-service";
|
||||||
import { createAppLifecycleDepsRuntimeService } from "./core/services/app-lifecycle-deps-runtime-service";
|
import { createAppLifecycleDepsRuntimeService } from "./core/services/app-lifecycle-deps-runtime-service";
|
||||||
|
import { createCliCommandDepsRuntimeService } from "./core/services/cli-command-deps-runtime-service";
|
||||||
import { createRuntimeOptionsManagerRuntimeService } from "./core/services/runtime-options-manager-runtime-service";
|
import { createRuntimeOptionsManagerRuntimeService } from "./core/services/runtime-options-manager-runtime-service";
|
||||||
import { createAppLoggingRuntimeService } from "./core/services/app-logging-runtime-service";
|
import { createAppLoggingRuntimeService } from "./core/services/app-logging-runtime-service";
|
||||||
import {
|
import {
|
||||||
@@ -653,63 +654,57 @@ function handleCliCommand(
|
|||||||
args: CliArgs,
|
args: CliArgs,
|
||||||
source: CliCommandSource = "initial",
|
source: CliCommandSource = "initial",
|
||||||
): void {
|
): void {
|
||||||
handleCliCommandService(args, source, {
|
const deps = createCliCommandDepsRuntimeService({
|
||||||
getMpvSocketPath: () => mpvSocketPath,
|
mpv: {
|
||||||
setMpvSocketPath: (socketPath) => {
|
getSocketPath: () => mpvSocketPath,
|
||||||
mpvSocketPath = socketPath;
|
setSocketPath: (socketPath) => {
|
||||||
|
mpvSocketPath = socketPath;
|
||||||
|
},
|
||||||
|
getClient: () => mpvClient,
|
||||||
|
showOsd: (text) => showMpvOsd(text),
|
||||||
},
|
},
|
||||||
setMpvClientSocketPath: (socketPath) => {
|
texthooker: {
|
||||||
if (!mpvClient) return;
|
service: texthookerService,
|
||||||
mpvClient.setSocketPath(socketPath);
|
getPort: () => texthookerPort,
|
||||||
|
setPort: (port) => {
|
||||||
|
texthookerPort = port;
|
||||||
|
},
|
||||||
|
shouldOpenBrowser: () => getResolvedConfig().texthooker?.openBrowser !== false,
|
||||||
|
openInBrowser: (url) => {
|
||||||
|
shell.openExternal(url);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
hasMpvClient: () => Boolean(mpvClient),
|
overlay: {
|
||||||
connectMpvClient: () => {
|
isInitialized: () => overlayRuntimeInitialized,
|
||||||
if (!mpvClient) return;
|
initialize: () => initializeOverlayRuntime(),
|
||||||
mpvClient.connect();
|
toggleVisible: () => toggleVisibleOverlay(),
|
||||||
|
toggleInvisible: () => toggleInvisibleOverlay(),
|
||||||
|
setVisible: (visible) => setVisibleOverlayVisible(visible),
|
||||||
|
setInvisible: (visible) => setInvisibleOverlayVisible(visible),
|
||||||
},
|
},
|
||||||
isTexthookerRunning: () => texthookerService.isRunning(),
|
mining: {
|
||||||
setTexthookerPort: (port) => {
|
copyCurrentSubtitle: () => copyCurrentSubtitle(),
|
||||||
texthookerPort = port;
|
startPendingMultiCopy: (timeoutMs) => startPendingMultiCopy(timeoutMs),
|
||||||
|
mineSentenceCard: () => mineSentenceCard(),
|
||||||
|
startPendingMineSentenceMultiple: (timeoutMs) =>
|
||||||
|
startPendingMineSentenceMultiple(timeoutMs),
|
||||||
|
updateLastCardFromClipboard: () => updateLastCardFromClipboard(),
|
||||||
|
triggerFieldGrouping: () => triggerFieldGrouping(),
|
||||||
|
triggerSubsyncFromConfig: () => triggerSubsyncFromConfig(),
|
||||||
|
markLastCardAsAudioCard: () => markLastCardAsAudioCard(),
|
||||||
},
|
},
|
||||||
getTexthookerPort: () => texthookerPort,
|
ui: {
|
||||||
shouldOpenTexthookerBrowser: () =>
|
openYomitanSettings: () => openYomitanSettings(),
|
||||||
getResolvedConfig().texthooker?.openBrowser !== false,
|
cycleSecondarySubMode: () => cycleSecondarySubMode(),
|
||||||
ensureTexthookerRunning: (port) => {
|
openRuntimeOptionsPalette: () => openRuntimeOptionsPalette(),
|
||||||
if (!texthookerService.isRunning()) {
|
printHelp: () => printHelp(DEFAULT_TEXTHOOKER_PORT),
|
||||||
texthookerService.start(port);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
openTexthookerInBrowser: (url) => {
|
app: {
|
||||||
shell.openExternal(url);
|
stop: () => app.quit(),
|
||||||
|
hasMainWindow: () => Boolean(mainWindow),
|
||||||
},
|
},
|
||||||
stopApp: () => app.quit(),
|
|
||||||
isOverlayRuntimeInitialized: () => overlayRuntimeInitialized,
|
|
||||||
initializeOverlayRuntime: () => initializeOverlayRuntime(),
|
|
||||||
toggleVisibleOverlay: () => toggleVisibleOverlay(),
|
|
||||||
toggleInvisibleOverlay: () => toggleInvisibleOverlay(),
|
|
||||||
openYomitanSettingsDelayed: (delayMs) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
openYomitanSettings();
|
|
||||||
}, delayMs);
|
|
||||||
},
|
|
||||||
setVisibleOverlayVisible: (visible) => setVisibleOverlayVisible(visible),
|
|
||||||
setInvisibleOverlayVisible: (visible) =>
|
|
||||||
setInvisibleOverlayVisible(visible),
|
|
||||||
copyCurrentSubtitle: () => copyCurrentSubtitle(),
|
|
||||||
startPendingMultiCopy: (timeoutMs) => startPendingMultiCopy(timeoutMs),
|
|
||||||
mineSentenceCard: () => mineSentenceCard(),
|
|
||||||
startPendingMineSentenceMultiple: (timeoutMs) =>
|
|
||||||
startPendingMineSentenceMultiple(timeoutMs),
|
|
||||||
updateLastCardFromClipboard: () => updateLastCardFromClipboard(),
|
|
||||||
cycleSecondarySubMode: () => cycleSecondarySubMode(),
|
|
||||||
triggerFieldGrouping: () => triggerFieldGrouping(),
|
|
||||||
triggerSubsyncFromConfig: () => triggerSubsyncFromConfig(),
|
|
||||||
markLastCardAsAudioCard: () => markLastCardAsAudioCard(),
|
|
||||||
openRuntimeOptionsPalette: () => openRuntimeOptionsPalette(),
|
|
||||||
printHelp: () => printHelp(DEFAULT_TEXTHOOKER_PORT),
|
|
||||||
hasMainWindow: () => Boolean(mainWindow),
|
|
||||||
getMultiCopyTimeoutMs: () => getConfiguredShortcuts().multiCopyTimeoutMs,
|
getMultiCopyTimeoutMs: () => getConfiguredShortcuts().multiCopyTimeoutMs,
|
||||||
showMpvOsd: (text) => showMpvOsd(text),
|
schedule: (fn, delayMs) => setTimeout(fn, delayMs),
|
||||||
log: (message) => {
|
log: (message) => {
|
||||||
console.log(message);
|
console.log(message);
|
||||||
},
|
},
|
||||||
@@ -720,6 +715,7 @@ function handleCliCommand(
|
|||||||
console.error(message, err);
|
console.error(message, err);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
handleCliCommandService(args, source, deps);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleInitialArgs(): void {
|
function handleInitialArgs(): void {
|
||||||
|
|||||||
Reference in New Issue
Block a user