fix: next round of cleanups

This commit is contained in:
2026-02-27 11:26:00 +01:00
parent c6edacba51
commit 18e0557ef5
25 changed files with 1021 additions and 1415 deletions

View File

@@ -1,2 +0,0 @@
export declare function generatePythonApiModuleV1(): string;
//# sourceMappingURL=generatePythonApiModuleV1.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"generatePythonApiModuleV1.d.ts","sourceRoot":"","sources":["generatePythonApiModuleV1.ts"],"names":[],"mappings":"AAyHA,wBAAgB,yBAAyB,IAAI,MAAM,CA+DlD"}

View File

@@ -1,162 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.generatePythonApiModuleV1 = generatePythonApiModuleV1;
const pythonApiContractV1_1 = require("./pythonApiContractV1");
const PYTHON_RESERVED_KEYWORDS = new Set([
'false',
'none',
'true',
'and',
'as',
'assert',
'async',
'await',
'break',
'class',
'continue',
'def',
'del',
'elif',
'else',
'except',
'finally',
'for',
'from',
'global',
'if',
'import',
'in',
'is',
'lambda',
'nonlocal',
'not',
'or',
'pass',
'raise',
'return',
'try',
'while',
'with',
'yield',
'match',
'case',
]);
function toSnakeCase(value) {
return value
.replace(/([a-z0-9])([A-Z])/g, '$1_$2')
.replace(/[^a-zA-Z0-9]+/g, '_')
.replace(/^_+|_+$/g, '')
.toLowerCase();
}
function quotePython(value) {
return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
}
function toPythonIdentifier(value) {
let identifier = toSnakeCase(value);
if (!identifier) {
identifier = '_';
}
if (/^[0-9]/.test(identifier)) {
identifier = `_${identifier}`;
}
if (PYTHON_RESERVED_KEYWORDS.has(identifier)) {
identifier = `${identifier}_`;
}
return identifier;
}
function buildPythonMethod(method) {
const [namespace, member] = method.method.split('.');
if (!namespace || !member) {
return '';
}
const pythonMethodName = toPythonIdentifier(member);
const pythonParams = method.params.map((param) => ({
sourceName: param.name,
pythonName: toPythonIdentifier(param.name),
required: param.required,
}));
const signature = pythonParams.length > 0
? `, ${pythonParams.map((param) => (param.required ? param.pythonName : `${param.pythonName}=None`)).join(', ')}`
: '';
const argsDict = method.params.length > 0
? `{ ${method.params.map((param, index) => `"${param.name}": ${pythonParams[index]?.pythonName}`).join(', ')} }`
: '{}';
return [
` async def ${pythonMethodName}(self${signature}):`,
` \"\"\"${quotePython(method.description)}\"\"\"`,
` return await self._transport.call("${method.method}", ${argsDict})`,
'',
].join('\n');
}
function buildPythonNamespaceClass(namespace, methods) {
const className = `${namespace[0].toUpperCase()}${namespace.slice(1)}Api`;
const methodBlocks = methods.map((method) => buildPythonMethod(method)).join('');
return [
`class ${className}:`,
' def __init__(self, transport):',
' self._transport = transport',
'',
methodBlocks.trimEnd(),
'',
].join('\n');
}
function generatePythonApiModuleV1() {
const namespaceMap = new Map();
for (const method of pythonApiContractV1_1.BDS_PYTHON_API_CONTRACT_V1.methods) {
const [namespace] = method.method.split('.');
if (!namespace) {
continue;
}
const entries = namespaceMap.get(namespace) ?? [];
entries.push({
method: method.method,
description: method.description,
params: method.params.map((param) => ({
name: param.name,
required: param.required,
})),
});
namespaceMap.set(namespace, entries);
}
const namespaceBlocks = Array.from(namespaceMap.entries())
.sort(([left], [right]) => left.localeCompare(right))
.map(([namespace, methods]) => buildPythonNamespaceClass(namespace, methods))
.join('\n');
const namespaceAssignments = Array.from(namespaceMap.keys())
.sort((left, right) => left.localeCompare(right))
.map((namespace) => ` self.${toPythonIdentifier(namespace)} = ${namespace[0].toUpperCase()}${namespace.slice(1)}Api(transport)`)
.join('\n');
return [
'# Auto-generated by generatePythonApiModuleV1.ts',
`# Contract version: ${pythonApiContractV1_1.BDS_PYTHON_API_CONTRACT_V1.version}`,
'',
'import json',
'',
'class BdsApiError(Exception):',
' pass',
'',
namespaceBlocks.trimEnd(),
'',
'class BdsApi:',
' def __init__(self, transport):',
' self._transport = transport',
namespaceAssignments,
'',
'class _Transport:',
' def __init__(self, call_impl):',
' self._call_impl = call_impl',
'',
' async def call(self, method, args):',
' raw_result = await self._call_impl(method, json.dumps(args))',
' if raw_result is None or raw_result == "":',
' return None',
' return json.loads(raw_result)',
'',
'def install_bds_api(call_impl):',
' _transport = _Transport(call_impl)',
' bds = BdsApi(_transport)',
' return bds',
'',
].join('\n');
}
//# sourceMappingURL=generatePythonApiModuleV1.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"generatePythonApiModuleV1.js","sourceRoot":"","sources":["generatePythonApiModuleV1.ts"],"names":[],"mappings":";;AAyHA,8DA+DC;AAxLD,+DAAmE;AAEnE,MAAM,wBAAwB,GAAG,IAAI,GAAG,CAAC;IACvC,OAAO;IACP,MAAM;IACN,MAAM;IACN,KAAK;IACL,IAAI;IACJ,QAAQ;IACR,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,UAAU;IACV,KAAK;IACL,KAAK;IACL,MAAM;IACN,MAAM;IACN,QAAQ;IACR,SAAS;IACT,KAAK;IACL,MAAM;IACN,QAAQ;IACR,IAAI;IACJ,QAAQ;IACR,IAAI;IACJ,IAAI;IACJ,QAAQ;IACR,UAAU;IACV,KAAK;IACL,IAAI;IACJ,MAAM;IACN,OAAO;IACP,QAAQ;IACR,KAAK;IACL,OAAO;IACP,MAAM;IACN,OAAO;IACP,OAAO;IACP,MAAM;CACP,CAAC,CAAC;AAEH,SAAS,WAAW,CAAC,KAAa;IAChC,OAAO,KAAK;SACT,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC;SACtC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC;SAC9B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,WAAW,EAAE,CAAC;AACnB,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAa;IACvC,IAAI,UAAU,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,UAAU,GAAG,GAAG,CAAC;IACnB,CAAC;IAED,IAAI,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC;IAChC,CAAC;IAED,IAAI,wBAAwB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7C,UAAU,GAAG,GAAG,UAAU,GAAG,CAAC;IAChC,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,iBAAiB,CAAC,MAI1B;IACC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrD,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM,EAAE,CAAC;QAC1B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,gBAAgB,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACjD,UAAU,EAAE,KAAK,CAAC,IAAI;QACtB,UAAU,EAAE,kBAAkB,CAAC,KAAK,CAAC,IAAI,CAAC;QAC1C,QAAQ,EAAE,KAAK,CAAC,QAAQ;KACzB,CAAC,CAAC,CAAC;IAEJ,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC;QACvC,CAAC,CAAC,KAAK,YAAY,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,UAAU,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACjH,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;QACvC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,IAAI,MAAM,YAAY,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;QAChH,CAAC,CAAC,IAAI,CAAC;IAET,OAAO;QACL,iBAAiB,gBAAgB,QAAQ,SAAS,IAAI;QACtD,iBAAiB,WAAW,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ;QACxD,8CAA8C,MAAM,CAAC,MAAM,MAAM,QAAQ,GAAG;QAC5E,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,SAAS,yBAAyB,CAChC,SAAiB,EACjB,OAA2G;IAE3G,MAAM,SAAS,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;IAC1E,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEjF,OAAO;QACL,SAAS,SAAS,GAAG;QACrB,oCAAoC;QACpC,qCAAqC;QACrC,EAAE;QACF,YAAY,CAAC,OAAO,EAAE;QACtB,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,SAAgB,yBAAyB;IACvC,MAAM,YAAY,GAAG,IAAI,GAAG,EAA8G,CAAC;IAE3I,KAAK,MAAM,MAAM,IAAI,gDAA0B,CAAC,OAAO,EAAE,CAAC;QACxD,MAAM,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7C,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,SAAS;QACX,CAAC;QAED,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QAClD,OAAO,CAAC,IAAI,CAAC;YACX,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBACpC,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;aACzB,CAAC,CAAC;SACJ,CAAC,CAAC;QACH,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,eAAe,GAAG,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;SACvD,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;SACpD,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,yBAAyB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;SAC5E,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,oBAAoB,GAAG,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;SACzD,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;SAChD,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,gBAAgB,kBAAkB,CAAC,SAAS,CAAC,MAAM,SAAS,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,gBAAgB,CAAC;SACtI,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;QACL,kDAAkD;QAClD,uBAAuB,gDAA0B,CAAC,OAAO,EAAE;QAC3D,EAAE;QACF,aAAa;QACb,EAAE;QACF,+BAA+B;QAC/B,UAAU;QACV,EAAE;QACF,eAAe,CAAC,OAAO,EAAE;QACzB,EAAE;QACF,eAAe;QACf,oCAAoC;QACpC,qCAAqC;QACrC,oBAAoB;QACpB,EAAE;QACF,mBAAmB;QACnB,oCAAoC;QACpC,qCAAqC;QACrC,EAAE;QACF,yCAAyC;QACzC,sEAAsE;QACtE,oDAAoD;QACpD,yBAAyB;QACzB,uCAAuC;QACvC,EAAE;QACF,iCAAiC;QACjC,wCAAwC;QACxC,8BAA8B;QAC9B,gBAAgB;QAChB,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}

