commit cf10cdeeb6277818bd231f4ce6000e587c304a41
parent 715d71e22b31ab481bd460edec67b54bf8a23001
Author: Florian Dold <dold@taler.net>
Date: Sat, 18 Jul 2026 20:05:50 +0200
db: move storage-neutral types to db-common.ts
Diffstat:
10 files changed, 143 insertions(+), 120 deletions(-)
diff --git a/packages/taler-wallet-core/src/common.ts b/packages/taler-wallet-core/src/common.ts
@@ -65,6 +65,7 @@ import {
ExchangeEntryDbRecordStatus,
ExchangeEntryDbUpdateStatus,
timestampPreciseToDb,
+ WalletRetryInfo,
} from "./db-common.js";
import {
CoinHistoryRecord,
@@ -557,11 +558,14 @@ export interface TaskRunErrorResult {
errorDetail: TalerErrorDetail;
}
-export interface DbRetryInfo {
- firstTry: DbPreciseTimestamp;
- nextRetry: DbPreciseTimestamp;
- retryCounter: number;
-}
+/**
+ * Retry state of a task.
+ *
+ * The stored shape lives in db-common.ts as {@link WalletRetryInfo}; this
+ * interface exists so the retry-policy helpers below can merge into a
+ * namespace of the same name.
+ */
+export interface DbRetryInfo extends WalletRetryInfo {}
export interface RetryPolicy {
readonly backoffDelta: Duration;
diff --git a/packages/taler-wallet-core/src/db-common.ts b/packages/taler-wallet-core/src/db-common.ts
@@ -117,6 +117,102 @@ export function timestampOptionalAbsoluteFromDb(
}
/**
+ * Metadata for a transaction.
+ * This object store is effectively a materialzed view of transactions gathered
+ * from various other object stores.
+ *
+ * Primary key: transactionId
+ */
+export interface WalletTransactionMeta {
+ /**
+ * Transaction identifier.
+ * Also determines the type of the transaction.
+ */
+ transactionId: string;
+
+ timestamp: DbPreciseTimestamp;
+
+ /**
+ * Status of the transaction, matches the status enum of the
+ * transaction of the type determined by the transaction ID.
+ */
+ status: number;
+
+ /**
+ * Exchanges involved in the transaction.
+ */
+ exchanges: string[];
+
+ currency: string;
+}
+
+/**
+ * Retry state of a task.
+ *
+ * The policy that computes these timestamps lives in common.ts; this is only
+ * the stored shape.
+ */
+export interface WalletRetryInfo {
+ firstTry: DbPreciseTimestamp;
+ nextRetry: DbPreciseTimestamp;
+ retryCounter: number;
+}
+
+export interface WalletOperationRetry {
+ /**
+ * Unique identifier for the operation. Typically of
+ * the format `${opType}-${opUniqueKey}`
+ *
+ * @see {@link TaskIdentifiers}
+ */
+ id: string;
+
+ lastError?: TalerErrorDetail;
+
+ retryInfo: WalletRetryInfo;
+}
+
+export interface WalletContractTerms {
+ /**
+ * Contract terms hash.
+ */
+ h: string;
+
+ /**
+ * Contract terms JSON.
+ *
+ * Deliberately untyped: this is arbitrary JSON as received from the
+ * merchant, and there is nothing to validate it against at this layer.
+ */
+ contractTermsRaw: any;
+}
+
+/**
+ * How a coin came into the wallet.
+ */
+export enum CoinSourceType {
+ Withdraw = "withdraw",
+ Refresh = "refresh",
+ Reward = "reward",
+}
+
+export enum RefundReason {
+ /**
+ * Normal refund given by the merchant.
+ */
+ NormalRefund = "normal-refund",
+ /**
+ * Refund from an aborted payment.
+ */
+ AbortRefund = "abort-pay-refund",
+}
+
+export enum ExchangeMigrationReason {
+ MismatchedBaseUrl = "mismatched-base-url",
+ UnavailableOldUrl = "unavailable-old-url",
+}
+
+/**
* Status of a denomination.
*/
export enum DenominationVerificationStatus {
diff --git a/packages/taler-wallet-core/src/db-indexeddb.ts b/packages/taler-wallet-core/src/db-indexeddb.ts
@@ -112,8 +112,14 @@ import {
timestampProtocolFromDb,
WalletDenomination,
DenomFees,
+ CoinSourceType,
+ WalletTransactionMeta,
+ WalletOperationRetry,
+ WalletContractTerms,
+ RefundReason,
+ ExchangeMigrationReason,
} from "./db-common.js";
-export { DenomFees };
+export { DenomFees, CoinSourceType, RefundReason, ExchangeMigrationReason };
import {
DbAccess,
DbAccessImpl,
@@ -523,12 +529,6 @@ export interface PlanchetRecord {
ageCommitmentProof?: AgeCommitmentProof;
}
-export enum CoinSourceType {
- Withdraw = "withdraw",
- Refresh = "refresh",
- Reward = "reward",
-}
-
export interface WithdrawCoinSource {
type: CoinSourceType.Withdraw;
@@ -964,17 +964,6 @@ export interface RefreshSessionRecord {
// (legacy v1 refresh)
}
-export enum RefundReason {
- /**
- * Normal refund given by the merchant.
- */
- NormalRefund = "normal-refund",
- /**
- * Refund from an aborted payment.
- */
- AbortRefund = "abort-pay-refund",
-}
-
/**
* Partial information about the downloaded proposal.
* Only contains data that is relevant for indexing on the
@@ -1841,20 +1830,6 @@ export interface ReserveRecord {
amlReview?: boolean;
}
-export interface OperationRetryRecord {
- /**
- * Unique identifier for the operation. Typically of
- * the format `${opType}-${opUniqueKey}`
- *
- * @see {@link TaskIdentifiers}
- */
- id: string;
-
- lastError?: TalerErrorDetail;
-
- retryInfo: DbRetryInfo;
-}
-
/**
* Availability of coins of a given denomination (and age restriction!).
*
@@ -1892,18 +1867,6 @@ export interface CoinAvailabilityRecord {
pendingRefreshOutputCount?: number;
}
-export interface ContractTermsRecord {
- /**
- * Contract terms hash.
- */
- h: string;
-
- /**
- * Contract terms JSON.
- */
- contractTermsRaw: any;
-}
-
export interface DbExchangeHandle {
url: string;
exchangeMasterPub: string;
@@ -1993,36 +1956,6 @@ export interface GlobalCurrencyExchangeRecord {
exchangeMasterPub: string;
}
-/**
- * Metadata for a transaction.
- * This object store is effectively a materialzed view of transactions gathered
- * from various other object stores.
- *
- * Primary key: transactionId
- */
-export interface TransactionMetaRecord {
- /**
- * Transaction identifier.
- * Also determines the type of the transaction.
- */
- transactionId: string;
-
- timestamp: DbPreciseTimestamp;
-
- /**
- * Status of the transaction, matches the status enum of the
- * transaction of the type determined by the transaction ID.
- */
- status: number;
-
- /**
- * Exchanges involved in the transaction.
- */
- exchanges: string[];
-
- currency: string;
-}
-
export interface DenomLossEventRecord {
denomLossEventId: string;
currency: string;
@@ -2051,11 +1984,6 @@ export interface CurrencyInfoRecord {
source: "exchange" | "user" | "preset";
}
-export enum ExchangeMigrationReason {
- MismatchedBaseUrl = "mismatched-base-url",
- UnavailableOldUrl = "unavailable-old-url",
-}
-
export interface ExchangeMigrationLogRecord {
oldExchangeBaseUrl: string;
newExchangeBaseUrl: string;
@@ -2185,7 +2113,7 @@ export const WalletIndexedDbStoresV1 = {
},
}),
transactionsMeta: describeStoreV2({
- recordCodec: passthroughCodec<TransactionMetaRecord>(),
+ recordCodec: passthroughCodec<WalletTransactionMeta>(),
storeName: "transactionsMeta",
keyPath: "transactionId",
versionAdded: 13,
@@ -2631,7 +2559,7 @@ export const WalletIndexedDbStoresV1 = {
),
operationRetries: describeStore(
"operationRetries",
- describeContents<OperationRetryRecord>({
+ describeContents<WalletOperationRetry>({
keyPath: "id",
}),
{},
@@ -2718,7 +2646,7 @@ export const WalletIndexedDbStoresV1 = {
),
contractTerms: describeStore(
"contractTerms",
- describeContents<ContractTermsRecord>({
+ describeContents<WalletContractTerms>({
keyPath: "h",
}),
{},
diff --git a/packages/taler-wallet-core/src/dbtx-indexeddb.ts b/packages/taler-wallet-core/src/dbtx-indexeddb.ts
@@ -58,10 +58,6 @@ import type {
DonationSummaryRecord,
} from "./db-indexeddb.js";
import {
- PeerPullCreditRecord,
- PeerPushDebitRecord,
- PeerPushCreditRecord,
- PeerPullPaymentIncomingRecord,
PurchaseRecord,
WalletIndexedDbTransaction,
} from "./db-indexeddb.js";
@@ -591,25 +587,25 @@ export class IdbWalletTransaction implements WalletDbTransaction {
);
}
- async getActivePeerPushDebits(): Promise<PeerPushDebitRecord[]> {
+ async getActivePeerPushDebits(): Promise<WalletPeerPushDebit[]> {
return await this.tx.peerPushDebit.indexes.byStatus.getAll(
getActiveKeyRange(),
);
}
- async getActivePeerPushCredits(): Promise<PeerPushCreditRecord[]> {
+ async getActivePeerPushCredits(): Promise<WalletPeerPushCredit[]> {
return await this.tx.peerPushCredit.indexes.byStatus.getAll(
getActiveKeyRange(),
);
}
- async getActivePeerPullCredits(): Promise<PeerPullCreditRecord[]> {
+ async getActivePeerPullCredits(): Promise<WalletPeerPullCredit[]> {
return await this.tx.peerPullCredit.indexes.byStatus.getAll(
getActiveKeyRange(),
);
}
- async getActivePeerPullDebits(): Promise<PeerPullPaymentIncomingRecord[]> {
+ async getActivePeerPullDebits(): Promise<WalletPeerPullDebit[]> {
return await this.tx.peerPullDebit.indexes.byStatus.getAll(
getActiveKeyRange(),
);
diff --git a/packages/taler-wallet-core/src/dbtx.ts b/packages/taler-wallet-core/src/dbtx.ts
@@ -26,9 +26,12 @@
* Wallet<Name>. The <Name>Record types in db-indexeddb.ts describe the
* IndexedDB object stores and must not appear here.
*
- * FIXME: The remaining imports from db-indexeddb.js below are a violation of
- * that rule, and are removed in phase 0c of the DAL migration (see
- * DB_MIGRATION_PLAN.md) by introducing Wallet<Name> counterparts for them.
+ * FIXME: The nine type-only imports from db-indexeddb.js below still violate
+ * that rule. They are converted lazily: each gets its Wallet<Name>
+ * counterpart in db-common.ts when the migration phase that touches its store
+ * arrives, rather than all at once up front (see DB_MIGRATION_PLAN.md).
+ * They are type-only, so this file still emits an empty JS module and has no
+ * runtime dependency on IndexedDB.
*/
import {
@@ -58,10 +61,6 @@ import type {
DepositGroupRecord,
ExchangeDetailsRecord,
DonationSummaryRecord,
- PeerPullCreditRecord,
- PeerPushDebitRecord,
- PeerPushCreditRecord,
- PeerPullPaymentIncomingRecord,
PurchaseRecord,
} from "./db-indexeddb.js";
@@ -246,13 +245,13 @@ export interface WalletDbTransaction {
getActiveWithdrawalGroups(): Promise<WithdrawalGroupRecord[]>;
- getActivePeerPushDebits(): Promise<PeerPushDebitRecord[]>;
+ getActivePeerPushDebits(): Promise<WalletPeerPushDebit[]>;
- getActivePeerPushCredits(): Promise<PeerPushCreditRecord[]>;
+ getActivePeerPushCredits(): Promise<WalletPeerPushCredit[]>;
- getActivePeerPullCredits(): Promise<PeerPullCreditRecord[]>;
+ getActivePeerPullCredits(): Promise<WalletPeerPullCredit[]>;
- getActivePeerPullDebits(): Promise<PeerPullPaymentIncomingRecord[]>;
+ getActivePeerPullDebits(): Promise<WalletPeerPullDebit[]>;
getActivePurchases(): Promise<PurchaseRecord[]>;
diff --git a/packages/taler-wallet-core/src/pay-peer-pull-credit.ts b/packages/taler-wallet-core/src/pay-peer-pull-credit.ts
@@ -70,9 +70,9 @@ import {
WithdrawalGroupStatus,
timestampPreciseFromDb,
timestampPreciseToDb,
+ WalletOperationRetry,
} from "./db-common.js";
import {
- OperationRetryRecord,
WithdrawalGroupRecord,
WithdrawalRecordType,
} from "./db-indexeddb.js";
@@ -177,7 +177,7 @@ export class PeerPullCreditTransactionContext implements TransactionContext {
const peerContractTerms = ct.contractTermsRaw;
let wsr: WithdrawalGroupRecord | undefined = undefined;
- let wsrOrt: OperationRetryRecord | undefined = undefined;
+ let wsrOrt: WalletOperationRetry | undefined = undefined;
if (pullCredit.withdrawalGroupId) {
wsr = await tx.withdrawalGroups.get(pullCredit.withdrawalGroupId);
if (wsr) {
diff --git a/packages/taler-wallet-core/src/pay-peer-push-credit.ts b/packages/taler-wallet-core/src/pay-peer-push-credit.ts
@@ -72,9 +72,9 @@ import {
timestampPreciseFromDb,
timestampPreciseToDb,
WalletPeerPushCredit,
+ WalletOperationRetry,
} from "./db-common.js";
import {
- OperationRetryRecord,
WalletIndexedDbTransaction,
WithdrawalGroupRecord,
WithdrawalRecordType,
@@ -164,7 +164,7 @@ export class PeerPushCreditTransactionContext implements TransactionContext {
}
let wg: WithdrawalGroupRecord | undefined = undefined;
- let wgRetryRecord: OperationRetryRecord | undefined = undefined;
+ let wgRetryRecord: WalletOperationRetry | undefined = undefined;
if (pushInc.withdrawalGroupId) {
wg = await tx.withdrawalGroups.get(pushInc.withdrawalGroupId);
if (wg) {
diff --git a/packages/taler-wallet-core/src/shepherd.ts b/packages/taler-wallet-core/src/shepherd.ts
@@ -57,9 +57,9 @@ import {
ReserveRecordStatus,
timestampAbsoluteFromDb,
timestampPreciseToDb,
+ WalletOperationRetry,
} from "./db-common.js";
import {
- OperationRetryRecord,
WalletIndexedDbTransaction,
} from "./db-indexeddb.js";
import { processValidateDenoms } from "./denominations.js";
@@ -619,7 +619,7 @@ async function storePendingTaskError(
ws: InternalWalletState,
pendingTaskId: string,
e: TalerErrorDetail,
-): Promise<OperationRetryRecord> {
+): Promise<WalletOperationRetry> {
logger.trace(`storing task [pending] with ERROR for ${pendingTaskId}`);
const res = await ws.runStandaloneLegacyWalletDbTx(async (tx) => {
let retryRecord = await tx.operationRetries.get(pendingTaskId);
@@ -662,7 +662,7 @@ async function storePendingTaskPending(
ws: InternalWalletState,
pendingTaskId: string,
schedTime?: AbsoluteTime,
-): Promise<OperationRetryRecord> {
+): Promise<WalletOperationRetry> {
logger.trace(`storing task [pending] for ${pendingTaskId}`);
const res = await ws.runStandaloneLegacyWalletDbTx(async (tx) => {
let retryRecord = await tx.operationRetries.get(pendingTaskId);
diff --git a/packages/taler-wallet-core/src/transactions.ts b/packages/taler-wallet-core/src/transactions.ts
@@ -58,7 +58,7 @@ import {
PurchaseStatus,
timestampPreciseToDb,
} from "./db-common.js";
-import { TransactionMetaRecord } from "./db-indexeddb.js";
+import { WalletTransactionMeta } from "./db-common.js";
import { DepositTransactionContext } from "./deposits.js";
import { DenomLossTransactionContext } from "./exchanges.js";
import {
@@ -182,7 +182,7 @@ export function isUnsuccessfulTransaction(state: TransactionState): boolean {
function checkFilterIncludes(
req: GetTransactionsV2Request | undefined,
- mtx: TransactionMetaRecord,
+ mtx: WalletTransactionMeta,
): boolean {
if (shouldSkipCurrency(req, mtx.currency, mtx.exchanges)) {
return false;
@@ -254,7 +254,7 @@ async function addFiltered(
tx: LegacyWalletTxHandle,
req: GetTransactionsV2Request | undefined,
target: Transaction[],
- source: TransactionMetaRecord[],
+ source: WalletTransactionMeta[],
): Promise<number> {
let numAdded: number = 0;
for (const mtx of source) {
@@ -312,7 +312,7 @@ function sortTransactions(
async function findOffsetTransaction(
tx: LegacyWalletTxHandle,
req?: GetTransactionsV2Request,
-): Promise<TransactionMetaRecord | undefined> {
+): Promise<WalletTransactionMeta | undefined> {
let forwards = req?.limit == null || req.limit >= 0;
let closestTimestamp: DbPreciseTimestamp | undefined = undefined;
if (req?.offsetTransactionId) {
diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts
@@ -145,11 +145,11 @@ import {
timestampPreciseToDb,
timestampProtocolToDb,
WalletDenomination,
+ WalletOperationRetry,
} from "./db-common.js";
import {
CoinRecord,
CoinSourceType,
- OperationRetryRecord,
PlanchetRecord,
WgInfo,
WithdrawalGroupRecord,
@@ -213,7 +213,7 @@ interface TxKycDetails {
function buildTransactionForBankIntegratedWithdraw(
wg: WithdrawalGroupRecord,
scopes: ScopeInfo[],
- ort: OperationRetryRecord | undefined,
+ ort: WalletOperationRetry | undefined,
kycDetails: TxKycDetails | undefined,
): TransactionWithdrawal {
if (wg.wgInfo.withdrawalType !== WithdrawalRecordType.BankIntegrated) {
@@ -282,7 +282,7 @@ function buildTransactionForManualWithdraw(
wg: WithdrawalGroupRecord,
exchangeDetails: ExchangeDetails | undefined,
scopes: ScopeInfo[],
- ort: OperationRetryRecord | undefined,
+ ort: WalletOperationRetry | undefined,
kycDetails: TxKycDetails | undefined,
): TransactionWithdrawal {
if (wg.wgInfo.withdrawalType !== WithdrawalRecordType.BankManual) {