Skip to content

Add developer API beta#365

Merged
Flohhhhh merged 4 commits into
mainfrom
development
Jul 14, 2026
Merged

Add developer API beta#365
Flohhhhh merged 4 commits into
mainfrom
development

Conversation

@Flohhhhh

Copy link
Copy Markdown
Owner

Summary

Adds the first gated Developer API beta for published Sharply gear data.

What changed

  • Adds authenticated /api/v1 search, gear detail, live spec catalog, and selected-spec endpoints.
  • Adds user-owned API keys with one-time secret display, per-key fixed-minute rate limiting, daily usage totals, and access revocation.
  • Adds an approved-user developer portal, endpoint documentation, selector browser, cURL and TypeScript examples, and account-menu entry point.
  • Adds an admin page for granting developer access, managing keys, and reviewing basic usage.
  • Keeps developer API logic isolated under src/server/developer-api while reusing existing search and gear services.
  • Adds live API grouping metadata for registry-backed selectable specs.
  • Adds the developer API schema migration, documentation, translations, and unit/Playwright coverage.

Deployment notes

  • Apply migration 0024_tiresome_lilandra before serving the new routes.
  • Confirm Vercel firewall rules do not apply a browser challenge to /api/v1/*.

Validation

  • npm run test — 691 tests passed.
  • Targeted Developer API Playwright coverage passed.
  • Scoped ESLint and Prettier checks passed.
  • Translation parity passed.
  • React Doctor reported no issues.

npm run typecheck is still blocked by the pre-existing tests/unit/spec-registry-i18n.test.ts weightGrams typing error.

@vercel

vercel Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sharplyphoto Ready Ready Preview, Comment Jul 14, 2026 6:36pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Flohhhhh, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 27 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2301f8ac-4ab7-4c21-9e13-bb38ef33ddf6

📥 Commits

Reviewing files that changed from the base of the PR and between 40056d2 and f3ec503.

📒 Files selected for processing (7)
  • src/app/[locale]/(admin)/admin/developer-api/developer-api-admin-manager.tsx
  • src/app/[locale]/(pages)/developer/developer-portal.tsx
  • src/server/developer-api/actions.ts
  • src/server/developer-api/data.ts
  • src/server/developer-api/service.ts
  • tests/unit/developer-api-actions.test.ts
  • tests/unit/developer-api-portal-service.test.ts
📝 Walkthrough

Walkthrough

Adds a gated Developer API with hashed keys, rate limiting, usage tracking, read-only search/gear/spec endpoints, developer and admin portals, localized UI, documentation, and tests. It also expands BotID route protection and narrows sensor-spec data passed to the API.

Changes

Developer API foundation

Layer / File(s) Summary
Storage and API contracts
src/server/db/schema.ts, drizzle/*, src/server/developer-api/constants.ts, src/server/developer-api/data.ts
Adds developer-access state, API-key records, rate-limit buckets, daily usage counters, persistence helpers, endpoint constants, and typed errors.
Authentication, validation, and HTTP pipeline
src/server/developer-api/{service,http,schemas,serializers}.ts
Implements bearer-key authentication, SHA-256 key lookup, rate limiting, usage recording, request validation, standardized errors, headers, and public response serialization.
Spec registry and API routes
src/lib/specs/registry.tsx, src/server/developer-api/specs.ts, src/app/api/v1/**, src/server/gear/data.ts
Adds the live developer spec catalog and selector handling, then exposes search, suggestions, gear, and spec endpoints through shared service and serialization layers.
Portal, admin, and navigation surfaces
src/app/[locale]/(pages)/developer/**, src/app/[locale]/(admin)/admin/developer-api/**, src/server/developer-api/actions.ts, src/components/layout/*, messages/*.json
Adds developer key management, admin access/key controls, documentation pages, conditional navigation, localized labels, and translated portal/admin copy.
Behavioral validation and regression coverage
tests/unit/developer-api-*, tests/unit/*header*, tests/unit/user-menu.test.ts, tests/playwright/developer-api.spec.ts
Covers API gating, request handling, schemas, serializers, spec selection, portal filtering, documentation access, localized links, and conditional menu rendering.
Documentation and route protection
docs/*.md, src/lib/security/botid-protected-routes.ts, tests/unit/botid-protected-routes.test.ts
Documents the Developer API structure and contract, and adds BotID protection for under-construction list POST routes.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant API Route
  participant HTTP Wrapper
  participant Developer Service
  participant Database
  Client->>API Route: Send authenticated GET request
  API Route->>HTTP Wrapper: Run endpoint handler
  HTTP Wrapper->>Developer Service: Authenticate and rate-limit key
  Developer Service->>Database: Read key and update rate bucket
  API Route->>Developer Service: Fetch domain data
  Developer Service-->>API Route: Return search, gear, or spec data
  HTTP Wrapper->>Developer Service: Record endpoint usage
  API Route-->>Client: Return JSON with request and rate-limit metadata
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding a Developer API beta.
Description check ✅ Passed The description is directly related to the change set and summarizes the added endpoints, portal, admin tools, and docs.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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.

@Flohhhhh
Flohhhhh marked this pull request as ready for review July 14, 2026 18:01

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
messages/de.json (1)

1-1: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Translate the new developerApi strings in all locale files. messages/de.json, messages/es.json, messages/fr.json, messages/it.json, messages/ja.json, messages/ms.json, and messages/zh.json still leave 77 of 80 developerApi entries identical to messages/en.json, so the developer portal/docs/admin UI will mostly render in English for those locales.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@messages/de.json` at line 1, Translate the 77 untranslated developerApi
entries in messages/de.json, messages/es.json, messages/fr.json,
messages/it.json, messages/ja.json, messages/ms.json, and messages/zh.json,
using messages/en.json as the source while preserving all keys, placeholders,
and formatting.
🧹 Nitpick comments (8)
src/server/developer-api/data.ts (1)

175-189: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider conditionally spreading the set properties.

While this implementation is perfectly safe and functional, it produces SQL that redundantly assigns unmatched columns to their existing values (e.g., search_requests = search_requests). Conditionally spreading the properties results in a more compact UPDATE clause payload.

♻️ Proposed refactor
       set: {
         totalRequests: sql`${developerApiUsageDaily.totalRequests} + 1`,
-        searchRequests:
-          params.endpoint === "search"
-            ? sql`${developerApiUsageDaily.searchRequests} + 1`
-            : developerApiUsageDaily.searchRequests,
-        suggestionRequests:
-          params.endpoint === "suggestions"
-            ? sql`${developerApiUsageDaily.suggestionRequests} + 1`
-            : developerApiUsageDaily.suggestionRequests,
-        gearRequests:
-          params.endpoint === "gear"
-            ? sql`${developerApiUsageDaily.gearRequests} + 1`
-            : developerApiUsageDaily.gearRequests,
+        ...(params.endpoint === "search" && { searchRequests: sql`${developerApiUsageDaily.searchRequests} + 1` }),
+        ...(params.endpoint === "suggestions" && { suggestionRequests: sql`${developerApiUsageDaily.suggestionRequests} + 1` }),
+        ...(params.endpoint === "gear" && { gearRequests: sql`${developerApiUsageDaily.gearRequests} + 1` }),
       },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/developer-api/data.ts` around lines 175 - 189, Update the `set`
payload in the developer API usage update to conditionally spread only the
counter matching `params.endpoint`, while always incrementing `totalRequests`.
Remove the unmatched-column self-assignments so the generated UPDATE includes
only relevant properties, preserving the existing endpoint-specific increments.
src/server/developer-api/http.ts (2)

1-3: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Non-standard conditional server-only guard duplicated in two files.

Both files gate the server-only import behind process.env.NEXT_RUNTIME with a dynamic import(), unlike service.ts's plain import "server-only";. The package's protection is driven by the bundler's export-condition resolution, not a runtime env var, so this pattern likely doesn't add the intended safety net and just adds inconsistency.

  • src/server/developer-api/http.ts#L1-L3: replace with a plain import "server-only"; at the top, matching service.ts, unless there's a confirmed reason (e.g. Vitest import-time crash) this workaround is needed.
  • src/server/developer-api/serializers.ts#L1-L3: same fix — align with the standard unconditional import.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/developer-api/http.ts` around lines 1 - 3, Replace the conditional
dynamic server-only guard with a standard unconditional import at the top of
src/server/developer-api/http.ts lines 1-3, matching service.ts. Apply the same
change to src/server/developer-api/serializers.ts lines 1-3; remove the
NEXT_RUNTIME checks and dynamic imports in both files.

71-122: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Usage recording blocks the response instead of using after().

recordUsageBestEffort is awaited before the success/error response is returned, adding a DB write to the critical path of every request. Next.js's after() (stable, importable from next/server) is designed exactly for this "log/record after responding" pattern and would remove this from the request's latency.

⚡ Sketch using after()
+import { after } from "next/server";
...
-    const response = await handler();
-    await recordUsageBestEffort({ apiKeyId: credential.apiKeyId, endpoint });
+    const response = await handler();
+    after(() => recordUsageBestEffort({ apiKeyId: credential.apiKeyId, endpoint }));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/developer-api/http.ts` around lines 71 - 122, Update
runDeveloperApiRequest to schedule both success and error calls to
recordUsageBestEffort through Next.js after() instead of awaiting them before
returning. Import after from next/server, preserve the existing credential,
endpoint, and allowed-rate-limit conditions, and keep the response and error
handling paths otherwise unchanged.
src/lib/specs/registry.tsx (2)

2265-2278: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

hideInSpecsTable now doubles as a developer-API exclusion flag.

getDeveloperApiMetadata treats field.hideInSpecsTable as equivalent to api: null (Line 2270). hideInSpecsTable was designed to control website-UI visibility (e.g., a field surfaced elsewhere on the page) — reusing it to also gate the developer API means a field hidden from the specs table for unrelated reasons is silently excluded from the API too, with no explicit signal that this was intentional for the API contract.

♻️ Suggested decoupling
-  if (!config || field.hideInSpecsTable || field.api === null) return undefined;
+  if (!config || field.api === null) return undefined;

If any currently hideInSpecsTable field should indeed stay out of the API, mark it explicitly with api: null instead of relying on the coupling.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/specs/registry.tsx` around lines 2265 - 2278, Update
getDeveloperApiMetadata to stop using field.hideInSpecsTable as a developer-API
exclusion condition; gate metadata generation only on the section configuration
and field.api === null. For any fields that must remain excluded from the
developer API, set their api value explicitly to null at the field definitions.

2211-2278: 🎯 Functional Correctness | 🔵 Trivial

Developer API exposure is opt-in per section but opt-out per field — new fields leak by default.

Once a section is registered in developerApiSectionConfig, every field in it is exposed via the public API unless explicitly marked api: null (Line 2270). This PR already needed to retroactively add api: null for videoSummary/videoAvailableCodecs (Lines 1289, 1311-1312) to prevent leaking video-mode UI internals — a sign that this opt-out model requires developers to remember to exclude every future sensitive/internal field added to an already-registered section, or it ships to the public API unreviewed.

Consider flipping to an explicit opt-in (e.g., require api: {...} metadata to include a field, rather than api: null to exclude it) so new fields default to private until a conscious decision is made.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/specs/registry.tsx` around lines 2211 - 2278, The developer API
exposure logic in getDeveloperApiMetadata currently includes every field unless
api is null, causing newly added fields in registered sections to leak. Change
the eligibility check to require explicit field.api metadata, returning
undefined when api is absent or null, and preserve the existing hideInSpecsTable
exclusion and metadata construction for explicitly opted-in fields.
tests/playwright/developer-api.spec.ts (1)

11-14: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Simplify JSON assertions.

While await expect(...).resolves works correctly due to the underlying matcher engine, the standard Playwright idiom is to simply await the JSON parser result directly inside expect(...).

  • tests/playwright/developer-api.spec.ts#L11-L14: Change to expect(await response.json()).toMatchObject({ ... }).
  • tests/playwright/developer-api.spec.ts#L28-L30: Change to expect(await response.json()).toMatchObject({ ... }).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/playwright/developer-api.spec.ts` around lines 11 - 14, Update both
JSON assertions in tests/playwright/developer-api.spec.ts at lines 11-14 and
28-30 to await response.json() directly inside expect(...), then call
toMatchObject without resolves. Preserve the existing expected object matchers
and assertions at both sites.
src/app/[locale]/(admin)/admin/developer-api/developer-api-admin-manager.tsx (1)

246-246: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Use the shared key-limit constant here instead of hardcoding 3.

DEVELOPER_API_MAX_ACTIVE_KEYS already defines the server-side cap, so threading that value into the admin UI keeps the button state aligned if the limit ever changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`[locale]/(admin)/admin/developer-api/developer-api-admin-manager.tsx
at line 246, Replace the hardcoded 3 in the disabled condition with the shared
DEVELOPER_API_MAX_ACTIVE_KEYS constant, preserving the existing isPending check
and keys.length comparison.
src/app/[locale]/(admin)/admin/sidebar.tsx (1)

195-198: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider a consistent localization pattern for sidebar items.

Special-casing the "developerApi" label works for this addition, but it makes the render logic slightly fragile if more items require translations in the future. Consider adding an optional i18nKey property to the SidebarItem type and mapping it generically, or using a common translation namespace for all navigation items.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`[locale]/(admin)/admin/sidebar.tsx around lines 195 - 198, Update
the SidebarItem type and sidebar rendering to support an optional i18nKey for
localized labels, replacing the developerApi-specific conditional with generic
translation lookup when i18nKey is present and falling back to item.label
otherwise. Configure developerApi to use the appropriate i18nKey while
preserving existing labels for items without translations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@messages/de.json`:
- Around line 1856-1943: Translate all newly added developerApi entries in
messages/de.json, messages/es.json, messages/fr.json, and messages/it.json,
covering the portal, docs, and admin sections. Replace the remaining English
values while preserving translation keys, placeholders such as {name}, {count},
and {current}, and technical terms where appropriate; keep existing localized
values unchanged.

In `@messages/es.json`:
- Around line 1856-1943: Translate every remaining English string under
developerApi.portal, developerApi.docs, and developerApi.admin in
messages/es.json into Spanish, while preserving the already localized
portal.welcome, docs.serverOnlyNote, and docs.exampleRequest values. Keep
placeholders such as {name}, {count}, and {current} unchanged.

In `@messages/fr.json`:
- Around line 1856-1943: Translate every remaining English string under
developerApi in messages/fr.json, messages/de.json, and messages/es.json,
including portal, docs, and admin sections. Preserve all placeholders,
formatting, and API terminology while keeping the existing localized strings
intact.

In `@messages/it.json`:
- Around line 1856-1943: Translate every remaining English string under
developerApi in messages/it.json into Italian, covering portal, docs, and admin
while preserving placeholders, meaning, and formatting. Keep the already
localized portal.welcome, docs.serverOnlyNote, and docs.exampleRequest
unchanged.

In
`@src/app/`[locale]/(admin)/admin/developer-api/developer-api-admin-manager.tsx:
- Line 155: Update the error paragraph rendered by the developer API admin
manager to include role="alert", ensuring the existing error message remains
visually unchanged while action failures are announced to screen readers.
- Around line 85-96: Update createKey and the form’s name input to use
controlled draft-name state instead of an uncontrolled field. Preserve the
entered name while actionCreateDeveloperApiKeyForAdmin is pending or fails, and
clear the state only when result.ok is true after setting the secret and
refreshing.

In `@src/app/`[locale]/(pages)/developer/developer-portal.tsx:
- Line 1: Add role="alert" to the error message <p> elements in the developer
portal and admin developer API manager, using the existing {error} rendering so
action failures are announced to assistive technologies.
- Line 1: Update both create-key forms to make the name fields controlled with
state and bind their value and change handler, so form action completion does
not clear failed input. In each form, reset the name state only after the action
result is successful (`result.ok`), preserving the typed value on failure.
- Line 212: Add role="alert" to the error message paragraph in the developer
portal JSX so dynamically rendered {error} content is announced to assistive
technologies, while preserving the existing conditional rendering and styling.
- Around line 62-64: Update the dateFormatter construction in the developer
portal to pass an explicit, deterministic timeZone alongside locale and
dateStyle. Apply the same timezone configuration to the other date formatting
call around the referenced second location, preserving the existing medium-date
formatting behavior.
- Around line 66-79: Update createKey and the developer key name input to
preserve the submitted name while actionCreateDeveloperApiKey is pending or
fails: make the input controlled or otherwise retain its value, and clear it
only after a successful result before closing the dialog.

In `@src/server/developer-api/actions.ts`:
- Around line 12-17: Update actionError to preserve and return the
DeveloperApiError code and message for expected errors, while logging unexpected
errors server-side before returning the failure result. Use the existing
DeveloperApiError type or identifying fields to distinguish expected errors, and
remove the unused void error handling.

In `@src/server/developer-api/service.ts`:
- Around line 196-221: The duplicated parse → count → generate → insert logic in
createDeveloperApiKey and createDeveloperApiKeyForAdmin should be centralized in
a shared key-creation helper. Make enforcement of DEVELOPER_API_MAX_ACTIVE_KEYS
atomic by moving the limit check into the insert operation or wrapping the check
and createApiKeyData call in a transaction, ensuring concurrent requests cannot
exceed the limit while preserving the existing validation and error behavior.

---

Outside diff comments:
In `@messages/de.json`:
- Line 1: Translate the 77 untranslated developerApi entries in
messages/de.json, messages/es.json, messages/fr.json, messages/it.json,
messages/ja.json, messages/ms.json, and messages/zh.json, using messages/en.json
as the source while preserving all keys, placeholders, and formatting.

---

Nitpick comments:
In
`@src/app/`[locale]/(admin)/admin/developer-api/developer-api-admin-manager.tsx:
- Line 246: Replace the hardcoded 3 in the disabled condition with the shared
DEVELOPER_API_MAX_ACTIVE_KEYS constant, preserving the existing isPending check
and keys.length comparison.

In `@src/app/`[locale]/(admin)/admin/sidebar.tsx:
- Around line 195-198: Update the SidebarItem type and sidebar rendering to
support an optional i18nKey for localized labels, replacing the
developerApi-specific conditional with generic translation lookup when i18nKey
is present and falling back to item.label otherwise. Configure developerApi to
use the appropriate i18nKey while preserving existing labels for items without
translations.

In `@src/lib/specs/registry.tsx`:
- Around line 2265-2278: Update getDeveloperApiMetadata to stop using
field.hideInSpecsTable as a developer-API exclusion condition; gate metadata
generation only on the section configuration and field.api === null. For any
fields that must remain excluded from the developer API, set their api value
explicitly to null at the field definitions.
- Around line 2211-2278: The developer API exposure logic in
getDeveloperApiMetadata currently includes every field unless api is null,
causing newly added fields in registered sections to leak. Change the
eligibility check to require explicit field.api metadata, returning undefined
when api is absent or null, and preserve the existing hideInSpecsTable exclusion
and metadata construction for explicitly opted-in fields.

In `@src/server/developer-api/data.ts`:
- Around line 175-189: Update the `set` payload in the developer API usage
update to conditionally spread only the counter matching `params.endpoint`,
while always incrementing `totalRequests`. Remove the unmatched-column
self-assignments so the generated UPDATE includes only relevant properties,
preserving the existing endpoint-specific increments.

In `@src/server/developer-api/http.ts`:
- Around line 1-3: Replace the conditional dynamic server-only guard with a
standard unconditional import at the top of src/server/developer-api/http.ts
lines 1-3, matching service.ts. Apply the same change to
src/server/developer-api/serializers.ts lines 1-3; remove the NEXT_RUNTIME
checks and dynamic imports in both files.
- Around line 71-122: Update runDeveloperApiRequest to schedule both success and
error calls to recordUsageBestEffort through Next.js after() instead of awaiting
them before returning. Import after from next/server, preserve the existing
credential, endpoint, and allowed-rate-limit conditions, and keep the response
and error handling paths otherwise unchanged.

In `@tests/playwright/developer-api.spec.ts`:
- Around line 11-14: Update both JSON assertions in
tests/playwright/developer-api.spec.ts at lines 11-14 and 28-30 to await
response.json() directly inside expect(...), then call toMatchObject without
resolves. Preserve the existing expected object matchers and assertions at both
sites.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3747764b-45a5-485e-8a30-e0fd9591b57d

📥 Commits

Reviewing files that changed from the base of the PR and between 749d1fc and 40056d2.

📒 Files selected for processing (58)
  • docs/configuration.md
  • docs/developer-api.md
  • docs/search-system.md
  • docs/server-structure.md
  • drizzle/0024_tiresome_lilandra.sql
  • drizzle/meta/0024_snapshot.json
  • drizzle/meta/_journal.json
  • messages/de.json
  • messages/en.json
  • messages/es.json
  • messages/fr.json
  • messages/it.json
  • messages/ja.json
  • messages/ms.json
  • messages/zh.json
  • src/app/[locale]/(admin)/admin/developer-api/developer-api-admin-manager.tsx
  • src/app/[locale]/(admin)/admin/developer-api/page.tsx
  • src/app/[locale]/(admin)/admin/sidebar.tsx
  • src/app/[locale]/(pages)/developer/developer-portal.tsx
  • src/app/[locale]/(pages)/developer/docs/page.tsx
  • src/app/[locale]/(pages)/developer/docs/request-example-tabs.tsx
  • src/app/[locale]/(pages)/developer/docs/spec-selector-dialog.tsx
  • src/app/[locale]/(pages)/developer/page.tsx
  • src/app/api/v1/gear/[slug]/route.ts
  • src/app/api/v1/gear/[slug]/specs/route.ts
  • src/app/api/v1/search/route.ts
  • src/app/api/v1/search/suggestions/route.ts
  • src/app/api/v1/specs/route.ts
  • src/components/layout/header-client.tsx
  • src/components/layout/header-model.ts
  • src/components/layout/header.tsx
  • src/components/layout/user-menu.tsx
  • src/lib/auth/additional-fields.ts
  • src/lib/mapping/sensor-map.ts
  • src/lib/security/botid-protected-routes.ts
  • src/lib/specs/registry.tsx
  • src/server/db/schema.ts
  • src/server/developer-api/actions.ts
  • src/server/developer-api/constants.ts
  • src/server/developer-api/data.ts
  • src/server/developer-api/errors.ts
  • src/server/developer-api/http.ts
  • src/server/developer-api/schemas.ts
  • src/server/developer-api/serializers.ts
  • src/server/developer-api/service.ts
  • src/server/developer-api/specs.ts
  • src/server/gear/data.ts
  • tests/playwright/developer-api.spec.ts
  • tests/unit/botid-protected-routes.test.ts
  • tests/unit/developer-api-http.test.ts
  • tests/unit/developer-api-portal-service.test.ts
  • tests/unit/developer-api-schemas-serializers.test.ts
  • tests/unit/developer-api-spec-routes.test.ts
  • tests/unit/developer-api-specs.test.ts
  • tests/unit/developer-docs-page.test.ts
  • tests/unit/header-model.test.ts
  • tests/unit/header-server.test.ts
  • tests/unit/user-menu.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Include or update automated tests (vitest and/or playwright) covering the primary path and at least one meaningful edge case when introducing or modifying behavior

Files:

  • tests/unit/botid-protected-routes.test.ts
  • tests/playwright/developer-api.spec.ts
  • tests/unit/developer-api-http.test.ts
  • tests/unit/user-menu.test.ts
  • tests/unit/developer-api-portal-service.test.ts
  • tests/unit/header-model.test.ts
  • tests/unit/developer-api-spec-routes.test.ts
  • tests/unit/header-server.test.ts
  • tests/unit/developer-api-schemas-serializers.test.ts
  • tests/unit/developer-api-specs.test.ts
  • tests/unit/developer-docs-page.test.ts
**/{app,src}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Replace any new hardcoded user-facing strings with translation keys for all locales