View File

@@ -1,41 +0,0 @@
import type { ElectronAPI } from '../../main/shared/electronApi';
type PythonPromiseMethodPath = {
[Group in keyof ElectronAPI]: ElectronAPI[Group] extends Record<string, (...args: never[]) => unknown> ? {
[Method in keyof ElectronAPI[Group]]: ElectronAPI[Group][Method] extends (...args: never[]) => Promise<unknown> ? `${Extract<Group, string>}.${Extract<Method, string>}` : never;
}[keyof ElectronAPI[Group]] : never;
}[keyof ElectronAPI];
export type PythonApiParamType = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'any' | 'stringOrNull';
export interface PythonApiParamContractV1 {
name: string;
type: PythonApiParamType;
required: boolean;
}
export interface PythonApiMethodContractV1 {
method: PythonPromiseMethodPath;
description: string;
params: PythonApiParamContractV1[];
returns: string;
}
export interface PythonApiDataStructureFieldContractV1 {
name: string;
type: string;
required: boolean;
description: string;
}
export interface PythonApiDataStructureContractV1 {
name: string;
description: string;
fields: PythonApiDataStructureFieldContractV1[];
}
export interface PythonApiContractV1 {
version: string;
generatedAt: string;
methods: PythonApiMethodContractV1[];
dataStructures: PythonApiDataStructureContractV1[];
}
export declare const BDS_PYTHON_API_CONTRACT_V1: PythonApiContractV1;
export declare function listPythonApiMethodNames(): string[];
export declare function getPythonApiMethodContract(methodName: string): PythonApiMethodContractV1 | undefined;
export declare function getPythonApiDataStructureContracts(): PythonApiDataStructureContractV1[];
export {};
//# sourceMappingURL=pythonApiContractV1.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"pythonApiContractV1.d.ts","sourceRoot":"","sources":["pythonApiContractV1.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAEjE,KAAK,uBAAuB,GAAG;KAC5B,KAAK,IAAI,MAAM,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC,GAClG;SACG,MAAM,IAAI,MAAM,WAAW,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,GAC3G,GAAG,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GACtD,KAAK;KACV,CAAC,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC,GAC3B,KAAK;CACV,CAAC,MAAM,WAAW,CAAC,CAAC;AAErB,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,GAAG,cAAc,CAAC;AAE/G,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,kBAAkB,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,uBAAuB,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,wBAAwB,EAAE,CAAC;IACnC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qCAAqC;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gCAAgC;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,qCAAqC,EAAE,CAAC;CACjD;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,yBAAyB,EAAE,CAAC;IACrC,cAAc,EAAE,gCAAgC,EAAE,CAAC;CACpD;AA8TD,eAAO,MAAM,0BAA0B,EAAE,mBAKxC,CAAC;AAEF,wBAAgB,wBAAwB,IAAI,MAAM,EAAE,CAEnD;AAED,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,yBAAyB,GAAG,SAAS,CAEpG;AAED,wBAAgB,kCAAkC,IAAI,gCAAgC,EAAE,CAEvF"}

