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':