@@ -4,6 +4,11 @@ import { getNestedValue, setNestedValue } from './dotprops';
44
55export type { SerializedValue } from './serde' ;
66
7+ /**
8+ * Mathematical operators supported by the KV math method
9+ */
10+ export type KvMathOperator = '+' | '-' | '*' | '/' | '^' | '%' ;
11+
712/**
813 * Configuration options for the KV store
914 */
@@ -309,6 +314,112 @@ export class KV implements Disposable, AsyncDisposable {
309314 }
310315 }
311316
317+ /**
318+ * Performs mathematical operations on numeric values in the KV store
319+ *
320+ * @param key - The key to perform math operation on (supports dot notation for nested properties)
321+ * @param operator - The mathematical operator to apply
322+ * @param value - The value to use in the operation
323+ * @returns The updated value after the mathematical operation
324+ * @throws Error if the existing value is not numeric or if the operation is invalid
325+ *
326+ * @example
327+ * ```typescript
328+ * // Initialize a counter
329+ * kv.set('counter', 10);
330+ *
331+ * // Increment by 5
332+ * const result1 = kv.math('counter', '+', 5); // 15
333+ *
334+ * // Multiply by 2
335+ * const result2 = kv.math('counter', '*', 2); // 30
336+ *
337+ * // Use with bigint
338+ * kv.set('big_counter', BigInt(1000));
339+ * const result3 = kv.math('big_counter', '+', BigInt(500)); // 1500n
340+ *
341+ * // Use with dot notation
342+ * kv.set('user:123', { score: 100, level: 5 });
343+ * const result4 = kv.math('user:123.score', '+', 50); // 150
344+ * ```
345+ */
346+ public math (
347+ key : string ,
348+ operator : KvMathOperator ,
349+ value : number | bigint ,
350+ ) : number | bigint {
351+ const existingValue = this . get ( key ) ;
352+
353+ if ( existingValue === undefined ) {
354+ throw new Error ( `Key '${ key } ' does not exist` ) ;
355+ }
356+
357+ if (
358+ typeof existingValue !== 'number' &&
359+ typeof existingValue !== 'bigint'
360+ ) {
361+ throw new Error (
362+ `Value at key '${ key } ' is not numeric. Expected number or bigint, got ${ typeof existingValue } ` ,
363+ ) ;
364+ }
365+
366+ // Handle mixed number/bigint operations by converting to bigint
367+ const isBigIntOperation =
368+ typeof existingValue === 'bigint' || typeof value === 'bigint' ;
369+
370+ const existing = isBigIntOperation
371+ ? typeof existingValue === 'bigint'
372+ ? existingValue
373+ : BigInt ( existingValue )
374+ : ( existingValue as number ) ;
375+ const operand = isBigIntOperation
376+ ? typeof value === 'bigint'
377+ ? value
378+ : BigInt ( value )
379+ : ( value as number ) ;
380+
381+ let result : number | bigint ;
382+
383+ switch ( operator ) {
384+ case '+' :
385+ result = ( existing as any ) + ( operand as any ) ;
386+ break ;
387+ case '-' :
388+ result = ( existing as any ) - ( operand as any ) ;
389+ break ;
390+ case '*' :
391+ result = ( existing as any ) * ( operand as any ) ;
392+ break ;
393+ case '/' :
394+ if ( operand === 0 || operand === 0n ) {
395+ throw new Error ( 'Division by zero' ) ;
396+ }
397+ result = ( existing as any ) / ( operand as any ) ;
398+ break ;
399+ case '^' :
400+ if ( isBigIntOperation && operand < 0n ) {
401+ throw new Error (
402+ 'Exponentiation with negative exponent is not supported for bigint' ,
403+ ) ;
404+ }
405+ result = ( existing as any ) ** ( operand as any ) ;
406+ break ;
407+ case '%' :
408+ if ( operand === 0 || operand === 0n ) {
409+ throw new Error ( 'Modulo by zero' ) ;
410+ }
411+ result = ( existing as any ) % ( operand as any ) ;
412+ break ;
413+ default :
414+ throw new Error ( `Invalid operator: ${ operator } ` ) ;
415+ }
416+
417+ // Update the value in the store
418+ this . set ( key , result ) ;
419+
420+ return result ;
421+ }
422+
312423 /**
313424 * Sets expiration for an existing key
314425 *
0 commit comments