View File

@@ -1,320 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BDS_PYTHON_API_CONTRACT_V1 = void 0;
exports.listPythonApiMethodNames = listPythonApiMethodNames;
exports.getPythonApiMethodContract = getPythonApiMethodContract;
exports.getPythonApiDataStructureContracts = getPythonApiDataStructureContracts;
const requiredString = (name) => ({ name, type: 'string', required: true });
const optionalString = (name) => ({ name, type: 'string', required: false });
const optionalNumber = (name) => ({ name, type: 'number', required: false });
const requiredObject = (name) => ({ name, type: 'object', required: true });
const optionalObject = (name) => ({ name, type: 'object', required: false });
const requiredArray = (name) => ({ name, type: 'array', required: true });
const requiredAny = (name) => ({ name, type: 'any', required: true });
const requiredStringOrNull = (name) => ({ name, type: 'stringOrNull', required: true });
function method(methodName, description, params, returns) {
return {
method: methodName,
description,
params,
returns,
};
}
const METHODS_V1 = [
method('projects.create', 'Create a project.', [requiredObject('data')], 'ProjectData'),
method('projects.update', 'Update a project by id.', [requiredString('id'), requiredObject('data')], 'ProjectData | null'),
method('projects.delete', 'Delete a project by id.', [requiredString('id')], 'boolean'),
method('projects.deleteWithData', 'Delete a project and data by id.', [requiredString('id')], 'boolean'),
method('projects.get', 'Fetch one project by id.', [requiredString('id')], 'ProjectData | null'),
method('projects.getAll', 'Fetch all projects.', [], 'ProjectData[]'),
method('projects.getActive', 'Fetch active project.', [], 'ProjectData | null'),
method('projects.setActive', 'Set active project by id.', [requiredString('id')], 'ProjectData | null'),
method('posts.create', 'Create a post.', [requiredObject('data')], 'PostData'),
method('posts.update', 'Update a post by id.', [requiredString('id'), requiredObject('data')], 'PostData | null'),
method('posts.delete', 'Delete a post by id.', [requiredString('id')], 'boolean'),
method('posts.get', 'Fetch one post by id.', [requiredString('postId')], 'PostData | null'),
method('posts.getPreviewUrl', 'Get preview URL for post.', [requiredString('id'), optionalObject('options')], 'string | null'),
method('posts.getAll', 'Fetch posts with pagination.', [optionalObject('options')], 'PaginatedPostsResult'),
method('posts.getByStatus', 'Fetch posts by status.', [requiredString('status')], 'PostData[]'),
method('posts.publish', 'Publish a post by id.', [requiredString('id')], 'PostData | null'),
method('posts.discard', 'Discard draft changes for post.', [requiredString('id')], 'PostData | null'),
method('posts.hasPublishedVersion', 'Check if post has published version.', [requiredString('id')], 'boolean'),
method('posts.rebuildFromFiles', 'Rebuild posts database from files.', [], 'void'),
method('posts.reindexText', 'Reindex post search text.', [], 'void'),
method('posts.search', 'Search posts by free-text query.', [requiredString('query')], 'SearchResult[]'),
method('posts.filter', 'Filter posts by criteria.', [requiredObject('filter')], 'PostData[]'),
method('posts.getTags', 'Get all post tags.', [], 'string[]'),
method('posts.getCategories', 'Get all post categories.', [], 'string[]'),
method('posts.getByYearMonth', 'Get post counts grouped by year/month.', [], 'Array<{ year: number; month: number; count: number } >'),
method('posts.getDashboardStats', 'Get post dashboard stats.', [], 'DashboardStats'),
method('posts.getTagsWithCounts', 'Get post tags with counts.', [], 'TagCount[]'),
method('posts.getCategoriesWithCounts', 'Get post categories with counts.', [], 'CategoryCount[]'),
method('posts.getLinksTo', 'Get posts linked to given post.', [requiredString('id')], 'PostData[]'),
method('posts.getLinkedBy', 'Get posts linking to given post.', [requiredString('id')], 'PostData[]'),
method('posts.rebuildLinks', 'Rebuild post link graph.', [], 'void'),
method('posts.isSlugAvailable', 'Check if post slug is available.', [requiredString('slug'), optionalString('excludePostId')], 'boolean'),
method('posts.generateUniqueSlug', 'Generate unique slug from title.', [requiredString('title'), optionalString('excludePostId')], 'string'),
method('media.import', 'Import media file.', [requiredString('sourcePath'), optionalObject('metadata')], 'MediaData'),
method('media.update', 'Update media metadata by id.', [requiredString('id'), requiredObject('data')], 'MediaData | null'),
method('media.replaceFile', 'Replace media file by id.', [requiredString('id'), requiredString('newSourcePath')], 'MediaData | null'),
method('media.delete', 'Delete media by id.', [requiredString('id')], 'boolean'),
method('media.get', 'Fetch one media by id.', [requiredString('id')], 'MediaData | null'),
method('media.getUrl', 'Get media URL by id.', [requiredString('id')], 'string | null'),
method('media.getFilePath', 'Get media file path by id.', [requiredString('id')], 'string | null'),
method('media.getAll', 'Fetch all media.', [], 'MediaData[]'),
method('media.rebuildFromFiles', 'Rebuild media database from files.', [], 'void'),
method('media.reindexText', 'Reindex media search text.', [], 'void'),
method('media.getThumbnail', 'Get media thumbnail URL.', [requiredString('id'), optionalString('size')], 'string | null'),
method('media.regenerateThumbnails', 'Regenerate thumbnails for media.', [requiredString('id')], 'Record<string, string> | null'),
method('media.regenerateMissingThumbnails', 'Regenerate all missing thumbnails.', [], '{ processed: number; generated: number; failed: number }'),
method('media.filter', 'Filter media by criteria.', [requiredObject('filter')], 'MediaData[]'),
method('media.search', 'Search media by free-text query.', [requiredString('query')], 'MediaSearchResult[]'),
method('media.getByYearMonth', 'Get media counts grouped by year/month.', [], 'Array<{ year: number; month: number; count: number } >'),
method('media.getTags', 'Get all media tags.', [], 'string[]'),
method('media.getTagsWithCounts', 'Get media tags with counts.', [], 'TagCount[]'),
method('scripts.create', 'Create script.', [requiredObject('data')], 'ScriptData'),
method('scripts.update', 'Update script by id.', [requiredString('id'), requiredObject('data')], 'ScriptData | null'),
method('scripts.delete', 'Delete script by id.', [requiredString('id')], 'boolean'),
method('scripts.get', 'Fetch script by id.', [requiredString('id')], 'ScriptData | null'),
method('scripts.getAll', 'Fetch all scripts.', [], 'ScriptData[]'),
method('scripts.rebuildFromFiles', 'Rebuild scripts from files.', [], 'void'),
method('tasks.getAll', 'Fetch all tasks.', [], 'TaskProgress[]'),
method('tasks.getRunning', 'Fetch running tasks.', [], 'TaskProgress[]'),
method('tasks.cancel', 'Cancel task by id.', [requiredString('taskId')], 'boolean'),
method('tasks.clearCompleted', 'Clear completed tasks.', [], 'void'),
method('app.getDataPaths', 'Get app data paths.', [], '{ database: string; posts: string; media: string }'),
method('app.getSystemLanguage', 'Get system language.', [], 'string'),
method('app.getTitleBarMetrics', 'Get title bar metrics.', [], '{ macosLeftInset: number } | null'),
method('app.openFolder', 'Open folder in system file manager.', [requiredString('folderPath')], 'string'),
method('app.showItemInFolder', 'Reveal item in system file manager.', [requiredString('itemPath')], 'void'),
method('app.selectFolder', 'Show folder picker dialog.', [optionalString('title')], 'string | null'),
method('app.getDefaultProjectPath', 'Get default project path.', [requiredString('projectId')], 'string'),
method('app.readProjectMetadata', 'Read project metadata from path.', [requiredString('folderPath')], '{ name?: string; description?: string; publicUrl?: string; mainLanguage?: string } | null'),
method('app.getBlogmarkBookmarklet', 'Get blogmark bookmarklet script.', [], 'string'),
method('app.copyToClipboard', 'Copy text to clipboard.', [requiredString('text')], 'boolean'),
method('app.notifyRendererReady', 'Notify main process renderer is ready.', [], 'boolean'),
method('app.setPreviewPostTarget', 'Set preview post target.', [requiredStringOrNull('postId')], 'void'),
method('app.triggerMenuAction', 'Trigger menu action.', [requiredString('action')], 'void'),
method('meta.getTags', 'Get project tags.', [], 'string[]'),
method('meta.getCategories', 'Get project categories.', [], 'string[]'),
method('meta.addTag', 'Add project tag.', [requiredString('tag')], 'string[]'),
method('meta.removeTag', 'Remove project tag.', [requiredString('tag')], 'string[]'),
method('meta.addCategory', 'Add project category.', [requiredString('category')], 'string[]'),
method('meta.removeCategory', 'Remove project category.', [requiredString('category')], 'string[]'),
method('meta.syncOnStartup', 'Sync meta values on startup.', [], '{ tags: string[]; categories: string[]; projectMetadata: ProjectMetadata | null }'),
method('meta.getProjectMetadata', 'Read active project metadata.', [], 'ProjectMetadata | null'),
method('meta.setProjectMetadata', 'Set project metadata.', [requiredObject('metadata')], 'ProjectMetadata | null'),
method('meta.updateProjectMetadata', 'Update project metadata.', [requiredObject('updates')], 'ProjectMetadata | null'),
method('tags.getAll', 'Fetch all tags.', [], 'TagData[]'),
method('tags.getWithCounts', 'Fetch tags with counts.', [], 'TagWithCount[]'),
method('tags.get', 'Fetch tag by id.', [requiredString('id')], 'TagData | null'),
method('tags.getByName', 'Fetch tag by name.', [requiredString('name')], 'TagData | null'),
method('tags.create', 'Create tag.', [requiredObject('data')], 'TagData'),
method('tags.update', 'Update tag by id.', [requiredString('id'), requiredObject('data')], 'TagData | null'),
method('tags.delete', 'Delete tag by id.', [requiredString('id')], 'DeleteTagResult'),
method('tags.merge', 'Merge tags into target tag.', [requiredArray('sourceTagIds'), requiredString('targetTagId')], 'MergeTagsResult'),
method('tags.rename', 'Rename tag by id.', [requiredString('id'), requiredString('newName')], 'RenameTagResult'),
method('tags.getPostsWithTag', 'Get posts using a tag.', [requiredString('tagId')], 'string[]'),
method('tags.syncFromPosts', 'Sync tag index from posts.', [], 'SyncTagsResult'),
method('chat.checkReady', 'Check chat backend readiness.', [], 'ChatReadyStatus'),
method('chat.validateApiKey', 'Validate chat API key and list available models.', [requiredString('apiKey')], '{ isValid: boolean; models: ChatModel[] }'),
method('chat.setApiKey', 'Store chat API key.', [requiredString('apiKey')], '{ success: boolean; error?: string }'),
method('chat.getApiKey', 'Get stored chat API key status.', [], 'ChatApiKeyStatus'),
method('chat.getAvailableModels', 'Get available chat models and selected default.', [], '{ success: boolean; models?: ChatModel[]; selectedModel?: string; error?: string }'),
method('chat.setDefaultModel', 'Set default chat model.', [requiredString('modelId')], '{ success: boolean; error?: string }'),
method('chat.getSystemPrompt', 'Get configured system prompt.', [], '{ success: boolean; prompt?: string; error?: string }'),
method('chat.setSystemPrompt', 'Set system prompt.', [requiredString('prompt')], '{ success: boolean; error?: string }'),
method('chat.getConversations', 'Fetch all chat conversations.', [], 'ChatConversation[]'),
method('chat.createConversation', 'Create a chat conversation.', [optionalString('title'), optionalString('model')], 'ChatConversation'),
method('chat.getConversation', 'Fetch one chat conversation by id.', [requiredString('id')], 'ChatConversation | null'),
method('chat.updateConversation', 'Update chat conversation metadata.', [requiredString('id'), requiredObject('updates')], 'ChatConversation | null'),
method('chat.deleteConversation', 'Delete chat conversation by id.', [requiredString('id')], 'boolean'),
method('chat.sendMessage', 'Send message to chat conversation.', [requiredString('conversationId'), requiredString('message'), optionalObject('metadata')], '{ success: boolean; message?: string; error?: string }'),
method('chat.abortMessage', 'Abort active streaming chat response.', [requiredString('conversationId')], 'void'),
method('chat.getHistory', 'Get message history for conversation.', [requiredString('conversationId')], 'ChatMessage[]'),
method('chat.clearMessages', 'Clear messages for conversation.', [requiredString('conversationId')], 'void'),
method('chat.setConversationModel', 'Set model for a conversation.', [requiredString('conversationId'), requiredString('modelId')], 'void'),
method('chat.analyzeTaxonomy', 'Analyze categories and tags using AI.', [requiredArray('categories'), requiredArray('tags'), requiredString('modelId')], '{ success: boolean; categoryMappings?: Record<string, string>; tagMappings?: Record<string, string>; error?: string }'),
method('chat.analyzeMediaImage', 'Analyze media image and propose metadata.', [requiredString('mediaId'), optionalString('language')], '{ success: boolean; title?: string; alt?: string; caption?: string; error?: string }'),
method('sync.configure', 'Configure sync.', [requiredObject('config')], 'void'),
method('sync.start', 'Start sync operation.', [optionalString('direction')], 'SyncResult'),
method('sync.getStatus', 'Get sync status.', [], "'idle' | 'syncing' | 'error'"),
method('sync.isConfigured', 'Check if sync is configured.', [], 'boolean'),
method('sync.getPendingCount', 'Get pending sync item count.', [], '{ posts: number; media: number }'),
method('sync.getLog', 'Get sync log.', [optionalNumber('limit')], 'unknown[]'),
method('sync.stopAutoSync', 'Stop automatic sync.', [], 'void'),
];
const DATA_STRUCTURES_V1 = [
{
name: 'ProjectData',
description: 'Project metadata stored in the app database.',
fields: [
{ name: 'id', type: 'string', required: true, description: 'Unique project identifier.' },
{ name: 'name', type: 'string', required: true, description: 'Human-readable project name.' },
{ name: 'slug', type: 'string', required: true, description: 'URL-friendly project slug.' },
{ name: 'description', type: 'string', required: false, description: 'Optional project description.' },
{ name: 'dataPath', type: 'string', required: false, description: 'Filesystem path for project data.' },
{ name: 'isActive', type: 'boolean', required: true, description: 'Whether this project is currently active.' },
{ name: 'createdAt', type: 'string', required: true, description: 'Creation timestamp (ISO string).' },
{ name: 'updatedAt', type: 'string', required: true, description: 'Last update timestamp (ISO string).' },
],
},
{
name: 'PostData',
description: 'Canonical post object used across editor and generation flows.',
fields: [
{ name: 'id', type: 'string', required: true, description: 'Unique post identifier.' },
{ name: 'projectId', type: 'string', required: true, description: 'Owning project id.' },
{ name: 'title', type: 'string', required: true, description: 'Post title.' },
{ name: 'slug', type: 'string', required: true, description: 'URL slug used for generated routes.' },
{ name: 'excerpt', type: 'string', required: false, description: 'Optional short summary.' },
{ name: 'content', type: 'string', required: true, description: 'Markdown body content.' },
{ name: 'status', type: "'draft' | 'published' | 'archived'", required: true, description: 'Publication lifecycle state.' },
{ name: 'author', type: 'string', required: false, description: 'Optional author name.' },
{ name: 'createdAt', type: 'string', required: true, description: 'Creation timestamp (ISO string).' },
{ name: 'updatedAt', type: 'string', required: true, description: 'Last update timestamp (ISO string).' },
{ name: 'publishedAt', type: 'string', required: false, description: 'Publication timestamp for published posts.' },
{ name: 'tags', type: 'string[]', required: true, description: 'List of tag names.' },
{ name: 'categories', type: 'string[]', required: true, description: 'List of category names.' },
],
},
{
name: 'MediaData',
description: 'Canonical media object representing imported files and metadata.',
fields: [
{ name: 'id', type: 'string', required: true, description: 'Unique media identifier.' },
{ name: 'projectId', type: 'string', required: true, description: 'Owning project id.' },
{ name: 'filename', type: 'string', required: true, description: 'Stored filename in project media folder.' },
{ name: 'originalName', type: 'string', required: true, description: 'Original imported filename.' },
{ name: 'mimeType', type: 'string', required: true, description: 'Detected MIME type.' },
{ name: 'size', type: 'number', required: true, description: 'File size in bytes.' },
{ name: 'width', type: 'number', required: false, description: 'Image width in pixels when available.' },
{ name: 'height', type: 'number', required: false, description: 'Image height in pixels when available.' },
{ name: 'title', type: 'string', required: false, description: 'Optional display title.' },
{ name: 'alt', type: 'string', required: false, description: 'Optional alternative text.' },
{ name: 'caption', type: 'string', required: false, description: 'Optional caption text.' },
{ name: 'author', type: 'string', required: false, description: 'Optional author credit.' },
{ name: 'createdAt', type: 'string', required: true, description: 'Creation timestamp (ISO string).' },
{ name: 'updatedAt', type: 'string', required: true, description: 'Last update timestamp (ISO string).' },
{ name: 'tags', type: 'string[]', required: true, description: 'List of media tags.' },
],
},
{
name: 'ScriptData',
description: 'Script definition for Python macros, utilities, and transforms.',
fields: [
{ name: 'id', type: 'string', required: true, description: 'Unique script identifier.' },
{ name: 'projectId', type: 'string', required: true, description: 'Owning project id.' },
{ name: 'slug', type: 'string', required: true, description: 'Stable script slug.' },
{ name: 'title', type: 'string', required: true, description: 'Human-readable script title.' },
{ name: 'kind', type: "'macro' | 'utility' | 'transform'", required: true, description: 'Script category.' },
{ name: 'entrypoint', type: 'string', required: true, description: 'Python entrypoint function name.' },
{ name: 'enabled', type: 'boolean', required: true, description: 'Whether script is enabled.' },
{ name: 'version', type: 'number', required: true, description: 'Incrementing script version.' },
{ name: 'filePath', type: 'string', required: true, description: 'Filesystem path to script file.' },
{ name: 'content', type: 'string', required: true, description: 'Script source code.' },
{ name: 'createdAt', type: 'string', required: true, description: 'Creation timestamp (ISO string).' },
{ name: 'updatedAt', type: 'string', required: true, description: 'Last update timestamp (ISO string).' },
],
},
{
name: 'TaskProgress',
description: 'Task queue status object for long-running operations.',
fields: [
{ name: 'taskId', type: 'string', required: true, description: 'Unique task identifier.' },
{ name: 'name', type: 'string', required: true, description: 'Task display name.' },
{ name: 'status', type: "'pending' | 'running' | 'completed' | 'failed' | 'cancelled'", required: true, description: 'Current task status.' },
{ name: 'progress', type: 'number', required: true, description: 'Progress percentage from 0-100.' },
{ name: 'message', type: 'string', required: true, description: 'Current progress message.' },
{ name: 'startTime', type: 'string', required: true, description: 'Task start time (ISO string).' },
{ name: 'endTime', type: 'string', required: false, description: 'Task completion time (ISO string).' },
{ name: 'error', type: 'string', required: false, description: 'Error message when failed.' },
{ name: 'groupId', type: 'string', required: false, description: 'Optional grouping id.' },
{ name: 'groupName', type: 'string', required: false, description: 'Optional grouping label.' },
],
},
{
name: 'ProjectMetadata',
description: 'Extended project metadata from project settings.',
fields: [
{ name: 'name', type: 'string', required: true, description: 'Project display name.' },
{ name: 'description', type: 'string', required: false, description: 'Optional project description.' },
{ name: 'dataPath', type: 'string', required: false, description: 'Optional custom data path.' },
{ name: 'publicUrl', type: 'string', required: false, description: 'Optional public site URL.' },
{ name: 'mainLanguage', type: 'string', required: false, description: 'Main render language code.' },
{ name: 'defaultAuthor', type: 'string', required: false, description: 'Default author for new posts.' },
{ name: 'maxPostsPerPage', type: 'number', required: false, description: 'Pagination size for generated lists.' },
{ name: 'blogmarkCategory', type: 'string', required: false, description: 'Default category for blogmark imports.' },
{ name: 'pythonRuntimeMode', type: "'webworker' | 'main-thread'", required: false, description: 'Python runtime execution mode.' },
{ name: 'picoTheme', type: 'string', required: false, description: 'Preferred Pico theme token.' },
{ name: 'categoryMetadata', type: 'object', required: false, description: 'Category metadata keyed by category slug.' },
{ name: 'categorySettings', type: 'object', required: false, description: 'Category render settings keyed by category slug.' },
],
},
{
name: 'ChatConversation',
description: 'Chat conversation container.',
fields: [
{ name: 'id', type: 'string', required: true, description: 'Unique conversation identifier.' },
{ name: 'title', type: 'string', required: true, description: 'Conversation title.' },
{ name: 'model', type: 'string', required: false, description: 'Optional model id used by this conversation.' },
{ name: 'createdAt', type: 'string', required: true, description: 'Creation timestamp (ISO string).' },
{ name: 'updatedAt', type: 'string', required: true, description: 'Last update timestamp (ISO string).' },
],
},
{
name: 'ChatMessage',
description: 'Single message entry in a conversation history.',
fields: [
{ name: 'id', type: 'string', required: true, description: 'Unique message identifier.' },
{ name: 'conversationId', type: 'string', required: true, description: 'Owning conversation id.' },
{ name: 'role', type: "'user' | 'assistant' | 'system' | 'tool'", required: true, description: 'Message author role.' },
{ name: 'content', type: 'string', required: true, description: 'Message text content.' },
{ name: 'toolCallId', type: 'string', required: false, description: 'Tool call id when associated with tool output.' },
{ name: 'toolCalls', type: 'string', required: false, description: 'Serialized tool call payload when present.' },
{ name: 'createdAt', type: 'string', required: true, description: 'Creation timestamp (ISO string).' },
],
},
{
name: 'ChatModel',
description: 'Available chat model descriptor.',
fields: [
{ name: 'id', type: 'string', required: true, description: 'Model identifier.' },
{ name: 'name', type: 'string', required: true, description: 'Human-readable model name.' },
{ name: 'provider', type: 'string', required: false, description: 'Model provider name.' },
],
},
{
name: 'ChatReadyStatus',
description: 'Chat backend readiness status.',
fields: [
{ name: 'ready', type: 'boolean', required: true, description: 'Whether chat backend is ready.' },
{ name: 'error', type: 'string', required: false, description: 'Error description when not ready.' },
{ name: 'backend', type: 'string', required: false, description: 'Selected backend identifier.' },
],
},
{
name: 'ChatApiKeyStatus',
description: 'Stored API key state for chat provider.',
fields: [
{ name: 'hasKey', type: 'boolean', required: true, description: 'Whether a key is configured.' },
{ name: 'maskedKey', type: 'string', required: true, description: 'Masked key representation for UI display.' },
],
},
];
exports.BDS_PYTHON_API_CONTRACT_V1 = {
version: '1.6.0',
generatedAt: '2026-02-25T00:00:00.000Z',
methods: METHODS_V1,
dataStructures: DATA_STRUCTURES_V1,
};
function listPythonApiMethodNames() {
return exports.BDS_PYTHON_API_CONTRACT_V1.methods.map((entry) => entry.method);
}
function getPythonApiMethodContract(methodName) {
return exports.BDS_PYTHON_API_CONTRACT_V1.methods.find((entry) => entry.method === methodName);
}
function getPythonApiDataStructureContracts() {
return exports.BDS_PYTHON_API_CONTRACT_V1.dataStructures;
}
//# sourceMappingURL=pythonApiContractV1.js.map

File diff suppressed because one or more lines are too long