diff --git a/apps/website/docs/api-reference/commandkit/classes/kv.mdx b/apps/website/docs/api-reference/commandkit/classes/kv.mdx
index 8b50c341..5a59d1c3 100644
--- a/apps/website/docs/api-reference/commandkit/classes/kv.mdx
+++ b/apps/website/docs/api-reference/commandkit/classes/kv.mdx
@@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
## KV
-
+
A key-value store implementation using SQLite
@@ -56,6 +56,7 @@ class KV implements Disposable, AsyncDisposable {
get(key: string) => T | undefined;
set(key: string, value: any) => void;
setex(key: string, value: any, ttl: number) => void;
+ math(key: string, operator: KvMathOperator, value: number | bigint) => number | bigint;
expire(key: string, ttl: number) => boolean;
ttl(key: string) => number;
delete(key: string) => void;
@@ -186,6 +187,34 @@ kv.setex('temp:data', { cached: true, timestamp: Date.now() }, 5 * 60 * 1000);
// Use dot notation with expiration
kv.setex('user:123.temp_settings', { theme: 'light' }, 30 * 60 * 1000);
```
+### math
+
+KvMathOperator, value: number | bigint) => number | bigint`} />
+
+Performs mathematical operations on numeric values in the KV store
+
+
+
+*Example*
+
+```typescript
+// Initialize a counter
+kv.set('counter', 10);
+
+// Increment by 5
+const result1 = kv.math('counter', '+', 5); // 15
+
+// Multiply by 2
+const result2 = kv.math('counter', '*', 2); // 30
+
+// Use with bigint
+kv.set('big_counter', BigInt(1000));
+const result3 = kv.math('big_counter', '+', BigInt(500)); // 1500n
+
+// Use with dot notation
+kv.set('user:123', { score: 100, level: 5 });
+const result4 = kv.math('user:123.score', '+', 50); // 150
+```
### expire
boolean`} />
diff --git a/apps/website/docs/api-reference/commandkit/functions/open-kv.mdx b/apps/website/docs/api-reference/commandkit/functions/open-kv.mdx
index 45a7eb2c..c9cd9d63 100644
--- a/apps/website/docs/api-reference/commandkit/functions/open-kv.mdx
+++ b/apps/website/docs/api-reference/commandkit/functions/open-kv.mdx
@@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
## openKV
-
+
Opens a new KV instance
diff --git a/apps/website/docs/api-reference/commandkit/interfaces/kv-options.mdx b/apps/website/docs/api-reference/commandkit/interfaces/kv-options.mdx
index babdad8f..ee84e053 100644
--- a/apps/website/docs/api-reference/commandkit/interfaces/kv-options.mdx
+++ b/apps/website/docs/api-reference/commandkit/interfaces/kv-options.mdx
@@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
## KvOptions
-
+
Configuration options for the KV store
diff --git a/apps/website/docs/api-reference/commandkit/types/kv-math-operator.mdx b/apps/website/docs/api-reference/commandkit/types/kv-math-operator.mdx
new file mode 100644
index 00000000..598be814
--- /dev/null
+++ b/apps/website/docs/api-reference/commandkit/types/kv-math-operator.mdx
@@ -0,0 +1,22 @@
+---
+title: "KvMathOperator"
+isDefaultIndex: false
+generated: true
+---
+
+import MemberInfo from '@site/src/components/MemberInfo';
+import GenerationInfo from '@site/src/components/GenerationInfo';
+import MemberDescription from '@site/src/components/MemberDescription';
+
+
+
+
+## KvMathOperator
+
+
+
+Mathematical operators supported by the KV math method
+
+```ts title="Signature"
+type KvMathOperator = '+' | '-' | '*' | '/' | '^' | '%'
+```
diff --git a/apps/website/docs/guide/15-key-value-store/01-introduction.mdx b/apps/website/docs/guide/15-key-value-store/01-introduction.mdx
index 39361952..897e6429 100644
--- a/apps/website/docs/guide/15-key-value-store/01-introduction.mdx
+++ b/apps/website/docs/guide/15-key-value-store/01-introduction.mdx
@@ -20,7 +20,7 @@ The CommandKit Key-Value (KV) store provides a simple, persistent storage soluti
## Quick Start
```typescript
-import { KV } from '@commandkit/kv';
+import { KV } from 'commandkit/kv';
// Create a new KV store
const kv = new KV('data.db');
@@ -65,7 +65,7 @@ The KV store supports storing and retrieving the following data types:
The KV store is included with CommandKit. No additional installation is required.
```bash
-npm install @commandkit/kv
+npm install commandkit/kv
```
## Next Steps
diff --git a/apps/website/docs/guide/15-key-value-store/02-basic-operations.mdx b/apps/website/docs/guide/15-key-value-store/02-basic-operations.mdx
index 11e947b6..af369105 100644
--- a/apps/website/docs/guide/15-key-value-store/02-basic-operations.mdx
+++ b/apps/website/docs/guide/15-key-value-store/02-basic-operations.mdx
@@ -10,7 +10,7 @@ This guide covers the fundamental operations of the CommandKit KV store, includi
## Creating a KV Store
```typescript
-import { KV, openKV } from '@commandkit/kv';
+import { KV, openKV } from 'commandkit/kv';
// Create with custom database file
const kv = new KV('my-bot-data.db');
diff --git a/apps/website/ignore_build.js b/apps/website/ignore_build.js
index e0eeb352..da97b082 100644
--- a/apps/website/ignore_build.js
+++ b/apps/website/ignore_build.js
@@ -2,33 +2,37 @@ const { execSync } = require('child_process');
// Check if there are changes in the website directory
function hasWebsiteChanges() {
- try {
- // Get the list of changed files in the current commit compared to the base branch
- const baseBranch = process.env.BASE_BRANCH || 'main';
- const changedFiles = execSync(`git diff --name-only origin/${baseBranch}...HEAD`, { encoding: 'utf8' });
+ try {
+ // Get the list of changed files in the current commit compared to the base branch
+ const baseBranch = process.env.BASE_BRANCH || 'main';
+ const changedFiles = execSync(
+ `git diff --name-only origin/${baseBranch}...HEAD`,
+ { encoding: 'utf8' },
+ );
- // Check if any of the changed files are in the website directory
- const websiteChanges = changedFiles
- .split('\n')
- .filter(file => file.trim() && file.startsWith('apps/website/'));
+ // Check if any of the changed files are in the website directory
+ const websiteChanges = changedFiles
+ .split('\n')
+ .filter((file) => file.trim() && file.startsWith('apps/website/'));
- return websiteChanges.length > 0;
- } catch (error) {
- // If we can't determine changes, allow the build to proceed
- console.log('Could not determine changes, allowing build to proceed');
- return true;
- }
+ return websiteChanges.length > 0;
+ } catch (error) {
+ // If we can't determine changes, allow the build to proceed
+ console.log('Could not determine changes, allowing build to proceed');
+ return true;
+ }
}
// Ignore build if:
// 1. Branch is owned by renovate, OR
// 2. No changes in website directory
-const shouldIgnore = process.env.BRANCH?.includes('renovate') || !hasWebsiteChanges();
+const shouldIgnore =
+ process.env.BRANCH?.includes('renovate') || !hasWebsiteChanges();
process.exitCode = shouldIgnore ? 0 : 1;
if (shouldIgnore) {
- console.log('Build ignored: No relevant changes detected');
+ console.log('Build ignored: No relevant changes detected');
} else {
- console.log('Build proceeding: Website changes detected');
-}
\ No newline at end of file
+ console.log('Build proceeding: Website changes detected');
+}
diff --git a/apps/website/src/plugins/llms-txt.js b/apps/website/src/plugins/llms-txt.js
index 8ce12bc1..e1ac3378 100644
--- a/apps/website/src/plugins/llms-txt.js
+++ b/apps/website/src/plugins/llms-txt.js
@@ -29,7 +29,8 @@ module.exports = function (context) {
entry.name.endsWith('.md')
) {
const content = await fs.promises.readFile(fullPath, 'utf8');
- const { data: frontmatter, content: markdownContent } = matter(content);
+ const { data: frontmatter, content: markdownContent } =
+ matter(content);
// Get relative path from guide directory
const relativePath = path
diff --git a/packages/commandkit/package.json b/packages/commandkit/package.json
index 2ef3acd7..efcc8f39 100644
--- a/packages/commandkit/package.json
+++ b/packages/commandkit/package.json
@@ -174,7 +174,7 @@
"picocolors": "^1.1.1",
"rfdc": "^1.3.1",
"rimraf": "^6.0.0",
- "tsdown": "^0.13.0",
+ "tsdown": "^0.13.1",
"use-macro": "^1.1.0"
},
"devDependencies": {
diff --git a/packages/commandkit/src/kv/kv.ts b/packages/commandkit/src/kv/kv.ts
index eb58cb9b..fc01f085 100644
--- a/packages/commandkit/src/kv/kv.ts
+++ b/packages/commandkit/src/kv/kv.ts
@@ -4,6 +4,11 @@ import { getNestedValue, setNestedValue } from './dotprops';
export type { SerializedValue } from './serde';
+/**
+ * Mathematical operators supported by the KV math method
+ */
+export type KvMathOperator = '+' | '-' | '*' | '/' | '^' | '%';
+
/**
* Configuration options for the KV store
*/
@@ -309,6 +314,112 @@ export class KV implements Disposable, AsyncDisposable {
}
}
+ /**
+ * Performs mathematical operations on numeric values in the KV store
+ *
+ * @param key - The key to perform math operation on (supports dot notation for nested properties)
+ * @param operator - The mathematical operator to apply
+ * @param value - The value to use in the operation
+ * @returns The updated value after the mathematical operation
+ * @throws Error if the existing value is not numeric or if the operation is invalid
+ *
+ * @example
+ * ```typescript
+ * // Initialize a counter
+ * kv.set('counter', 10);
+ *
+ * // Increment by 5
+ * const result1 = kv.math('counter', '+', 5); // 15
+ *
+ * // Multiply by 2
+ * const result2 = kv.math('counter', '*', 2); // 30
+ *
+ * // Use with bigint
+ * kv.set('big_counter', BigInt(1000));
+ * const result3 = kv.math('big_counter', '+', BigInt(500)); // 1500n
+ *
+ * // Use with dot notation
+ * kv.set('user:123', { score: 100, level: 5 });
+ * const result4 = kv.math('user:123.score', '+', 50); // 150
+ * ```
+ */
+ public math(
+ key: string,
+ operator: KvMathOperator,
+ value: number | bigint,
+ ): number | bigint {
+ const existingValue = this.get(key);
+
+ if (existingValue === undefined) {
+ throw new Error(`Key '${key}' does not exist`);
+ }
+
+ if (
+ typeof existingValue !== 'number' &&
+ typeof existingValue !== 'bigint'
+ ) {
+ throw new Error(
+ `Value at key '${key}' is not numeric. Expected number or bigint, got ${typeof existingValue}`,
+ );
+ }
+
+ // Handle mixed number/bigint operations by converting to bigint
+ const isBigIntOperation =
+ typeof existingValue === 'bigint' || typeof value === 'bigint';
+
+ const existing = isBigIntOperation
+ ? typeof existingValue === 'bigint'
+ ? existingValue
+ : BigInt(existingValue)
+ : (existingValue as number);
+ const operand = isBigIntOperation
+ ? typeof value === 'bigint'
+ ? value
+ : BigInt(value)
+ : (value as number);
+
+ let result: number | bigint;
+
+ switch (operator) {
+ case '+':
+ result = (existing as any) + (operand as any);
+ break;
+ case '-':
+ result = (existing as any) - (operand as any);
+ break;
+ case '*':
+ result = (existing as any) * (operand as any);
+ break;
+ case '/':
+ if (operand === 0 || operand === 0n) {
+ throw new Error('Division by zero');
+ }
+ result = (existing as any) / (operand as any);
+ break;
+ case '^':
+ if (isBigIntOperation && operand < 0n) {
+ throw new Error(
+ 'Exponentiation with negative exponent is not supported for bigint',
+ );
+ }
+ result = (existing as any) ** (operand as any);
+ break;
+ case '%':
+ if (operand === 0 || operand === 0n) {
+ throw new Error('Modulo by zero');
+ }
+ result = (existing as any) % (operand as any);
+ break;
+ default:
+ throw new Error(`Invalid operator: ${operator}`);
+ }
+
+ // Update the value in the store
+ this.set(key, result);
+
+ return result;
+ }
+
/**
* Sets expiration for an existing key
*
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 90795327..0c9a87d3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -275,7 +275,7 @@ importers:
specifier: ^6.0.0
version: 6.0.1
tsdown:
- specifier: ^0.13.0
+ specifier: ^0.13.1
version: 0.13.1(typescript@5.9.2)
use-macro:
specifier: ^1.1.0
@@ -10981,7 +10981,7 @@ snapshots:
'@docusaurus/react-loadable@6.0.0(react@19.1.1)':
dependencies:
- '@types/react': 19.1.8
+ '@types/react': 19.1.9
react: 19.1.1
'@docusaurus/remark-plugin-npm2yarn@3.8.1':