mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-07-29 01:01:34 -07:00
refactor(sync): collapse 11 sync args into forwarded token array
- Replace syncHost/syncSnapshotPath/… with syncCliTokens: string[] passed verbatim to --sync-cli sync - Move sync CLI validation from launcher to parseSyncCliTokens (app-side single owner) - Extract resolveImmersionDbPath to shared db-path.ts importable by both launcher and app - Delete driver.ts; inline types into libsql-driver.ts and hard-wire openLibsqlSyncDb - Drop OpenSyncDb injection from mergeSnapshotIntoDb and createDbSnapshot
This commit is contained in:
@@ -136,17 +136,7 @@ test('applyInvocationsToArgs maps config and jellyfin invocation state', () => {
|
||||
statsCleanupLifetime: false,
|
||||
statsLogLevel: null,
|
||||
syncTriggered: false,
|
||||
syncHost: null,
|
||||
syncSnapshotPath: null,
|
||||
syncMergePath: null,
|
||||
syncDirection: 'both',
|
||||
syncRemoteCmd: null,
|
||||
syncDbPath: null,
|
||||
syncForce: false,
|
||||
syncJson: false,
|
||||
syncCheck: false,
|
||||
syncMakeTemp: false,
|
||||
syncRemoveTempPath: '',
|
||||
syncCliTokens: [],
|
||||
syncLogLevel: null,
|
||||
syncUiTriggered: false,
|
||||
syncUiLogLevel: null,
|
||||
@@ -197,17 +187,7 @@ test('applyInvocationsToArgs maps settings invocation to settings window', () =>
|
||||
statsCleanupLifetime: false,
|
||||
statsLogLevel: null,
|
||||
syncTriggered: false,
|
||||
syncHost: null,
|
||||
syncSnapshotPath: null,
|
||||
syncMergePath: null,
|
||||
syncDirection: 'both',
|
||||
syncRemoteCmd: null,
|
||||
syncDbPath: null,
|
||||
syncForce: false,
|
||||
syncJson: false,
|
||||
syncCheck: false,
|
||||
syncMakeTemp: false,
|
||||
syncRemoveTempPath: '',
|
||||
syncCliTokens: [],
|
||||
syncLogLevel: null,
|
||||
syncUiTriggered: false,
|
||||
syncUiLogLevel: null,
|
||||
@@ -251,17 +231,7 @@ test('applyInvocationsToArgs fails when config invocation has no action', () =>
|
||||
statsCleanupLifetime: false,
|
||||
statsLogLevel: null,
|
||||
syncTriggered: false,
|
||||
syncHost: null,
|
||||
syncSnapshotPath: null,
|
||||
syncMergePath: null,
|
||||
syncDirection: 'both',
|
||||
syncRemoteCmd: null,
|
||||
syncDbPath: null,
|
||||
syncForce: false,
|
||||
syncJson: false,
|
||||
syncCheck: false,
|
||||
syncMakeTemp: false,
|
||||
syncRemoveTempPath: '',
|
||||
syncCliTokens: [],
|
||||
syncLogLevel: null,
|
||||
syncUiTriggered: false,
|
||||
syncUiLogLevel: null,
|
||||
@@ -303,17 +273,7 @@ test('applyInvocationsToArgs maps texthooker browser-open request', () => {
|
||||
statsCleanupLifetime: false,
|
||||
statsLogLevel: null,
|
||||
syncTriggered: false,
|
||||
syncHost: null,
|
||||
syncSnapshotPath: null,
|
||||
syncMergePath: null,
|
||||
syncDirection: 'both',
|
||||
syncRemoteCmd: null,
|
||||
syncDbPath: null,
|
||||
syncForce: false,
|
||||
syncJson: false,
|
||||
syncCheck: false,
|
||||
syncMakeTemp: false,
|
||||
syncRemoveTempPath: '',
|
||||
syncCliTokens: [],
|
||||
syncLogLevel: null,
|
||||
syncUiTriggered: false,
|
||||
syncUiLogLevel: null,
|
||||
|
||||
@@ -200,17 +200,7 @@ export function createDefaultArgs(
|
||||
useRofi: false,
|
||||
history: false,
|
||||
sync: false,
|
||||
syncHost: '',
|
||||
syncSnapshotPath: '',
|
||||
syncMergePath: '',
|
||||
syncDirection: 'both',
|
||||
syncRemoteCmd: '',
|
||||
syncDbPath: '',
|
||||
syncForce: false,
|
||||
syncJson: false,
|
||||
syncCheck: false,
|
||||
syncMakeTemp: false,
|
||||
syncRemoveTempPath: '',
|
||||
syncCliTokens: [],
|
||||
syncUi: false,
|
||||
logLevel: loggingConfig.level ?? 'warn',
|
||||
logRotation: loggingConfig.rotation ?? 7,
|
||||
@@ -279,17 +269,7 @@ export function applyInvocationsToArgs(parsed: Args, invocations: CliInvocations
|
||||
}
|
||||
if (invocations.syncTriggered) {
|
||||
parsed.sync = true;
|
||||
parsed.syncHost = invocations.syncHost ?? '';
|
||||
parsed.syncSnapshotPath = invocations.syncSnapshotPath ?? '';
|
||||
parsed.syncMergePath = invocations.syncMergePath ?? '';
|
||||
parsed.syncDirection = invocations.syncDirection;
|
||||
parsed.syncRemoteCmd = invocations.syncRemoteCmd ?? '';
|
||||
parsed.syncDbPath = invocations.syncDbPath ?? '';
|
||||
parsed.syncForce = invocations.syncForce;
|
||||
parsed.syncJson = invocations.syncJson;
|
||||
parsed.syncCheck = invocations.syncCheck;
|
||||
parsed.syncMakeTemp = invocations.syncMakeTemp;
|
||||
parsed.syncRemoveTempPath = invocations.syncRemoveTempPath ?? '';
|
||||
parsed.syncCliTokens = invocations.syncCliTokens;
|
||||
if (invocations.syncLogLevel) parsed.logLevel = parseLogLevel(invocations.syncLogLevel);
|
||||
}
|
||||
if (invocations.syncUiTriggered) {
|
||||
|
||||
@@ -43,42 +43,63 @@ test('parseCliPrograms captures texthooker browser-open flag', () => {
|
||||
assert.equal(result.invocations.texthookerOpenBrowser, true);
|
||||
});
|
||||
|
||||
test('parseCliPrograms captures one-way sync directions', () => {
|
||||
test('parseCliPrograms lowers sync options into app-owned CLI tokens', () => {
|
||||
const push = parseCliPrograms(['sync', 'media-box', '--push'], 'subminer');
|
||||
assert.equal(push.invocations.syncTriggered, true);
|
||||
assert.deepEqual(push.invocations.syncCliTokens, ['media-box', '--push']);
|
||||
|
||||
const pull = parseCliPrograms(['sync', 'media-box', '--pull'], 'subminer');
|
||||
|
||||
assert.equal(push.invocations.syncDirection, 'push');
|
||||
assert.equal(pull.invocations.syncDirection, 'pull');
|
||||
});
|
||||
|
||||
test('parseCliPrograms rejects conflicting or hostless one-way sync directions', () => {
|
||||
assert.throws(
|
||||
() => parseCliPrograms(['sync', 'media-box', '--push', '--pull'], 'subminer'),
|
||||
/--push and --pull cannot be combined/,
|
||||
);
|
||||
assert.throws(
|
||||
() => parseCliPrograms(['sync', '--snapshot', '/tmp/stats.sqlite', '--push'], 'subminer'),
|
||||
/--push and --pull require a host/,
|
||||
);
|
||||
});
|
||||
|
||||
test('parseCliPrograms captures sync --json and --check flags', () => {
|
||||
const json = parseCliPrograms(['sync', 'media-box', '--json'], 'subminer');
|
||||
assert.equal(json.invocations.syncJson, true);
|
||||
assert.equal(json.invocations.syncCheck, false);
|
||||
assert.deepEqual(pull.invocations.syncCliTokens, ['media-box', '--pull']);
|
||||
|
||||
const check = parseCliPrograms(['sync', 'media-box', '--check', '--json'], 'subminer');
|
||||
assert.equal(check.invocations.syncCheck, true);
|
||||
assert.equal(check.invocations.syncJson, true);
|
||||
assert.equal(check.invocations.syncHost, 'media-box');
|
||||
assert.deepEqual(check.invocations.syncCliTokens, ['media-box', '--check', '--json']);
|
||||
|
||||
const full = parseCliPrograms(
|
||||
[
|
||||
'sync',
|
||||
'media-box',
|
||||
'--remote-cmd',
|
||||
'/opt/SubMiner.AppImage',
|
||||
'--db',
|
||||
'/tmp/db.sqlite',
|
||||
'--force',
|
||||
'--log-level',
|
||||
'debug',
|
||||
],
|
||||
'subminer',
|
||||
);
|
||||
assert.deepEqual(full.invocations.syncCliTokens, [
|
||||
'media-box',
|
||||
'--remote-cmd',
|
||||
'/opt/SubMiner.AppImage',
|
||||
'--db',
|
||||
'/tmp/db.sqlite',
|
||||
'--force',
|
||||
]);
|
||||
assert.equal(full.invocations.syncLogLevel, 'debug');
|
||||
|
||||
const snapshot = parseCliPrograms(['sync', '--snapshot', '/tmp/out.sqlite'], 'subminer');
|
||||
assert.deepEqual(snapshot.invocations.syncCliTokens, ['--snapshot', '/tmp/out.sqlite']);
|
||||
|
||||
const merge = parseCliPrograms(['sync', '--merge', '/tmp/in.sqlite'], 'subminer');
|
||||
assert.deepEqual(merge.invocations.syncCliTokens, ['--merge', '/tmp/in.sqlite']);
|
||||
|
||||
const makeTemp = parseCliPrograms(['sync', '--make-temp'], 'subminer');
|
||||
assert.deepEqual(makeTemp.invocations.syncCliTokens, ['--make-temp']);
|
||||
|
||||
const removeTemp = parseCliPrograms(['sync', '--remove-temp', '/tmp/subminer-sync-x'], 'subminer');
|
||||
assert.deepEqual(removeTemp.invocations.syncCliTokens, ['--remove-temp', '/tmp/subminer-sync-x']);
|
||||
});
|
||||
|
||||
test('parseCliPrograms rejects invalid sync --check combinations', () => {
|
||||
assert.throws(() => parseCliPrograms(['sync', '--check'], 'subminer'), /--check requires a host/);
|
||||
assert.throws(
|
||||
() => parseCliPrograms(['sync', 'media-box', '--check', '--push'], 'subminer'),
|
||||
/--check cannot be combined/,
|
||||
);
|
||||
test('parseCliPrograms leaves sync validation to the app parser', () => {
|
||||
// Invalid combinations are forwarded; the app's parseSyncCliTokens rejects them.
|
||||
const invalid = parseCliPrograms(['sync', 'media-box', '--push', '--pull'], 'subminer');
|
||||
assert.equal(invalid.invocations.syncTriggered, true);
|
||||
assert.deepEqual(invalid.invocations.syncCliTokens, ['media-box', '--push', '--pull']);
|
||||
|
||||
const empty = parseCliPrograms(['sync'], 'subminer');
|
||||
assert.equal(empty.invocations.syncTriggered, true);
|
||||
assert.deepEqual(empty.invocations.syncCliTokens, []);
|
||||
});
|
||||
|
||||
test('parseCliPrograms captures sync --ui', () => {
|
||||
|
||||
@@ -39,17 +39,7 @@ export interface CliInvocations {
|
||||
statsCleanupLifetime: boolean;
|
||||
statsLogLevel: string | null;
|
||||
syncTriggered: boolean;
|
||||
syncHost: string | null;
|
||||
syncSnapshotPath: string | null;
|
||||
syncMergePath: string | null;
|
||||
syncDirection: 'both' | 'push' | 'pull';
|
||||
syncRemoteCmd: string | null;
|
||||
syncDbPath: string | null;
|
||||
syncForce: boolean;
|
||||
syncJson: boolean;
|
||||
syncCheck: boolean;
|
||||
syncMakeTemp: boolean;
|
||||
syncRemoveTempPath: string | null;
|
||||
syncCliTokens: string[];
|
||||
syncLogLevel: string | null;
|
||||
syncUiTriggered: boolean;
|
||||
syncUiLogLevel: string | null;
|
||||
@@ -181,17 +171,7 @@ export function parseCliPrograms(
|
||||
let statsCleanupLifetime = false;
|
||||
let statsLogLevel: string | null = null;
|
||||
let syncTriggered = false;
|
||||
let syncHost: string | null = null;
|
||||
let syncSnapshotPath: string | null = null;
|
||||
let syncMergePath: string | null = null;
|
||||
let syncDirection: 'both' | 'push' | 'pull' = 'both';
|
||||
let syncRemoteCmd: string | null = null;
|
||||
let syncDbPath: string | null = null;
|
||||
let syncForce = false;
|
||||
let syncJson = false;
|
||||
let syncCheck = false;
|
||||
let syncMakeTemp = false;
|
||||
let syncRemoveTempPath: string | null = null;
|
||||
let syncCliTokens: string[] = [];
|
||||
let syncLogLevel: string | null = null;
|
||||
let syncUiTriggered = false;
|
||||
let syncUiLogLevel: string | null = null;
|
||||
@@ -369,49 +349,25 @@ export function parseCliPrograms(
|
||||
syncUiLogLevel = typeof options.logLevel === 'string' ? options.logLevel : null;
|
||||
return;
|
||||
}
|
||||
if (push && pull) {
|
||||
throw new Error('Sync --push and --pull cannot be combined.');
|
||||
}
|
||||
if ((push || pull) && !host) {
|
||||
throw new Error('Sync --push and --pull require a host.');
|
||||
}
|
||||
if (check && !host) {
|
||||
throw new Error('Sync --check requires a host.');
|
||||
}
|
||||
if (check && (push || pull || snapshot || merge)) {
|
||||
throw new Error(
|
||||
'Sync --check cannot be combined with --push, --pull, --snapshot, or --merge.',
|
||||
);
|
||||
}
|
||||
if ((makeTemp || removeTemp) && (push || pull || check)) {
|
||||
throw new Error('Sync --make-temp/--remove-temp cannot be combined with other sync options.');
|
||||
}
|
||||
const modes = [
|
||||
Boolean(host),
|
||||
Boolean(snapshot),
|
||||
Boolean(merge),
|
||||
makeTemp,
|
||||
Boolean(removeTemp),
|
||||
].filter(Boolean).length;
|
||||
if (modes === 0) {
|
||||
throw new Error('Sync requires a host, --snapshot <file>, or --merge <file>.');
|
||||
}
|
||||
if (modes > 1) {
|
||||
throw new Error('Sync host, --snapshot, --merge, --make-temp, and --remove-temp cannot be combined.');
|
||||
}
|
||||
// No validation here: the app's parseSyncCliTokens owns the sync rules
|
||||
// and its error text reaches the terminal through the child's stdio.
|
||||
const remoteCmd = typeof options.remoteCmd === 'string' ? options.remoteCmd.trim() : '';
|
||||
const dbPath = typeof options.db === 'string' ? options.db.trim() : '';
|
||||
const tokens: string[] = [];
|
||||
if (host) tokens.push(host);
|
||||
if (snapshot) tokens.push('--snapshot', snapshot);
|
||||
if (merge) tokens.push('--merge', merge);
|
||||
if (makeTemp) tokens.push('--make-temp');
|
||||
if (removeTemp) tokens.push('--remove-temp', removeTemp);
|
||||
if (push) tokens.push('--push');
|
||||
if (pull) tokens.push('--pull');
|
||||
if (check) tokens.push('--check');
|
||||
if (remoteCmd) tokens.push('--remote-cmd', remoteCmd);
|
||||
if (dbPath) tokens.push('--db', dbPath);
|
||||
if (options.force === true) tokens.push('--force');
|
||||
if (options.json === true) tokens.push('--json');
|
||||
syncTriggered = true;
|
||||
syncHost = host || null;
|
||||
syncSnapshotPath = snapshot || null;
|
||||
syncMergePath = merge || null;
|
||||
syncDirection = push ? 'push' : pull ? 'pull' : 'both';
|
||||
syncRemoteCmd =
|
||||
typeof options.remoteCmd === 'string' ? options.remoteCmd.trim() || null : null;
|
||||
syncDbPath = typeof options.db === 'string' ? options.db.trim() || null : null;
|
||||
syncForce = options.force === true;
|
||||
syncJson = options.json === true;
|
||||
syncCheck = check;
|
||||
syncMakeTemp = makeTemp;
|
||||
syncRemoveTempPath = removeTemp || null;
|
||||
syncCliTokens = tokens;
|
||||
syncLogLevel = typeof options.logLevel === 'string' ? options.logLevel : null;
|
||||
});
|
||||
|
||||
@@ -527,17 +483,7 @@ export function parseCliPrograms(
|
||||
statsCleanupLifetime,
|
||||
statsLogLevel,
|
||||
syncTriggered,
|
||||
syncHost,
|
||||
syncSnapshotPath,
|
||||
syncMergePath,
|
||||
syncDirection,
|
||||
syncRemoteCmd,
|
||||
syncDbPath,
|
||||
syncForce,
|
||||
syncJson,
|
||||
syncCheck,
|
||||
syncMakeTemp,
|
||||
syncRemoveTempPath,
|
||||
syncCliTokens,
|
||||
syncLogLevel,
|
||||
syncUiTriggered,
|
||||
syncUiLogLevel,
|
||||
|
||||
Reference in New Issue
Block a user