Tests. Autocomplete. Few Fixes. Mocks for Electrum Service. Template-to-Json parser. Fix global paths. Use IO Dependency injection for logging from cli. Additional commands in CLI.

This commit is contained in:
2026-04-20 10:30:38 +00:00
parent df4f438f6d
commit ff2fe126c6
44 changed files with 8220 additions and 1503 deletions

View File

@@ -0,0 +1,62 @@
#!/usr/bin/env node
/**
* Build script for xo-complete autocomplete helper.
*
* Bundles the autocomplete script with all dependencies into a single file
* for faster startup time. This avoids the ~600ms module import overhead
* that occurs with dynamic ESM imports.
*/
import * as esbuild from "esbuild";
import { join, dirname } from "path";
import { fileURLToPath } from "url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const projectRoot = join(__dirname, "..");
async function build() {
try {
const result = await esbuild.build({
entryPoints: [join(projectRoot, "src/cli/autocomplete/complete.ts")],
bundle: true,
platform: "node",
target: "node20",
format: "esm",
outfile: join(projectRoot, "dist/cli/autocomplete/complete.bundle.js"),
// Mark modules as external that either have native components or bundling issues
// We keep heavy deps external, and also keep offline-engine external so it stays
// as a dynamic import (it pulls in the heavy engine dependencies)
external: [
"better-sqlite3",
"fsevents",
"@bitauth/libauth",
"@xo-cash/*",
"@electrum-cash/*",
"./offline-engine.js",
],
// Generate source maps for debugging
sourcemap: true,
// Minify for slightly smaller bundle
minify: false,
// Keep names for better error messages
keepNames: true,
// Log build info
logLevel: "info",
});
console.log("Autocomplete bundle built successfully!");
if (result.warnings.length > 0) {
console.warn("Warnings:", result.warnings);
}
} catch (error) {
console.error("Build failed:", error);
process.exit(1);
}
}
build();

102
scripts/template-to-json.ts Normal file
View File

@@ -0,0 +1,102 @@
import fs from "node:fs/promises";
import path from "node:path";
import { pathToFileURL } from "node:url";
/**
* This just convers the <template>.ts file to a <template>.json file.
* Im fairly sure there is a util in the engine or engine-packages for this, but I decided to just keep it as simple as possible because I didn't feel like digging around for it.
*
* Usage:
* tsx scripts/template-to-json.ts ../templates/source/p2pkh.ts ./p2pkh.json p2pkhTemplate
*/
/**
* Prints usage to stderr and exits with a non-zero code.
*/
function printUsageAndExit(): never {
console.error(
[
"Usage: tsx scripts/template-to-json.ts <input.ts> <output.json> [exportName]",
"",
"Loads a TypeScript module, picks one exported value, and writes JSON.stringify to the output path.",
"If exportName is omitted: uses default export, or the only non-function export if there is exactly one.",
"",
"Example:",
" tsx scripts/template-to-json.ts ../templates/source/p2pkh.ts ./p2pkh.json p2pkhTemplate",
].join("\n"),
);
process.exit(1);
}
/**
* Collects runtime export keys whose values are not functions (typical for data/template objects).
*/
function listDataExportKeys(mod: Record<string, unknown>): string[] {
return Object.keys(mod).filter((key) => {
if (key === "__esModule") {
return false;
}
const value = mod[key];
return typeof value !== "function";
});
}
/**
* Resolves which export to serialize: explicit name, default, or a single unambiguous data export.
*/
function resolveExportedValue(
mod: Record<string, unknown>,
exportName: string | undefined,
): unknown {
if (exportName !== undefined) {
if (!(exportName in mod)) {
const keys = listDataExportKeys(mod);
console.error(
`Export "${exportName}" not found. Available data exports: ${keys.length ? keys.join(", ") : "(none)"}`,
);
process.exit(1);
}
return mod[exportName];
}
if ("default" in mod && mod.default !== undefined) {
return mod.default;
}
const keys = listDataExportKeys(mod);
if (keys.length === 1) {
return mod[keys[0]!];
}
if (keys.length === 0) {
console.error("No suitable exports found (need default or a non-function export).");
} else {
console.error(
`Multiple data exports found; pass exportName. Candidates: ${keys.join(", ")}`,
);
}
process.exit(1);
}
async function main(): Promise<void> {
const args = process.argv.slice(2);
if (args.length < 2) {
printUsageAndExit();
}
const [inputRel, outputRel, exportName] = args;
const inputPath = path.resolve(process.cwd(), inputRel!);
const outputPath = path.resolve(process.cwd(), outputRel!);
/** Dynamic import needs a file URL so Windows paths and ESM resolution behave. */
const fileUrl = pathToFileURL(inputPath).href;
const mod = (await import(fileUrl)) as Record<string, unknown>;
const value = resolveExportedValue(mod, exportName);
const json = `${JSON.stringify(value, null, 2)}\n`;
await fs.mkdir(path.dirname(outputPath), { recursive: true });
await fs.writeFile(outputPath, json, "utf8");
console.log(`Wrote ${outputPath}`);
}
await main();