Files:

  • src/app/api/v1/specs/route.ts
  • src/app/api/v1/search/suggestions/route.ts
  • src/app/api/v1/gear/[slug]/route.ts
  • src/app/[locale]/(admin)/admin/developer-api/developer-api-admin-manager.tsx
  • src/app/api/v1/gear/[slug]/specs/route.ts
  • src/lib/auth/additional-fields.ts
  • src/lib/security/botid-protected-routes.ts
  • src/server/developer-api/errors.ts
  • src/app/api/v1/search/route.ts
  • src/app/[locale]/(pages)/developer/docs/spec-selector-dialog.tsx
  • src/app/[locale]/(pages)/developer/docs/request-example-tabs.tsx
  • src/server/developer-api/constants.ts
  • src/app/[locale]/(admin)/admin/developer-api/page.tsx
  • src/server/developer-api/specs.ts
  • src/components/layout/header.tsx
  • src/server/db/schema.ts
  • src/app/[locale]/(pages)/developer/page.tsx
  • src/app/[locale]/(pages)/developer/developer-portal.tsx
  • src/server/developer-api/http.ts
  • src/app/[locale]/(admin)/admin/sidebar.tsx
  • src/lib/mapping/sensor-map.ts
  • src/server/gear/data.ts
  • src/components/layout/header-model.ts
  • src/server/developer-api/schemas.ts
  • src/server/developer-api/actions.ts
  • src/components/layout/header-client.tsx
  • src/server/developer-api/serializers.ts
  • src/app/[locale]/(pages)/developer/docs/page.tsx
  • src/components/layout/user-menu.tsx
  • src/server/developer-api/service.ts
  • src/server/developer-api/data.ts
  • src/lib/specs/registry.tsx
