Skip to content

Conversation

@pedroscosta
Copy link
Contributor

@pedroscosta pedroscosta commented Oct 27, 2025

This PR aims to add a Kysely Postgres adapter:

  • Adds a Kysely adapter, based on the existing Drizzle adapter
  • Adds test for the new adapter, based on the existing Drizzle adapter's test file

Quick note: even tough Kysely supports many SQL dialects, this implementation only covers Postgres based adapters, because Kysely doesn't provide dialect agnostic JSON operations. This implementation could be expanded to accept other dialects (like MySQL, MSSQL, SQLite)


Summary by cubic

Adds a Kysely Postgres adapter for API key storage with JSONB metadata, matching the Drizzle adapter’s API. Includes a full test suite and a new package export for easy use.

  • New Features

    • KyselyStore with save/find/update/delete methods.
    • Owner and tag queries using JSONB (@>), tags use OR logic; optional owner filter.
    • Postgres-only implementation due to Kysely’s JSON operations.
    • New export path: "keypal/kysely" and test script: "test:kysely".
  • Migration

    • Ensure a Postgres table (e.g., "apikey") with:
      • id TEXT PRIMARY KEY
      • key_hash TEXT (unique index recommended)
      • metadata JSONB
    • Import from "keypal/kysely" and pass your Kysely instance and table name.

Summary by CodeRabbit

  • New Features

    • Added a PostgreSQL-backed storage adapter with advanced querying, metadata merge/update, and full CRUD for API keys.
  • Tests

    • Added comprehensive PostgreSQL test suite covering CRUD, metadata edge cases, concurrency, large-data scenarios, and adjusted existing DB test configurations.
  • Chores

    • Exposed new public export for the adapter, added optional Postgres dependency, added test script, and updated build, CI workflows, and local Docker services for multi-database testing.

@coderabbitai
Copy link

coderabbitai bot commented Oct 27, 2025

Caution

Review failed

The pull request is closed.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a Kysely-based PostgreSQL storage adapter and tests, updates package/build/docker/CI configs to include Kysely (and separate DB services for Kysely/Drizzle/Prisma), exposes the adapter via package exports with an optional Kysely dependency, and adjusts existing test DB endpoints and cleanup logic.

Changes

Cohort / File(s) Summary
Package config
package.json
Added "./kysely" export entry; added "test:kysely" script; added "kysely": "^0.28.8" to optionalDependencies.
Build config
build.config.ts
Included src/storage/kysely.ts in build entries; added kysely and pg to externals.
Storage adapter implementation
src/storage/kysely.ts
New ApiKeysDatabase type and exported KyselyStore implementing Storage with save/find/update/delete methods; uses JSONB containment queries for owner/tags and provides row↔record helpers.
Storage adapter tests
src/storage/kysely.test.ts
New comprehensive test suite for KyselyStore covering CRUD, metadata merging, tag/owner queries, concurrency, edge-cases, large payloads, and cleanup.
CI / Docker
.github/workflows/test.yml, docker-compose.yml
Added three Postgres services (postgres-kysely, postgres-drizzle, postgres-prisma) with distinct ports/databases; updated workflow to run Kysely and Drizzle tests against their DBs; adjusted Prisma steps to use its dedicated DB.
Other tests adjusted
src/storage/drizzle.test.ts, src/storage/prisma.test.ts
Updated connection ports/databases (drizzle -> port 5434/db keypal_drizzle; prisma -> port 5435/db keypal_prisma); prisma tests simplified cleanup/disconnect logic.
Docs / guidance
FEATURE_SUGGESTIONS.md, .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc
Minor documentation/example change to use a template handle; removed guidance line recommending Bun in one rule file.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant KyselyStore
    participant Kysely
    participant Postgres

    rect rgb(235,245,255)
    Client->>KyselyStore: save(record)
    KyselyStore->>Kysely: insert row (toRow)
    Kysely->>Postgres: INSERT INTO apikey
    Postgres-->>Kysely: OK
    Kysely-->>KyselyStore: result
    KyselyStore-->>Client: void
    end

    rect rgb(245,255,235)
    Client->>KyselyStore: findByTags(tags, ownerId?)
    KyselyStore->>Kysely: SELECT WHERE metadata @> {...} OR tags match
    Kysely->>Postgres: SELECT ... JSONB containment
    Postgres-->>Kysely: rows[]
    Kysely-->>KyselyStore: rows[]
    KyselyStore->>Client: ApiKeyRecord[]
    end

    rect rgb(255,245,235)
    Client->>KyselyStore: updateMetadata(id, partialMetadata)
    KyselyStore->>Kysely: SELECT by id (existence)
    Kysely->>Postgres: SELECT
    alt exists
        KyselyStore->>Kysely: UPDATE metadata = merged
        Kysely->>Postgres: UPDATE
        Postgres-->>Kysely: OK
        KyselyStore-->>Client: void
    else not found
        KyselyStore-->>Client: Error (not found)
    end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas warranting extra attention:
    • JSONB containment queries and index usage (metadata @> ...) in src/storage/kysely.ts.
    • Correct typing and mapping in toRecord / toRow helpers.
    • Metadata merge semantics and existence checks in updateMetadata.
    • Isolation, cleanup, and concurrency patterns in src/storage/kysely.test.ts.
    • CI/docker changes wiring ports and databases in .github/workflows/test.yml and docker-compose.yml.

