commit 51d902402de1356a9089d9ab867d340dcaf83315
parent 5ea6298d9cb9945ed23a9b856e69fca839bc48a0
Author: Florian Dold <florian@dold.me>
Date: Thu, 19 Jun 2025 23:30:43 +0200
implement JSON file export for qtart platform
Diffstat:
5 files changed, 80 insertions(+), 10 deletions(-)
diff --git a/packages/taler-util/src/qtart.ts b/packages/taler-util/src/qtart.ts
@@ -16,20 +16,47 @@ export interface QjsHttpOptions {
headers?: string[];
}
+export interface ErrReceiver {
+ errno?: number;
+}
+
+export interface QjsFile {
+ /**
+ * Close the file. Return 0 if OK or -errno in case of I/O error.
+ */
+ close(): number;
+ /**
+ * Outputs the string with UTF-8 encoding.
+ */
+ puts(str: string): void;
+}
+
export interface QjsOsLib {
- fetchHttp(url: string, options?: QjsHttpOptions): {
- promise: Promise<QjsHttpResp>,
- cancelFn: () => number,
+ fetchHttp(
+ url: string,
+ options?: QjsHttpOptions,
+ ): {
+ promise: Promise<QjsHttpResp>;
+ cancelFn: () => number;
};
postMessageToHost(s: string): void;
setMessageFromHostHandler(h: (s: string) => void): void;
rename(oldPath: string, newPath: string): number;
remove(path: string): number;
+
+ readonly O_RDONLY: number;
+ readonly O_WRONLY: number;
+ readonly O_RDWR: number;
+ readonly O_APPEND: number;
+ readonly O_CREAT: number;
+ readonly O_EXCL: number;
+ readonly O_TRUNC: number;
}
export interface QjsStdLib {
writeFile(filename: string, contents: string): void;
loadFile(filename: string): string;
+ open(filename: string, mode: string, errorObj?: ErrReceiver): QjsFile | null;
}
// This is not the nodejs "os" module, but the qjs "os" module.
diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts
@@ -4112,12 +4112,21 @@ export interface ExportDbToFileRequest {
* where the extension depends on the used DB backend.
*/
stem: string;
+
+ /**
+ * Force the format of the export.
+ *
+ * Currently only "json" is supported as a forced
+ * export format.
+ */
+ forceFormat?: string;
}
export const codecForExportDbToFileRequest = (): Codec<ExportDbToFileRequest> =>
buildCodecForObject<ExportDbToFileRequest>()
.property("directory", codecForString())
.property("stem", codecForString())
+ .property("forceFormat", codecOptional(codecForString()))
.build("ExportDbToFileRequest");
export interface ExportDbToFileResponse {
diff --git a/packages/taler-wallet-core/src/host-impl.qtart.ts b/packages/taler-wallet-core/src/host-impl.qtart.ts
@@ -49,6 +49,7 @@ import {
getSqlite3FilenameFromStoragePath,
makeTempfileId,
} from "./host-common.js";
+import { exportDb } from "./index.js";
import { Wallet, WalletDatabaseImplementation } from "./wallet.js";
const logger = new Logger("host-impl.qtart.ts");
@@ -122,12 +123,29 @@ async function makeSqliteDb(
primitiveStatements: numStmt,
};
},
- async exportToFile(directory, stem) {
- const path = `${directory}/${stem}.sqlite3`;
- await myBackend.backupToFile(path);
- return {
- path,
- };
+ async exportToFile(directory, stem, forceFormat) {
+ if (forceFormat === "json") {
+ const path = `${directory}/${stem}.json`;
+ const dbDump = await exportDb(myBridgeIdbFactory);
+ const errObj = { errno: undefined };
+ const file = qjsStd.open(path, "w+", errObj);
+ if (!file) {
+ throw Error(`could not create file (errno=${errObj.errno})`);
+ }
+ file.puts(JSON.stringify(dbDump));
+ file.close();
+ return {
+ path,
+ };
+ } else if (forceFormat === "sqlite3" || forceFormat == null) {
+ const path = `${directory}/${stem}.sqlite3`;
+ await myBackend.backupToFile(path);
+ return {
+ path,
+ };
+ } else {
+ throw Error(`forcing format ${forceFormat} not supported`);
+ }
},
idbFactory: myBridgeIdbFactory,
};
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
@@ -1670,6 +1670,7 @@ async function handleExportDbToFile(
const res = await wex.ws.dbImplementation.exportToFile(
req.directory,
req.stem,
+ req.forceFormat,
);
return {
path: res.path,
@@ -2557,7 +2558,11 @@ export type HttpFactory = (config: WalletRunConfig) => HttpRequestLibrary;
export interface WalletDatabaseImplementation {
idbFactory: BridgeIDBFactory;
getStats: () => AccessStats;
- exportToFile: (directory: string, stem: string) => Promise<{ path: string }>;
+ exportToFile: (
+ directory: string,
+ stem: string,
+ forceFormat?: string,
+ ) => Promise<{ path: string }>;
}
/**
diff --git a/packages/taler-wallet-embedded/src/wallet-qjs.ts b/packages/taler-wallet-embedded/src/wallet-qjs.ts
@@ -51,6 +51,10 @@ import {
testWithLocal,
} from "./wallet-qjs-tests.js";
+import * as talerUtil from "@gnu-taler/taler-util";
+import * as talerUtilHttp from "@gnu-taler/taler-util/http";
+import * as talerWalletCore from "@gnu-taler/taler-wallet-core";
+
setGlobalLogLevelFromString("trace");
const logger = new Logger("taler-wallet-embedded/index.ts");
@@ -261,3 +265,10 @@ globalThis.testReduceAction = reduceAction;
globalThis.testDiscoverPolicies = discoverPolicies;
// @ts-ignore
globalThis.testWithFdold = testWithFdold;
+
+// @ts-ignore
+globalThis.talerModules = {
+ talerUtil,
+ talerWalletCore,
+ talerUtilHttp,
+};