src/server/db/schema.ts

📄 CodeRabbit inference engine (AGENTS.md)

src/server/db/schema.ts: Make all schema changes in src/server/db/schema.ts; never modify the database directly without going through the schema
All schema changes must be backwards compatible by default, using deprecations instead of deletions
Use Drizzle's type-safe schema definitions for database schema in src/server/db/schema.ts

Files:

  • src/server/db/schema.ts
messages/**/*.json

📄 CodeRabbit inference engine (AGENTS.md)

Maintain translation key parity: if a key is added, removed, or renamed in messages/en.json, apply the same key change across all locale files in messages/ directory

Files:

  • messages/zh.json
  • messages/en.json
  • messages/fr.json
  • messages/ja.json
  • messages/de.json
  • messages/es.json
  • messages/ms.json
  • messages/it.json
🪛 Betterleaks (1.6.1)
src/app/[locale]/(pages)/developer/docs/page.tsx

[high] 160-161: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)


[high] 211-212: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)


[high] 267-268: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)


[high] 308-309: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)

🪛 LanguageTool
docs/developer-api.md

[style] ~11-~11: Consider using a different verb to strengthen your wording.
Context: ...tores a SHA-256 hash, not the secret. - Removing developer access immediately revokes al...

(REMOVE_REVOKE)


