@@ -4,6 +4,11 @@ import { getNestedValue, setNestedValue } from './dotprops';
4
4
5
5
export type { SerializedValue } from './serde' ;
6
6
7
+ /**
8
+ * Mathematical operators supported by the KV math method
9
+ */
10
+ export type KvMathOperator = '+' | '-' | '*' | '/' | '^' | '%' ;
11
+
7
12
/**
8
13
* Configuration options for the KV store
9
14
*/
@@ -309,6 +314,112 @@ export class KV implements Disposable, AsyncDisposable {
309
314
}
310
315
}
311
316
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
+
312
423
/**
313
424
* Sets expiration for an existing key
314
425
*
0 commit comments