Possibly related PRs

Poem

🐰
I hopped through schemas, dug JSONB beds at night,
Indexed the carrots and kept tags in sight.
Concurrent hops and deletes gave chase,
Tests kept my burrow tidy, safe, and ace.
A crunchy carrot commit — code done right! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "feat: add kysely postgres adapter" accurately and directly reflects the primary change in the changeset. The PR introduces a new Kysely-based PostgreSQL storage adapter implementation (KyselyStore class) along with its corresponding test suite, package exports, and supporting infrastructure changes. The title is concise, clear, and uses conventional commit format, making it immediately understandable to someone scanning the repository history that this PR adds a new storage adapter capability.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63e5f9a and ab2f374.

📒 Files selected for processing (1)
  • src/storage/kysely.test.ts (1 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (9)
package.json (1)

74-74: Use bun test instead of vitest for the Kysely test script

Per project rules, prefer Bun’s test runner.

As per coding guidelines / Based on learnings

-    "test:kysely": "vitest run src/storage/kysely.test.ts",
+    "test:kysely": "bun test src/storage/kysely.test.ts",
src/storage/kysely.ts (1)

99-109: Add a GIN index on metadata for JSONB containment queries

metadata @> ... benefits significantly from a GIN index for owner/tag scans.

Optionally document/ensure:

CREATE INDEX IF NOT EXISTS apikey_metadata_gin_idx
  ON apikey USING GIN (metadata jsonb_path_ops);

or without jsonb_path_ops depending on query mix.

src/storage/kysely.test.ts (7)

3-3: Use Bun’s test runner instead of Vitest

Switch imports to "bun:test" to align with project rules.

As per coding guidelines / Based on learnings

-import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
+import { afterAll, afterEach, beforeAll, describe, expect, it } from "bun:test";

23-27: Skip the suite when DATABASE_URL is missing (avoid failing locally/CI without Postgres)

Gate describe to skip cleanly instead of warning/throwing later.

-describe("KyselyStore", () => {
+const describePg = process.env.DATABASE_URL ? describe : describe.skip;
+describePg("KyselyStore", () => {

As per coding guidelines (don’t use console).


36-44: Remove console.warn and avoid throwing in setup for “skipped” runs

With the gated describe above, this try/catch and console usage can be dropped.

-    try {
-      await pool.query("SELECT 1");
-    } catch (error) {
-      console.warn(
-        "PostgreSQL not available. Skipping Kysely tests. Start with: docker-compose up postgres"
-      );
-      throw error;
-    }
+    await pool.query("SELECT 1");

57-64: Drop redundant non-unique index; keep only the UNIQUE index on key_hash

The unique index already provides an index on key_hash; maintaining both increases write overhead.

-    await sql`
-      CREATE INDEX IF NOT EXISTS apikey_key_hash_idx ON apikey(key_hash)
-    `.execute(db);
-
     await sql`
       CREATE UNIQUE INDEX IF NOT EXISTS apikey_key_hash_unique ON apikey(key_hash)
     `.execute(db);

624-639: Avoid awaiting sequentially in loops; parallelize to speed up tests

This block can run in parallel safely.

-    const longDescription = "A".repeat(LONG_DESCRIPTION_LENGTH);
-
-    const { key, record } = await keys.create({
-      ownerId: "user_long_description",
-      description: longDescription,
-    });
+    const longDescription = "A".repeat(LONG_DESCRIPTION_LENGTH);
+    const { key, record } = await keys.create({
+      ownerId: "user_long_description",
+      description: longDescription,
+    });

And for other places like Lines 645-651 and 1194-1199, batch with Promise.all(...) instead of for/await loops to reduce test time. As per coding guidelines.


198-201: Prefer at() over bracket indexing for arrays

Use .at(0) for clarity and to follow local rules.

Examples:

- expect(found[0]?.id).toBe(record.id);
+ expect(found.at(0)?.id).toBe(record.id);

As per coding guidelines.

Also applies to: 232-235, 248-251


1-2: Consider replacing pg with a Bun-compatible approach (future work)

Guidelines prefer Bun.sql over pg/postgres.js. Kysely’s PostgresDialect typically uses pg or postgres—evaluate a Bun-compatible dialect (e.g., community Bun dialect for Kysely) or an adapter over Bun.sql in a follow-up.

If you want, I can draft a spike plan to prototype a Bun.sql-backed Kysely dialect and migration steps.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bdb7eb0 and cb154e8.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • package.json (3 hunks)
  • src/storage/kysely.test.ts (1 hunks)
  • src/storage/kysely.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
{**/*.{ts,tsx,js,jsx},package.json}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

{**/*.{ts,tsx,js,jsx},package.json}: Use bun <file> instead of node <file> or ts-node <file>
Use Bun.serve() for HTTP/WebSocket servers; do not use express
Use bun:sqlite for SQLite; do not use better-sqlite3
Use Bun.redis for Redis; do not use ioredis
Use Bun.sql for Postgres; do not use pg or postgres.js
Use the built-in WebSocket; do not use ws
Use Bun.$ shell (e.g., Bun.$ls) instead of execa

Files:

  • package.json
  • src/storage/kysely.test.ts
  • src/storage/kysely.ts
{**/*.test.{ts,tsx,js,jsx},package.json}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

{**/*.test.{ts,tsx,js,jsx},package.json}: Use bun test instead of jest or vitest
Use bun test to run tests

Files:

  • package.json
  • src/storage/kysely.test.ts
{**/*.{html,ts,css},package.json}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun build <file.html|file.ts|file.css> instead of webpack or esbuild

Files:

  • package.json
  • src/storage/kysely.test.ts
  • src/storage/kysely.ts
package.json

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

package.json: Use bun install instead of npm install, yarn install, or pnpm install
Use bun run <script> instead of npm run <script>, yarn run <script>, or pnpm run <script>

Files:

  • package.json
{**/*.html,package.json}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use HTML imports with Bun.serve() for frontend; do not use vite

Files:

  • package.json
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{ts,tsx,js,jsx}: Don't use consecutive spaces in regular expression literals
Don't use the comma operator
Avoid functions exceeding configured Cognitive Complexity
Don't use unnecessary boolean casts
Don't use unnecessary callbacks with flatMap
Prefer for...of statements over Array.forEach
Don't create classes that only have static members
Don't use this and super in static contexts
Don't use unnecessary catch clauses
Don't use unnecessary constructors
Don't use unnecessary continue statements
Don't export empty modules
Don't use unnecessary escape sequences in regex literals
Don't use unnecessary labels
Don't use unnecessary nested block statements
Don't rename imports/exports/destructured assignments to the same name
Avoid unnecessary string or template literal concatenation
Don't use String.raw in template literals without escapes
Don't use useless case statements in switch
Avoid ternary operators when simpler alternatives exist
Don't use useless this aliasing
Don't initialize variables to undefined
Don't use the void operator
Prefer arrow functions over function expressions
Use Date.now() for epoch milliseconds
Prefer .flatMap() over map().flat() when possible
Use literal property access instead of computed when possible
Don't use parseInt/Number.parseInt for binary, octal, or hex literals
Use optional chaining instead of chained logical expressions
Prefer regex literals over RegExp constructor when possible
Don't use non-decimal or underscored number literal member names
Remove redundant terms from logical expressions
Use while loops when for initial/update are not needed
Don't reassign const variables
Don't use constant expressions in conditions
Avoid Math.min/Math.max to clamp when result is constant
Don't return a value from a constructor
Don't use empty character classes in regex literals
Don't use empty destructuring patterns
Don't call global object properties as functions
Don't declare functions/vars that escape their block (use block scoping prop...

Files:

  • src/storage/kysely.test.ts
  • src/storage/kysely.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{ts,tsx}: Don't use the arguments object
Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use any or unknown as type constraints
Don't return a value from a function with return type void
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables/params/class properties initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with ! postfix
Don't use parameter properties in class constructors
Don't use user-defined types (type aliases) where discouraged
Prefer as const over literal types/type annotations for literals
Use either T[] or Array consistently
Initialize each enum member value explicitly
Use export type for types
Use import type for types
Ensure all enum members are literal values
Don't use const enum
Don't declare empty interfaces
Don't let variables evolve into any through reassignments
Don't use the any type
Don't misuse non-null assertion operator in TS files
Don't use implicit any in variable declarations
Don't merge interfaces and classes unsafely
Place overload signatures adjacent to each other
Use namespace keyword instead of module to declare TS namespaces
Don't use void type outside generics or return types

Files:

  • src/storage/kysely.test.ts
  • src/storage/kysely.ts
**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{test,spec}.{js,jsx,ts,tsx}: Don't nest describe() blocks too deeply in test files
Don't use callbacks in asynchronous tests and hooks
Don't have duplicate hooks in describe blocks
Don't use export or module.exports in test files
Don't use focused tests
Ensure assertions (e.g., expect) are placed inside an it() call
Don't use disabled tests

Files:

  • src/storage/kysely.test.ts
**/*.{jsx,tsx,js,ts}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx,js,ts}: Ensure preconnect is used with Google Fonts
Use a recommended display strategy with Google Fonts

Files:

  • src/storage/kysely.test.ts
  • src/storage/kysely.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

Don't import next/document outside of pages/_document.jsx

Files:

  • src/storage/kysely.test.ts
  • src/storage/kysely.ts
🧠 Learnings (2)
📚 Learning: 2025-10-25T11:54:06.273Z
Learnt from: CR
PR: izadoesdev/keypal#0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-10-25T11:54:06.273Z
Learning: Applies to {**/*.test.{ts,tsx,js,jsx},package.json} : Use `bun test` instead of `jest` or `vitest`

Applied to files:

  • package.json
📚 Learning: 2025-10-25T11:54:06.273Z
Learnt from: CR
PR: izadoesdev/keypal#0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-10-25T11:54:06.273Z
Learning: Applies to {**/*.test.{ts,tsx,js,jsx},package.json} : Use `bun test` to run tests

Applied to files:

  • package.json
🧬 Code graph analysis (1)
src/storage/kysely.test.ts (1)
src/storage/kysely.ts (2)
  • ApiKeysDatabase (8-14)
  • KyselyStore (39-176)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: cubic · AI code reviewer
🔇 Additional comments (2)
package.json (2)

35-39: Export entry for Kysely looks good

Paths align with dist layout used by other storage adapters.


112-114: Optional dependency additions look fine

Adding "@prisma/client" and "kysely" as optional aligns with pluggable storage approach.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 4 files

Prompt for AI agents (all 1 issues)

Understand the root cause of the following 1 issues and fix them.


<file name="src/storage/kysely.ts">

<violation number="1" location="src/storage/kysely.ts:127">
The OR clause for tag filters wraps each `metadata @&gt; …` condition in `IS NOT NULL`, so the predicate always evaluates to true and the query returns every API key instead of filtering by the requested tags.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Ask questions if you need clarification on any suggestion

React with 👍 or 👎 to teach cubic. Mention @cubic-dev-ai to give feedback, ask questions, or re-run the review.


// biome-ignore lint/suspicious/noExplicitAny: Kysely or/eb types are complex
query = query.where(({ or, eb }: any) =>
or(tagConditions.map((condition: any) => eb(condition, "is not", null)))
Copy link

@cubic-dev-ai cubic-dev-ai bot Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OR clause for tag filters wraps each metadata @> … condition in IS NOT NULL, so the predicate always evaluates to true and the query returns every API key instead of filtering by the requested tags.

Prompt for AI agents
Address the following comment on src/storage/kysely.ts at line 127:

<comment>The OR clause for tag filters wraps each `metadata @&gt; …` condition in `IS NOT NULL`, so the predicate always evaluates to true and the query returns every API key instead of filtering by the requested tags.</comment>

<file context>
@@ -0,0 +1,177 @@
+
+			// biome-ignore lint/suspicious/noExplicitAny: Kysely or/eb types are complex
+			query = query.where(({ or, eb }: any) =&gt;
+				or(tagConditions.map((condition: any) =&gt; eb(condition, &quot;is not&quot;, null)))
+			);
+		}
</file context>

✅ Addressed in c2d2014

@izadoesdev izadoesdev merged commit bccf0b5 into izadoesdev:main Oct 27, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/storage/kysely.test.ts (1)

1176-1176: Consider avoiding empty string fallback in test assertions.

Using || "" as a fallback when records[i]?.id is undefined passes an empty string to methods that expect valid IDs. While safe (returns null), this pattern could mask test logic issues.

Consider using more explicit guards:

-const updated = await store.findById(records[0]?.id || "");
+const record = records[0];
+expect(record).toBeDefined();
+const updated = await store.findById(record!.id);
-store.delete(records[i]?.id || "")
+const record = records[i];
+if (!record) throw new Error("Record not found");
+store.delete(record.id)

Also applies to: 1182-1182

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb154e8 and 63e5f9a.

📒 Files selected for processing (9)
  • .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc (0 hunks)
  • .github/workflows/test.yml (2 hunks)
  • FEATURE_SUGGESTIONS.md (1 hunks)
  • build.config.ts (2 hunks)
  • docker-compose.yml (2 hunks)
  • src/storage/drizzle.test.ts (1 hunks)
  • src/storage/kysely.test.ts (1 hunks)
  • src/storage/kysely.ts (1 hunks)
  • src/storage/prisma.test.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{ts,tsx,js,jsx}: Don't use consecutive spaces in regular expression literals
Don't use the comma operator
Avoid functions exceeding configured Cognitive Complexity
Don't use unnecessary boolean casts
Don't use unnecessary callbacks with flatMap
Prefer for...of statements over Array.forEach
Don't create classes that only have static members
Don't use this and super in static contexts
Don't use unnecessary catch clauses
Don't use unnecessary constructors
Don't use unnecessary continue statements
Don't export empty modules
Don't use unnecessary escape sequences in regex literals
Don't use unnecessary labels
Don't use unnecessary nested block statements
Don't rename imports/exports/destructured assignments to the same name
Avoid unnecessary string or template literal concatenation
Don't use String.raw in template literals without escapes
Don't use useless case statements in switch
Avoid ternary operators when simpler alternatives exist
Don't use useless this aliasing
Don't initialize variables to undefined
Don't use the void operator
Prefer arrow functions over function expressions
Use Date.now() for epoch milliseconds
Prefer .flatMap() over map().flat() when possible
Use literal property access instead of computed when possible
Don't use parseInt/Number.parseInt for binary, octal, or hex literals
Use optional chaining instead of chained logical expressions
Prefer regex literals over RegExp constructor when possible
Don't use non-decimal or underscored number literal member names
Remove redundant terms from logical expressions
Use while loops when for initial/update are not needed
Don't reassign const variables
Don't use constant expressions in conditions
Avoid Math.min/Math.max to clamp when result is constant
Don't return a value from a constructor
Don't use empty character classes in regex literals
Don't use empty destructuring patterns
Don't call global object properties as functions
Don't declare functions/vars that escape their block (use block scoping prop...

Files:

  • src/storage/prisma.test.ts
  • src/storage/drizzle.test.ts
  • src/storage/kysely.test.ts
  • src/storage/kysely.ts
  • build.config.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{ts,tsx}: Don't use the arguments object
Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use any or unknown as type constraints
Don't return a value from a function with return type void
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables/params/class properties initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with ! postfix
Don't use parameter properties in class constructors
Don't use user-defined types (type aliases) where discouraged
Prefer as const over literal types/type annotations for literals
Use either T[] or Array consistently
Initialize each enum member value explicitly
Use export type for types
Use import type for types
Ensure all enum members are literal values
Don't use const enum
Don't declare empty interfaces
Don't let variables evolve into any through reassignments
Don't use the any type
Don't misuse non-null assertion operator in TS files
Don't use implicit any in variable declarations
Don't merge interfaces and classes unsafely
Place overload signatures adjacent to each other
Use namespace keyword instead of module to declare TS namespaces
Don't use void type outside generics or return types

Files:

  • src/storage/prisma.test.ts
  • src/storage/drizzle.test.ts
  • src/storage/kysely.test.ts
  • src/storage/kysely.ts
  • build.config.ts
**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{test,spec}.{js,jsx,ts,tsx}: Don't nest describe() blocks too deeply in test files
Don't use callbacks in asynchronous tests and hooks
Don't have duplicate hooks in describe blocks
Don't use export or module.exports in test files
Don't use focused tests
Ensure assertions (e.g., expect) are placed inside an it() call
Don't use disabled tests

Files:

  • src/storage/prisma.test.ts
  • src/storage/drizzle.test.ts
  • src/storage/kysely.test.ts
**/*.{jsx,tsx,js,ts}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx,js,ts}: Ensure preconnect is used with Google Fonts
Use a recommended display strategy with Google Fonts

Files:

  • src/storage/prisma.test.ts
  • src/storage/drizzle.test.ts
  • src/storage/kysely.test.ts
  • src/storage/kysely.ts
  • build.config.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

Don't import next/document outside of pages/_document.jsx

Files:

  • src/storage/prisma.test.ts
  • src/storage/drizzle.test.ts
  • src/storage/kysely.test.ts
  • src/storage/kysely.ts
  • build.config.ts
{**/*.{ts,tsx,js,jsx},package.json}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

{**/*.{ts,tsx,js,jsx},package.json}: Use bun <file> instead of node <file> or ts-node <file>
Use Bun.serve() for HTTP/WebSocket servers; do not use express
Use bun:sqlite for SQLite; do not use better-sqlite3
Use Bun.redis for Redis; do not use ioredis
Use Bun.sql for Postgres; do not use pg or postgres.js
Use the built-in WebSocket; do not use ws
Use Bun.$ shell (e.g., Bun.$ls) instead of execa

Files:

  • src/storage/prisma.test.ts
  • src/storage/drizzle.test.ts
  • src/storage/kysely.test.ts
  • src/storage/kysely.ts
  • build.config.ts
{**/*.test.{ts,tsx,js,jsx},package.json}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

{**/*.test.{ts,tsx,js,jsx},package.json}: Use bun test instead of jest or vitest
Use bun test to run tests

Files:

  • src/storage/prisma.test.ts
  • src/storage/drizzle.test.ts
  • src/storage/kysely.test.ts
{**/*.{html,ts,css},package.json}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun build <file.html|file.ts|file.css> instead of webpack or esbuild

Files:

  • src/storage/prisma.test.ts
  • src/storage/drizzle.test.ts
  • src/storage/kysely.test.ts
  • src/storage/kysely.ts
  • build.config.ts
🧬 Code graph analysis (1)
src/storage/kysely.test.ts (2)
src/drizzle/db.ts (1)
  • db (5-12)
src/storage/kysely.ts (2)
  • ApiKeysDatabase (8-14)
  • KyselyStore (39-179)
🪛 Checkov (3.2.334)
.github/workflows/test.yml

[medium] 104-105: Basic Auth Credentials

(CKV_SECRET_4)

🔇 Additional comments (8)
docker-compose.yml (1)

19-68: LGTM! Well-isolated test database services.

The three new PostgreSQL services provide proper isolation for testing Kysely, Drizzle, and Prisma adapters separately. Port mappings (5433, 5434, 5435) avoid conflicts, and dedicated volumes prevent cross-contamination between test runs.

FEATURE_SUGGESTIONS.md (1)

68-76: LGTM! Clearer template usage pattern.

The updated example shows a more explicit pattern by assigning the template to a variable before using it, which improves clarity.

src/storage/kysely.ts (1)

1-180: LGTM! Solid Kysely adapter implementation.

The implementation correctly handles:

  • JSONB containment queries for owner and tag filtering
  • OR logic across tags (line 131) without the previously flagged wrapping issue
  • Existence validation before metadata updates
  • Proper type conversions between database rows and API records

The use of any type at line 130 is reasonable given Kysely's complex type inference for the or helper.

build.config.ts (1)

13-13: LGTM! Build configuration updated correctly.

The Kysely adapter is properly included in build entries, and both kysely and pg are correctly externalized to prevent bundling peer dependencies.

Also applies to: 40-41

src/storage/drizzle.test.ts (1)

34-34: LGTM! Database endpoint updated for isolated testing.

The connection string now points to the dedicated Drizzle test database (port 5434, keypal_drizzle), ensuring proper test isolation.

src/storage/prisma.test.ts (1)

32-32: LGTM! Streamlined test setup and cleanup.

The test suite now uses the dedicated Prisma database (port 5435, keypal_prisma) and simplifies cleanup by removing unnecessary conditional checks. The unconditional cleanup is cleaner and safe.

Also applies to: 62-62, 66-66

.github/workflows/test.yml (1)

39-79: LGTM! Well-structured multi-database test workflow.

The workflow properly isolates each adapter's tests with dedicated PostgreSQL service containers and correct DATABASE_URL environment variables. This ensures tests don't interfere with each other.

Note: The static analysis warning about credentials at line 104 is a false positive—these are test database credentials for CI, not production secrets.

Also applies to: 94-119

src/storage/kysely.test.ts (1)

1-1215: LGTM! Comprehensive test coverage for KyselyStore.

The test suite provides excellent coverage including CRUD operations, tag filtering with OR logic, concurrent operations, edge cases, Unicode handling, key rotation, and revocation. The structure mirrors existing adapter tests, ensuring consistency across the codebase.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants