taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 0c0a4092043ca8cd4660b541d26e112bc6741b3c
parent a5a96dd347c95adce76ee2180c57f52fe0c406a6
Author: Antoine A <>
Date:   Wed, 23 Apr 2025 17:04:09 +0200

wallet-core: clean some type magic

Diffstat:
Mpackages/taler-wallet-core/src/observable-wrappers.ts | 23++++++++++++-----------
Mpackages/taler-wallet-core/src/query.ts | 94+++++++++++++++++++++++++++++--------------------------------------------------
2 files changed, 46 insertions(+), 71 deletions(-)

diff --git a/packages/taler-wallet-core/src/observable-wrappers.ts b/packages/taler-wallet-core/src/observable-wrappers.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2024 Taler Systems SA + (C) 2024-2025 Taler Systems SA GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -33,6 +33,7 @@ import { DbAccess, DbReadOnlyTransaction, DbReadWriteTransaction, + StoreMap, StoreNames, } from "./query.js"; import { TaskScheduler } from "./shepherd.js"; @@ -44,7 +45,7 @@ export class ObservableTaskScheduler implements TaskScheduler { constructor( private impl: TaskScheduler, private oc: ObservabilityContext, - ) {} + ) { } private taskDepCache = new Set<string>(); @@ -126,11 +127,11 @@ export function getCallerInfo(up: number = 2): string { return identifies.slice(up, up + 2).join("/"); } -export class ObservableDbAccess<StoreMap> implements DbAccess<StoreMap> { +export class ObservableDbAccess<Stores extends StoreMap> implements DbAccess<Stores> { constructor( - private impl: DbAccess<StoreMap>, + private impl: DbAccess<Stores>, private oc: ObservabilityContext, - ) {} + ) { } idbHandle(): IDBDatabase { return this.impl.idbHandle(); } @@ -140,7 +141,7 @@ export class ObservableDbAccess<StoreMap> implements DbAccess<StoreMap> { label?: string; }, txf: ( - tx: DbReadWriteTransaction<StoreMap, StoreNames<StoreMap>[]>, + tx: DbReadWriteTransaction<Stores, StoreNames<Stores>[]>, ) => Promise<T>, ): Promise<T> { const location = getCallerInfo(); @@ -173,7 +174,7 @@ export class ObservableDbAccess<StoreMap> implements DbAccess<StoreMap> { label?: string; }, txf: ( - tx: DbReadOnlyTransaction<StoreMap, StoreNames<StoreMap>[]>, + tx: DbReadOnlyTransaction<Stores, StoreNames<Stores>[]>, ) => Promise<T>, ): Promise<T> { const location = getCallerInfo(); @@ -201,12 +202,12 @@ export class ObservableDbAccess<StoreMap> implements DbAccess<StoreMap> { } } - async runReadWriteTx<T, StoreNameArray extends StoreNames<StoreMap>[]>( + async runReadWriteTx<T, StoreNameArray extends StoreNames<Stores>[]>( opts: { storeNames: StoreNameArray; label?: string; }, - txf: (tx: DbReadWriteTransaction<StoreMap, StoreNameArray>) => Promise<T>, + txf: (tx: DbReadWriteTransaction<Stores, StoreNameArray>) => Promise<T>, ): Promise<T> { const location = getCallerInfo(); this.oc.observe({ @@ -233,12 +234,12 @@ export class ObservableDbAccess<StoreMap> implements DbAccess<StoreMap> { } } - async runReadOnlyTx<T, StoreNameArray extends StoreNames<StoreMap>[]>( + async runReadOnlyTx<T, StoreNameArray extends StoreNames<Stores>[]>( opts: { storeNames: StoreNameArray; label?: string; }, - txf: (tx: DbReadOnlyTransaction<StoreMap, StoreNameArray>) => Promise<T>, + txf: (tx: DbReadOnlyTransaction<Stores, StoreNameArray>) => Promise<T>, ): Promise<T> { const location = getCallerInfo(); try { diff --git a/packages/taler-wallet-core/src/query.ts b/packages/taler-wallet-core/src/query.ts @@ -1,6 +1,7 @@ /* This file is part of TALER (C) 2016 GNUnet e.V. + (C) 2025 Taler Systems S.A. TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -344,7 +345,7 @@ export interface IndexDescriptor { } export interface StoreDescriptor<RecordType> { - _dummy: undefined & RecordType; + _dummy: RecordType; keyPath?: IDBKeyPath | IDBKeyPath[]; autoIncrement?: boolean; /** @@ -525,8 +526,8 @@ type DerefKeyPath<T, P> = P extends `${infer PX extends keyof T & KeyPathComponents}` ? T[PX] : P extends `${infer P0 extends keyof T & KeyPathComponents}.${infer Rest}` - ? DerefKeyPath<T[P0], Rest> - : unknown; + ? DerefKeyPath<T[P0], Rest> + : unknown; /** * Return a path if it is a valid dot-separate path to an object. @@ -536,8 +537,8 @@ type ValidateKeyPath<T, P> = P extends `${infer PX extends keyof T & KeyPathComponents}` ? PX : P extends `${infer P0 extends keyof T & KeyPathComponents}.${infer Rest}` - ? `${P0}.${ValidateKeyPath<T[P0], Rest>}` - : never; + ? `${P0}.${ValidateKeyPath<T[P0], Rest>}` + : never; // function foo<T, P>( // x: T, @@ -546,47 +547,20 @@ type ValidateKeyPath<T, P> = P extends `${infer PX extends keyof T & // foo({x: [0,1,2]}, "x.0"); -export type StoreNames<StoreMap> = StoreMap extends { - [P in keyof StoreMap]: StoreWithIndexes<infer SN1, infer SD1, infer IM1>; -} - ? keyof StoreMap - : unknown; - +export type StoreMap = { [P in string]: StoreWithIndexes<any, any, any> } +export type StoreNames<Stores extends StoreMap> = keyof Stores export type DbReadWriteTransaction< - StoreMap, - StoresArr extends Array<StoreNames<StoreMap>>, -> = StoreMap extends { - [P in string]: StoreWithIndexes<infer _SN1, infer _SD1, infer _IM1>; -} - ? { - [X in StoresArr[number] & - keyof StoreMap]: StoreMap[X] extends StoreWithIndexes< - infer _StoreName, - infer RecordType, - infer IndexMap - > - ? StoreReadWriteAccessor<RecordType, IndexMap> - : unknown; - } - : never; - + Stores extends StoreMap, + StoresArr extends Array<StoreNames<Stores>>, +> = { + [X in StoresArr[number]]: StoreReadWriteAccessor<Stores[X]['store']['_dummy'], Stores[X]['indexMap']> + } export type DbReadOnlyTransaction< - StoreMap, - StoresArr extends Array<StoreNames<StoreMap>>, -> = StoreMap extends { - [P in string]: StoreWithIndexes<infer _SN1, infer _SD1, infer _IM1>; -} - ? { - [X in StoresArr[number] & - keyof StoreMap]: StoreMap[X] extends StoreWithIndexes< - infer _StoreName, - infer RecordType, - infer IndexMap - > - ? StoreReadOnlyAccessor<RecordType, IndexMap> - : unknown; - } - : never; + Stores extends StoreMap, + StoresArr extends Array<StoreNames<Stores>>, +> = { + [X in StoresArr[number]]: StoreReadOnlyAccessor<Stores[X]['store']['_dummy'], Stores[X]['indexMap']> + } /** * Convert the type of an array to a union of the contents. @@ -847,7 +821,7 @@ function makeTxContext( /** * Handle for typed access to a database. */ -export interface DbAccess<StoreMap> { +export interface DbAccess<Stores extends StoreMap> { /** * The underlying IndexedDB database handle. * @@ -869,7 +843,7 @@ export interface DbAccess<StoreMap> { label?: string; }, txf: ( - tx: DbReadWriteTransaction<StoreMap, Array<StoreNames<StoreMap>>>, + tx: DbReadWriteTransaction<Stores, Array<StoreNames<Stores>>>, ) => Promise<T>, ): Promise<T>; @@ -886,7 +860,7 @@ export interface DbAccess<StoreMap> { label?: string; }, txf: ( - tx: DbReadOnlyTransaction<StoreMap, Array<StoreNames<StoreMap>>>, + tx: DbReadOnlyTransaction<Stores, Array<StoreNames<Stores>>>, ) => Promise<T>, ): Promise<T>; @@ -898,12 +872,12 @@ export interface DbAccess<StoreMap> { * Waiting for macrotasks results in an autocommit and * a subsequent exception thrown by this function. */ - runReadWriteTx<T, StoreNameArray extends Array<StoreNames<StoreMap>>>( + runReadWriteTx<T, StoreNameArray extends Array<StoreNames<Stores>>>( opts: { storeNames: StoreNameArray; label?: string; }, - txf: (tx: DbReadWriteTransaction<StoreMap, StoreNameArray>) => Promise<T>, + txf: (tx: DbReadWriteTransaction<Stores, StoreNameArray>) => Promise<T>, ): Promise<T>; /** @@ -914,12 +888,12 @@ export interface DbAccess<StoreMap> { * Waiting for macrotasks results in an autocommit and * a subsequent exception thrown by this function. */ - runReadOnlyTx<T, StoreNameArray extends Array<StoreNames<StoreMap>>>( + runReadOnlyTx<T, StoreNameArray extends Array<StoreNames<Stores>>>( opts: { storeNames: StoreNameArray; label?: string; }, - txf: (tx: DbReadOnlyTransaction<StoreMap, StoreNameArray>) => Promise<T>, + txf: (tx: DbReadOnlyTransaction<Stores, StoreNameArray>) => Promise<T>, ): Promise<T>; } @@ -987,13 +961,13 @@ class InternalTransactionContext { * * A store map is the metadata that describes the store. */ -export class DbAccessImpl<StoreMap> implements DbAccess<StoreMap> { +export class DbAccessImpl<Stores extends StoreMap> implements DbAccess<Stores> { constructor( private db: IDBDatabase, - private stores: StoreMap, + private stores: Stores, private triggers: TriggerSpec = {}, private cancellationToken: CancellationToken, - ) {} + ) { } idbHandle(): IDBDatabase { return this.db; @@ -1004,7 +978,7 @@ export class DbAccessImpl<StoreMap> implements DbAccess<StoreMap> { label?: string; }, txf: ( - tx: DbReadWriteTransaction<StoreMap, Array<StoreNames<StoreMap>>>, + tx: DbReadWriteTransaction<Stores, Array<StoreNames<Stores>>>, ) => Promise<T>, ): Promise<T> { this.cancellationToken.throwIfCancelled(); @@ -1033,7 +1007,7 @@ export class DbAccessImpl<StoreMap> implements DbAccess<StoreMap> { label?: string; }, txf: ( - tx: DbReadOnlyTransaction<StoreMap, Array<StoreNames<StoreMap>>>, + tx: DbReadOnlyTransaction<Stores, Array<StoreNames<Stores>>>, ) => Promise<T>, ): Promise<T> { this.cancellationToken.throwIfCancelled(); @@ -1058,11 +1032,11 @@ export class DbAccessImpl<StoreMap> implements DbAccess<StoreMap> { return res; } - async runReadWriteTx<T, StoreNameArray extends Array<StoreNames<StoreMap>>>( + async runReadWriteTx<T, StoreNameArray extends Array<StoreNames<Stores>>>( opts: { storeNames: StoreNameArray; }, - txf: (tx: DbReadWriteTransaction<StoreMap, StoreNameArray>) => Promise<T>, + txf: (tx: DbReadWriteTransaction<Stores, StoreNameArray>) => Promise<T>, ): Promise<T> { this.cancellationToken.throwIfCancelled(); const accessibleStores: { [x: string]: StoreWithIndexes<any, any, any> } = @@ -1086,11 +1060,11 @@ export class DbAccessImpl<StoreMap> implements DbAccess<StoreMap> { return res; } - async runReadOnlyTx<T, StoreNameArray extends Array<StoreNames<StoreMap>>>( + async runReadOnlyTx<T, StoreNameArray extends Array<StoreNames<Stores>>>( opts: { storeNames: StoreNameArray; }, - txf: (tx: DbReadOnlyTransaction<StoreMap, StoreNameArray>) => Promise<T>, + txf: (tx: DbReadOnlyTransaction<Stores, StoreNameArray>) => Promise<T>, ): Promise<T> { this.cancellationToken.throwIfCancelled(); const accessibleStores: { [x: string]: StoreWithIndexes<any, any, any> } =