c34dab2cca
Build and Push Docker Container / build-and-push (push) Successful in 4m28s
- Create missing note files before running property:set. - Default date-only datetime values to midnight. - Keep property names out of path authorization checks. - Split policy, vault, daily note, and command target helpers. - Split n8n node args, description, output, and API response helpers. - Split API tests by auth, command, daily note, and property behavior. - Bump API and n8n node versions.
78 lines
2.4 KiB
TypeScript
78 lines
2.4 KiB
TypeScript
import type { IDataObject } from 'n8n-workflow';
|
|
|
|
import type { ObsidianCommandResult } from './ObsidianApiClient';
|
|
|
|
export function normalizeCommandResult(response: unknown): ObsidianCommandResult {
|
|
const data = extractResponseBody(response);
|
|
const result = typeof data === 'string' ? JSON.parse(data) : data;
|
|
|
|
if (!isRecord(result)) {
|
|
throw new Error('Obsidian API returned a non-object response');
|
|
}
|
|
|
|
return {
|
|
id: String(result.id ?? ''),
|
|
command: Array.isArray(result.command) ? result.command.map(String) : [],
|
|
cwd: String(result.cwd ?? ''),
|
|
exit_code: Number(result.exit_code ?? 0),
|
|
stdout: String(result.stdout ?? ''),
|
|
stderr: String(result.stderr ?? ''),
|
|
duration_ms: Number(result.duration_ms ?? 0),
|
|
timed_out: Boolean(result.timed_out),
|
|
};
|
|
}
|
|
|
|
export function maybeParseStdout(result: ObsidianCommandResult): IDataObject | undefined {
|
|
const cmd = result.command;
|
|
const hasVault = cmd.includes('vault');
|
|
const hasVaults = cmd.includes('vaults');
|
|
|
|
if (!hasVault && !hasVaults) return undefined;
|
|
|
|
const lines = (result.stdout || '')
|
|
.split(/\r?\n/)
|
|
.map((l) => l.trim())
|
|
.filter(Boolean);
|
|
|
|
if (!lines.length) return undefined;
|
|
if (hasVaults) return parseVaultsOutput(lines);
|
|
return parseVaultOutput(lines);
|
|
}
|
|
|
|
function parseVaultsOutput(lines: string[]): IDataObject {
|
|
const vaults: Array<{ name: string; path: string }> = [];
|
|
for (const line of lines) {
|
|
const parts = line.split('\t');
|
|
if (parts.length < 2) continue;
|
|
const name = parts[0].trim();
|
|
const path = parts.slice(1).join('\t').trim();
|
|
if (!name || !path) continue;
|
|
vaults.push({ name, path });
|
|
}
|
|
return vaults.length ? { vaults } : { vaults: lines };
|
|
}
|
|
|
|
function parseVaultOutput(lines: string[]): IDataObject | undefined {
|
|
const kv: Record<string, string | number> = {};
|
|
for (const line of lines) {
|
|
const parts = line.split('\t');
|
|
if (parts.length < 2) continue;
|
|
const key = parts[0].trim();
|
|
const rawValue = parts.slice(1).join('\t').trim();
|
|
if (!key || !rawValue) continue;
|
|
kv[key] = /^-?\d+$/.test(rawValue) ? Number(rawValue) : rawValue;
|
|
}
|
|
return Object.keys(kv).length ? kv : undefined;
|
|
}
|
|
|
|
function extractResponseBody(response: unknown): unknown {
|
|
if (!isRecord(response)) return response;
|
|
if ('body' in response) return response.body;
|
|
if ('data' in response) return response.data;
|
|
return response;
|
|
}
|
|
|
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
return typeof value === 'object' && value !== null;
|
|
}
|