[style] ~11-~11: Consider removing “of” to be more concise
Context: ...ng developer access immediately revokes all of the user’s keys. - Send keys with `Authoriz...

(ALL_OF_THE)

🪛 OpenGrep (1.25.0)
src/server/developer-api/service.ts

[ERROR] 77-77: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🪛 React Doctor (0.7.6)
src/app/[locale]/(pages)/developer/developer-portal.tsx

[error] 161-161: This can cause a hydration mismatch because Intl.DateTimeFormat().format() formats with the server's locale and timezone during server rendering but the user's in the browser. Format it in a post-mount useEffect, or pass an explicit locale and timeZone.

Format locale/timezone-dependent values in a post-mount useEffect + state, or pass an explicit locale and timeZone so the server and the browser render the same text. Only runs on SSR-capable projects.

(no-locale-format-in-render)


[error] 163-163: This can cause a hydration mismatch because Intl.DateTimeFormat().format() formats with the server's locale and timezone during server rendering but the user's in the browser. Format it in a post-mount useEffect, or pass an explicit locale and timeZone.

Format locale/timezone-dependent values in a post-mount useEffect + state, or pass an explicit locale and timeZone so the server and the browser render the same text. Only runs on SSR-capable projects.

(no-locale-format-in-render)

🔇 Additional comments (69)
drizzle/0024_tiresome_lilandra.sql (1)

1-39: LGTM!

drizzle/meta/0024_snapshot.json (1)

1-9340: LGTM!

drizzle/meta/_journal.json (1)

172-178: LGTM!

src/server/db/schema.ts (2)

2532-2536: LGTM!


2562-2632: LGTM!

src/lib/auth/additional-fields.ts (1)

39-43: LGTM!

src/server/developer-api/constants.ts (1)

1-13: LGTM!

src/server/developer-api/data.ts (2)

1-174: LGTM!


190-217: LGTM!

src/server/developer-api/actions.ts (1)

19-27: LGTM!

Also applies to: 29-37, 39-50, 52-65, 67-75

src/server/developer-api/errors.ts (1)

1-18: LGTM!

src/server/developer-api/service.ts (7)

69-106: Confirm whether API error message strings are in scope of the i18n guideline.

This file (and schemas.ts, http.ts) constructs many hardcoded English messages via DeveloperApiError (e.g. "An API key is required.", "The supplied API key is invalid."). Per path instructions, new hardcoded user-facing strings under src/** should use translation keys, but these are developer-facing HTTP API response bodies, not rendered UI — conventionally kept in one language for API consumers.

As per path instructions, **/{app,src}/**/*.{ts,tsx}: "Replace any new hardcoded user-facing strings with translation keys for all locales." Please confirm whether this rule is intended to cover machine-readable API error payloads, or only UI-rendered strings; if it applies here, these messages (and the similar ones throughout service.ts, schemas.ts, and http.ts) would need extraction into messages/*.json.

Source: Path instructions


42-57: LGTM!

Also applies to: 69-151


153-194: LGTM!


223-295: LGTM!


328-332: LGTM!


59-67: 🔒 Security & Privacy

keyPrefix only exposes 7 characters of the random secret (sharply_live_ + 7 base64url chars), so it doesn't meaningfully reduce API key entropy.

			> Likely an incorrect or invalid review comment.

334-358: 🗄️ Data Integrity & Integration

These passthroughs already return published gear only. searchGear and getSuggestions enforce publicationState = PUBLISHED in src/server/search/data.ts, and fetchGearBySlug filters out hidden/rumored items.

			> Likely an incorrect or invalid review comment.
src/server/developer-api/schemas.ts (1)

1-89: LGTM!

src/server/developer-api/http.ts (1)

5-70: LGTM!

src/server/developer-api/serializers.ts (2)

5-127: LGTM!

Also applies to: 155-165


138-146: 🔒 Security & Privacy

No change needed here. fetchGearBySlug already filters rawSamples to isDeleted = false, so serializeGear only serializes active samples.

			> Likely an incorrect or invalid review comment.
src/lib/mapping/sensor-map.ts (1)

4-8: LGTM!

Also applies to: 37-39

src/lib/specs/registry.tsx (2)

4-43: LGTM!

Also applies to: 90-111, 193-203, 254-262, 426-448, 649-698, 1198-1203, 1289-1289, 1311-1312, 1621-1621, 1688-1694, 2400-2406


2312-2354: 🎯 Functional Correctness

No issue: boolean spec values already have explicit formatters

Every boolean-like developer API field in src/lib/specs/registry.tsx passes through formatDisplay/yesNoNull, so textFromSpecDisplay does not drop a current spec value.

			> Likely an incorrect or invalid review comment.
tests/unit/developer-api-schemas-serializers.test.ts (1)

1-175: LGTM!

tests/unit/developer-api-specs.test.ts (1)

1-147: LGTM!

tests/unit/developer-docs-page.test.ts (1)

1-103: LGTM!

tests/unit/header-model.test.ts (1)

1-14: LGTM!

Also applies to: 56-61, 77-77

tests/unit/header-server.test.ts (1)

2-2: LGTM!

Also applies to: 40-42, 55-55, 68-71

src/server/developer-api/specs.ts (1)

1-63: LGTM!

src/app/api/v1/gear/[slug]/specs/route.ts (1)

7-28: LGTM!

tests/unit/botid-protected-routes.test.ts (1)

1-15: LGTM!

tests/unit/developer-api-http.test.ts (1)

14-98: LGTM!

tests/unit/developer-api-portal-service.test.ts (1)

56-99: LGTM!

tests/unit/developer-api-spec-routes.test.ts (1)

34-91: LGTM!

src/server/gear/data.ts (1)

351-362: LGTM!

src/app/api/v1/search/route.ts (1)

6-15: LGTM!

src/app/api/v1/search/suggestions/route.ts (1)

6-15: LGTM!

src/app/api/v1/gear/[slug]/route.ts (1)

6-21: LGTM!

tests/unit/user-menu.test.ts (1)

1-67: LGTM!

src/app/api/v1/specs/route.ts (1)

4-9: 🎯 Functional Correctness

No endpoint change needed. DeveloperApiEndpoint only includes search, suggestions, and gear, and this route is recorded under the shared gear bucket.

			> Likely an incorrect or invalid review comment.
src/app/[locale]/(pages)/developer/developer-portal.tsx (1)

1-278: The rest of the component (dialog wiring, revoke flow, translation usage) looks correct and consistent with the messages files.

src/app/[locale]/(admin)/admin/developer-api/developer-api-admin-manager.tsx (1)

1-289: Pagination, search filtering, and access/revoke flows are otherwise implemented soundly.

messages/de.json (1)

9-9: LGTM!

messages/en.json (1)

9-9: LGTM!

Also applies to: 1856-1943

messages/es.json (1)

9-9: LGTM!

messages/fr.json (1)

9-9: LGTM!

messages/it.json (1)

9-9: LGTM!

src/app/[locale]/(pages)/developer/page.tsx (1)

1-51: LGTM!

src/app/[locale]/(pages)/developer/docs/page.tsx (1)

1-338: LGTM!

src/app/[locale]/(pages)/developer/docs/request-example-tabs.tsx (1)

1-38: LGTM!

src/app/[locale]/(pages)/developer/docs/spec-selector-dialog.tsx (1)

1-85: LGTM!

docs/configuration.md (1)

29-30: LGTM!

src/lib/security/botid-protected-routes.ts (1)

26-33: LGTM!

src/app/[locale]/(admin)/admin/developer-api/page.tsx (1)

1-16: LGTM!

src/app/[locale]/(admin)/admin/sidebar.tsx (3)

4-4: LGTM!


46-53: LGTM!


121-130: LGTM!

messages/ja.json (1)

9-9: LGTM!

docs/developer-api.md (1)

1-79: LGTM!

docs/search-system.md (1)

49-52: LGTM!

docs/server-structure.md (1)

81-87: LGTM!

messages/ms.json (1)

9-9: LGTM!

Also applies to: 1856-1943

messages/zh.json (1)

9-9: LGTM!

Also applies to: 1856-1943

src/components/layout/header-client.tsx (1)

10-13: LGTM!

Also applies to: 40-42, 83-90, 103-108, 235-235

src/components/layout/header-model.ts (1)

38-38: LGTM!

Also applies to: 83-83, 95-95, 107-109, 191-191

src/components/layout/header.tsx (1)

21-21: LGTM!

src/components/layout/user-menu.tsx (1)

3-3: LGTM!

Also applies to: 12-19, 32-49, 102-117

Comment thread messages/de.json
Comment thread messages/es.json
Comment thread messages/fr.json
Comment thread messages/it.json
Comment thread src/app/[locale]/(pages)/developer/developer-portal.tsx Outdated
Comment thread src/app/[locale]/(pages)/developer/developer-portal.tsx
Comment thread src/app/[locale]/(pages)/developer/developer-portal.tsx Outdated
Comment thread src/server/developer-api/actions.ts
Comment thread src/server/developer-api/service.ts
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.

1 participant