Skip to content

Add support for Hono#1

Merged
itssimon merged 13 commits intomainfrom
hono
Dec 21, 2025
Merged

Add support for Hono#1
itssimon merged 13 commits intomainfrom
hono

Conversation

@itssimon
Copy link
Member

@itssimon itssimon commented Dec 21, 2025

Summary by CodeRabbit

  • New Features

    • Apitally integration for Hono: request/response logging, configurable masking of sensitive data, consumer handling, endpoint discovery, and optional response body capture.
  • Chores

    • Project initialization: package/config files, build/test/lint setup, license, and .gitignore.
    • CI/CD workflows for tests, summaries, and automated publishing.
  • Tests

    • Unit and integration tests covering consumers, headers, masking, response capture, and middleware behavior.
  • Documentation

    • README updates (badges and links).

✏️ Tip: You can customize this high-level summary in your review settings.

@itssimon itssimon self-assigned this Dec 21, 2025
@itssimon
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Dec 21, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Dec 21, 2025

Walkthrough

Adds project scaffolding for the @apitally/serverless package: CI workflows, lint/test/build configs, package manifest, utility libraries (bytes, headers, config, consumers, masking, output, response), Hono middleware and utils, VERSION placeholder, tests, and repository metadata (LICENSE, .gitignore, README updates).

Changes

Cohort / File(s) Summary
Workflows
​.github/workflows/publish.yaml, ​.github/workflows/summary.yaml, ​.github/workflows/tests.yaml
New GitHub Actions: publish-on-release workflow, a wait-for-status-checks workflow, and tests workflow running pre-commit, type/attw checks, build, tests with coverage and Codecov upload.
Repository metadata & docs
​.gitignore, LICENSE, README.md
Adds .gitignore, MIT LICENSE, and README updates (badge token and quickstart → setup-guides link).
Project & tooling configs
package.json, tsup.config.ts, tsconfig.json, vitest.config.ts, eslint.config.js, .pre-commit-config.yaml, renovate.json
Adds package manifest and configurations for build (tsup), TypeScript, testing (Vitest), ESLint, pre-commit hooks, and Renovate.
Common — bytes
src/common/bytes.ts
New pure helpers: stringToBytes, bytesToString, concatBytes, bytesToBase64, base64ToBytes.
Common — config
src/common/config.ts
Adds ApitallyConfig type, DEFAULT_CONFIG, and mergeConfigWithDefaults.
Common — consumers
src/common/consumers.ts
Adds ApitallyConsumer type and consumerFromStringOrObject with normalization, truncation, and in-memory deduplication via djb2 hashing.
Common — headers
src/common/headers.ts
Adds SUPPORTED_CONTENT_TYPES, convertHeaders, parseContentLength, and isSupportedContentType.
Common — masking
src/common/masking.ts
Adds DataMasker class implementing config-driven masking and exclusion for headers and bodies (JSON and NDJSON), recursive traversal, and masking rules.
Common — output/logging
src/common/output.ts
Adds OutputData type and logData that serializes, gzips, base64-encodes, enforces size truncation, and emits prefixed logs.
Common — response capture
src/common/response.ts
Adds captureResponse with CaptureResponseOptions and CapturedResponse types to stream and optionally capture response bodies with max-size and timeout handling.
Common — version
src/common/version.ts
Adds exported VERSION constant initialized to "0.0.0" (updated during release workflow).
Hono integration
src/hono/index.ts, src/hono/middleware.ts, src/hono/utils.ts
Adds useApitally middleware, endpoint listing and consumer helpers (listEndpoints, setConsumer, getConsumer, tryWaitUntil), and re-exports types.
Tests — common
tests/common/consumers.test.ts, tests/common/headers.test.ts, tests/common/masking.test.ts, tests/common/response.test.ts
Unit tests for consumer parsing/deduplication, header utilities, DataMasker behaviors (exclusions, header/body masking, JSON/NDJSON), and response capture size handling.
Tests — Hono & utils
tests/hono/app.ts, tests/hono/app.test.ts, tests/utils.ts
Test Hono app routes exercising middleware logging, integration tests asserting logged payloads, and test helpers for decompressing/extracting logged data and a wait util.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Focus review on:
    • src/common/masking.ts — recursive masking, NDJSON handling, exclusion rules, and pattern matching.
    • src/common/response.ts — streaming/pipe logic, timeout race, max-body enforcement, and error handling.
    • src/hono/middleware.ts — orchestration between request lifecycle, captureResponse, masking, and async logging (waitUntil usage).
    • src/common/consumers.ts — deduplication hash correctness and truncation edge cases.
    • src/common/output.ts — gzip/base64 serialization, size-threshold logic, and toJSON hooks for bodies.

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 title 'Add support for Hono' accurately describes the main objective of the changeset, which introduces a Hono middleware with comprehensive logging and masking capabilities.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hono

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ad5dfce and 1a5ba7b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • package.json (1 hunks)
  • src/common/headers.ts (1 hunks)
  • tsconfig.json (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • tsconfig.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • package.json
  • src/common/headers.ts

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

🧹 Nitpick comments (11)
vitest.config.ts (1)

7-7: Consider enabling test isolation for more reliable tests.

Setting isolate: false disables test isolation, which can lead to shared state between tests and make them order-dependent. This may cause flaky tests and make debugging harder. Consider enabling isolation unless there's a specific performance reason to disable it.

🔎 Proposed change
-    isolate: false,
+    isolate: true,
eslint.config.js (1)

9-11: Reconsider disabling strict TypeScript rules.

Disabling no-explicit-any, no-var-requires, and no-require-imports weakens TypeScript's type safety guarantees. Specifically:

  • no-explicit-any allows unchecked any types, reducing type safety
  • no-var-requires and no-require-imports permit CommonJS patterns, which conflicts with the ESM-only build configuration in tsup.config.ts

Consider enabling these rules to maintain stricter type checking and module consistency.

.github/workflows/publish.yaml (1)

29-33: Version update logic works but could be more robust.

The sed command for updating the version is functional but relies on the exact string format in src/common/version.ts. While this is acceptable for now, consider using a more robust approach like a Node.js script if the version management becomes more complex.

tsconfig.json (1)

12-13: Decorator options enabled but not used.

The emitDecoratorMetadata and experimentalDecorators options are enabled, but no decorators appear to be used in the provided code. While this doesn't cause issues, these options may be unnecessary unless decorators are planned for future use.

src/hono/index.ts (1)

1-4: Clean public API surface.

The exports are well-organized, separating type-only exports from value exports. The .js extensions are appropriate for ESM compatibility.

Consider whether getConsumer from ./utils.js should also be exported for users who may want to read the current consumer value in their handlers.

tests/utils.ts (1)

3-28: Consider adding error handling for malformed JSON.

The function correctly decompresses and decodes the logged data. However, if the decompressed data is not valid JSON, JSON.parse will throw an uncaught exception.

For test utilities, this may be acceptable since a failure would surface as a test error, but wrapping in try-catch could provide clearer error messages.

🔎 Optional: Add error context for debugging
   const jsonString = decoder.decode(decompressed);
-
-  return JSON.parse(jsonString);
+  try {
+    return JSON.parse(jsonString);
+  } catch (error) {
+    throw new Error(`Failed to parse logged data: ${jsonString.slice(0, 100)}...`);
+  }
 }
tests/hono/app.ts (1)

8-60: Test app is well-structured for comprehensive middleware testing.

The app covers key scenarios: query validation, path params, JSON body, error handling, and streaming responses.

Minor observation: getApp is declared async but contains no await expressions. This could be simplified to a synchronous function.

🔎 Optional: Remove unnecessary async
-export const getApp = async () => {
+export const getApp = () => {
   const app = new Hono();
   // ...
   return app;
 };
tests/hono/app.test.ts (1)

33-106: Good test coverage for core middleware behavior.

The tests effectively validate:

  • Request/response logging structure
  • Path parameter resolution (/hello/:id logged as template, not /hello/123)
  • Request body and headers capture
  • Error response handling

Consider adding coverage for:

  • The /stream endpoint (streaming responses)
  • The consumer field in logged data (set via setConsumer(c, "test") in the /hello handler)
src/hono/utils.ts (1)

35-41: Consider adding a brief comment explaining the silent catch.

The empty catch block is intentional for environments where executionCtx.waitUntil is unavailable (e.g., non-Cloudflare Workers). A brief comment would improve maintainability.

Suggested documentation
 export function tryWaitUntil(c: Context, promise: Promise<unknown>) {
   try {
     c.executionCtx.waitUntil(promise);
   } catch (error) {
-    // ignore
+    // Silently ignore - executionCtx.waitUntil may not be available in all environments
   }
 }
src/common/headers.ts (1)

1-9: Consider exporting SUPPORTED_CONTENT_TYPES for testing and documentation.

The constant is currently not exported, which limits testability and makes it harder for consumers to understand what content types are supported.

Suggested change
-const SUPPORTED_CONTENT_TYPES = [
+export const SUPPORTED_CONTENT_TYPES = [
src/common/consumers.ts (1)

7-15: Consider the implications of unbounded cache growth and hash collisions.

The seenConsumerHashes Set persists for the lifetime of the module/function instance and is never cleared. While serverless instances are typically short-lived, warmed instances or edge runtimes could accumulate entries over time.

Additionally, the 32-bit hash space means collisions become likely at scale (~77K unique identifier+name+group combinations gives ~50% collision probability). A collision would cause a consumer object to be incorrectly reduced to just its identifier string.

If this is intentional for the expected scale, consider adding a brief comment documenting these design tradeoffs.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b601aa4 and d24520d.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (31)
  • .github/workflows/publish.yaml (1 hunks)
  • .github/workflows/summary.yaml (1 hunks)
  • .github/workflows/tests.yaml (1 hunks)
  • .gitignore (1 hunks)
  • .pre-commit-config.yaml (1 hunks)
  • LICENSE (1 hunks)
  • README.md (2 hunks)
  • eslint.config.js (1 hunks)
  • package.json (1 hunks)
  • renovate.json (1 hunks)
  • src/common/bytes.ts (1 hunks)
  • src/common/config.ts (1 hunks)
  • src/common/consumers.ts (1 hunks)
  • src/common/headers.ts (1 hunks)
  • src/common/masking.ts (1 hunks)
  • src/common/output.ts (1 hunks)
  • src/common/response.ts (1 hunks)
  • src/common/version.ts (1 hunks)
  • src/hono/index.ts (1 hunks)
  • src/hono/middleware.ts (1 hunks)
  • src/hono/utils.ts (1 hunks)
  • tests/common/consumers.test.ts (1 hunks)
  • tests/common/headers.test.ts (1 hunks)
  • tests/common/masking.test.ts (1 hunks)
  • tests/common/response.test.ts (1 hunks)
  • tests/hono/app.test.ts (1 hunks)
  • tests/hono/app.ts (1 hunks)
  • tests/utils.ts (1 hunks)
  • tsconfig.json (1 hunks)
  • tsup.config.ts (1 hunks)
  • vitest.config.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (12)
tests/common/consumers.test.ts (1)
src/common/consumers.ts (1)
  • consumerFromStringOrObject (17-43)
tests/hono/app.ts (3)
src/hono/middleware.ts (1)
  • useApitally (17-96)
src/hono/index.ts (2)
  • useApitally (3-3)
  • setConsumer (4-4)
src/hono/utils.ts (1)
  • setConsumer (24-29)
tests/common/response.test.ts (2)
src/common/response.ts (1)
  • captureResponse (16-94)
src/common/bytes.ts (2)
  • stringToBytes (1-3)
  • bytesToString (5-7)
src/hono/utils.ts (1)
src/common/consumers.ts (2)
  • ApitallyConsumer (1-5)
  • consumerFromStringOrObject (17-43)
src/hono/middleware.ts (8)
src/common/config.ts (2)
  • ApitallyConfig (1-10)
  • mergeConfigWithDefaults (23-27)
src/common/masking.ts (1)
  • DataMasker (38-147)
src/common/response.ts (1)
  • captureResponse (16-94)
src/common/headers.ts (3)
  • isSupportedContentType (50-55)
  • parseContentLength (31-48)
  • convertHeaders (11-29)
src/common/bytes.ts (1)
  • stringToBytes (1-3)
src/hono/utils.ts (3)
  • listEndpoints (8-22)
  • getConsumer (31-33)
  • tryWaitUntil (35-41)
src/common/version.ts (1)
  • VERSION (1-1)
src/common/output.ts (1)
  • logData (53-63)
tests/common/headers.test.ts (1)
src/common/headers.ts (3)
  • parseContentLength (31-48)
  • convertHeaders (11-29)
  • isSupportedContentType (50-55)
src/common/response.ts (1)
src/common/bytes.ts (2)
  • concatBytes (9-18)
  • stringToBytes (1-3)
tests/common/masking.test.ts (3)
src/common/output.ts (1)
  • OutputData (4-27)
src/common/bytes.ts (2)
  • stringToBytes (1-3)
  • bytesToString (5-7)
src/common/masking.ts (1)
  • DataMasker (38-147)
src/common/consumers.ts (1)
src/hono/index.ts (1)
  • ApitallyConsumer (2-2)
tests/hono/app.test.ts (3)
tests/hono/app.ts (1)
  • getApp (8-61)
tests/utils.ts (2)
  • wait (30-32)
  • getLoggedData (3-28)
src/common/bytes.ts (2)
  • bytesToString (5-7)
  • base64ToBytes (24-31)
src/common/output.ts (2)
src/common/consumers.ts (1)
  • ApitallyConsumer (1-5)
src/common/bytes.ts (1)
  • bytesToBase64 (20-22)
src/common/masking.ts (3)
src/common/config.ts (1)
  • ApitallyConfig (1-10)
src/common/output.ts (1)
  • OutputData (4-27)
src/common/bytes.ts (2)
  • bytesToString (5-7)
  • stringToBytes (1-3)
🔇 Additional comments (45)
vitest.config.ts (1)

8-13: LGTM!

The coverage configuration is well-structured, appropriately excluding type definition files and generating both text and LCOV reports.

.gitignore (1)

1-8: LGTM!

The gitignore patterns appropriately cover build artifacts, dependencies, editor configurations, and debug logs.

renovate.json (1)

1-4: LGTM!

The Renovate configuration correctly references the schema and extends the shared organizational configuration for consistent dependency management.

tsup.config.ts (1)

3-12: Verify that bundle: false is the intended behavior.

The configuration sets bundle: false, which means external dependencies won't be bundled into the output. This is typically correct for libraries where users install dependencies separately, but ensure this aligns with the package distribution strategy.

The rest of the configuration looks appropriate:

  • ESM format is modern and widely supported
  • Type declarations and sourcemaps will aid consumers
  • platform: "neutral" is correct for cross-runtime compatibility
.github/workflows/summary.yaml (2)

9-11: LGTM!

The permissions are appropriately scoped to read-only access for checks, following the principle of least privilege.


13-16: No action required—version is current with no known security advisories.

The workflow uses poseidon/wait-for-status-checks@v0.6.0, which is the latest available version. No published security advisories were found for this action.

eslint.config.js (1)

12-18: LGTM!

The no-unused-vars customization is well-configured, appropriately ignoring single-underscore parameters and common error variable names while still catching genuinely unused variables.

.pre-commit-config.yaml (1)

1-7: pre-commit-hooks v6.0.0 is current.

v6.0.0 is the latest version and the hook selection is appropriate for maintaining consistent file formatting. No action required.

package.json (1)

1-78: LGTM!

The package configuration is well-structured with proper ESM exports, optional peer dependencies, and comprehensive metadata. The subpath exports pattern (./hono) aligns well with supporting multiple frameworks in the future.

.github/workflows/tests.yaml (2)

1-16: LGTM!

The workflow structure is well-organized with appropriate separation of concerns (pre-commit checks, type validation with attw, and test coverage). The concurrency configuration will prevent redundant runs.

Also applies to: 20-55


17-19: Invalid Python version specified.

Python 3.14 has not been released yet. The latest stable Python version is 3.13. Using an unreleased version will cause the workflow to fail or use an unstable pre-release.

🔎 Proposed fix
       - uses: actions/setup-python@v6
         with:
-          python-version: "3.14"
+          python-version: "3.13"

Likely an incorrect or invalid review comment.

README.md (1)

19-19: LGTM!

Badge and documentation link updates are appropriate for the new project structure.

Also applies to: 67-67

tests/common/response.test.ts (1)

1-37: LGTM!

The test coverage appropriately validates the core response capture functionality, including the important edge case where the body exceeds the size limit.

tests/common/headers.test.ts (1)

1-46: LGTM!

Comprehensive test coverage for header utilities with good edge case handling including undefined, null, arrays, and type conversions.

.github/workflows/publish.yaml (1)

1-28: LGTM!

The workflow structure is well-organized with proper checks and tests before publishing. The permissions are correctly scoped.

Also applies to: 34-37

tsconfig.json (1)

1-11: LGTM!

The TypeScript configuration is well-structured with strict mode enabled and proper module resolution for NodeNext. The configuration appropriately covers both source and test files.

Also applies to: 14-15

tests/common/consumers.test.ts (1)

1-26: Well-structured test coverage for consumer utility.

The test effectively validates:

  • Null/empty/whitespace handling returning undefined
  • String normalization
  • Object processing with identifier-only, identifier+name, and identifier+name+group scenarios
  • Deduplication behavior via the hash-based caching (lines 12-17 correctly test that the second call with same identifier/name returns just the identifier)
tests/utils.ts (1)

30-32: LGTM!

Simple and effective delay utility with a sensible default.

src/common/config.ts (1)

1-27: Configuration structure is well-designed.

The type definition is clear and the defaults are sensible:

  • enabled: false requires explicit opt-in
  • Response headers logged by default, request headers/bodies opt-in

Note that mergeConfigWithDefaults performs a shallow merge, so user-provided arrays (maskHeaders, maskBodyFields, excludePaths) completely replace the defaults rather than appending. Since defaults are empty arrays, this is correct behavior.

src/common/bytes.ts (1)

1-18: LGTM!

The remaining utilities (stringToBytes, bytesToString, concatBytes, base64ToBytes) are correctly implemented using web standard APIs.

src/hono/middleware.ts (2)

23-95: Middleware structure is well-designed.

The overall pattern is solid:

  • Response capture via TransformStream allows non-blocking body collection
  • Deferred logging via tryWaitUntil works well for serverless environments
  • Proper masking applied before logging
  • Consumer extraction supports both string and object forms

The response time measurement (line 37, 81) includes the full response streaming duration. This is appropriate for total request handling time rather than TTFB.


40-47: The code works as intended. Hono caches request bodies, allowing c.req.arrayBuffer() to be called even after downstream handlers have consumed the body via methods like c.req.json(). The test "Logs POST request with JSON body" confirms this scenario works correctly.

tests/common/masking.test.ts (5)

20-34: Partial override for nested objects may not work as intended.

The spread operator ...overrides only performs a shallow merge. When you pass { request: { path: "/healthz" } } (line 43), it completely replaces the default request object, losing headers and body defaults. This is likely intentional for some tests, but worth noting for clarity.

For test cases like line 43, this means data1.request will only have path and no headers or body, which aligns with testing the exclude path behavior. The current approach is acceptable since each test explicitly provides what it needs.


37-58: LGTM!

The test correctly validates built-in path exclusions, custom exclusions via regex, and non-excluded paths. The assertions properly verify that excluded paths clear headers, body, and set the exclude flag.


60-82: LGTM!

Tests correctly verify that headers and bodies are not logged when their respective config flags are disabled.


84-132: LGTM!

Header and body masking tests thoroughly cover built-in patterns (authorization, password, token), custom patterns, nested objects, and arrays. Good coverage of the masking functionality.


134-173: LGTM!

NDJSON and non-JSON body tests correctly validate that NDJSON lines are individually parsed and masked, while plain text bodies remain unchanged.

src/hono/utils.ts (2)

8-22: LGTM!

The listEndpoints function correctly filters out middleware handlers and HTTP methods that typically don't need tracking (ALL, HEAD, OPTIONS). The uppercase normalization ensures consistent method names.


24-33: LGTM!

setConsumer and getConsumer properly handle the consumer lifecycle, with getConsumer delegating to consumerFromStringOrObject for normalization and deduplication.

src/common/headers.ts (3)

11-29: LGTM!

convertHeaders correctly handles all input types: undefined, Headers instance, and plain objects with string/array/number values. The flatMap approach elegantly handles multi-value headers.


31-48: LGTM!

parseContentLength defensively handles all expected input types including the recursive case for string arrays. The isNaN check properly guards against unparseable strings.


50-55: LGTM!

isSupportedContentType correctly uses startsWith which handles content-type parameters (e.g., application/json; charset=utf-8 will match application/json).

src/common/masking.ts (4)

5-30: LGTM!

The built-in patterns provide good coverage for common sensitive paths, headers, and body fields. The regex patterns are appropriately case-insensitive.


67-83: LGTM!

The recursive maskBody method correctly handles nested objects and arrays. Only string values matching sensitive field patterns are masked, which preserves data structure integrity.


111-137: Consider the assumption that missing content-type implies JSON.

Line 115 treats bodies with no content-type as JSON (!contentType || /\bjson\b/i.test(contentType)). While the outer try-catch safely handles parse failures, this could cause unexpected behavior or unnecessary parsing attempts for non-JSON bodies without a content-type header.

This is likely acceptable since:

  1. The parse failure is caught and body is left unchanged
  2. Most APIs set content-type headers

However, verify this assumption aligns with your expected use cases.


139-146: LGTM!

Header masking is applied last, after body processing, and correctly respects the logging configuration flags.

src/common/output.ts (3)

4-27: LGTM!

The OutputData type is well-structured with appropriate optional fields for flexible logging scenarios.


40-51: Note: Input data is mutated.

The function adds a toJSON property to the body Uint8Arrays, mutating the original OutputData. This is acceptable since the data is typically used once for logging, but worth noting if the data might be reused.


53-63: LGTM!

The logData function correctly implements a fallback strategy when the log message exceeds the Cloudflare Workers limit, stripping bodies to ensure the log is emitted.

src/common/response.ts (4)

16-32: LGTM!

Good handling of the edge case where response.body is null, returning a completed capture with zero size immediately.


39-53: LGTM!

The TransformStream efficiently captures chunks while streaming, with good memory management by clearing the chunks array when the size limit is exceeded.


72-82: Clarify: The timeout only applies before the first chunk is read.

The readTimeout is designed to handle cases where the response body never starts streaming (e.g., a hung connection). Once reading begins, the timeout will not trigger even if subsequent reads are slow. This is appropriate behavior for streaming responses.


84-93: LGTM!

Good use of a new Response to proxy the original while allowing body interception. The Bun workaround comment is helpful for understanding the edge case.

src/common/consumers.ts (2)

1-5: LGTM!

Clean type definition with appropriate optional fields supporting both undefined and explicit null values.


17-43: LGTM!

The function correctly handles all input variants with proper normalization (trimming, length truncation) and implements a sensible deduplication strategy to reduce payload size on repeated consumer references. The early-return pattern keeps the control flow clear.

@codecov
Copy link

codecov bot commented Dec 21, 2025

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

Thanks for integrating Codecov - We've got you covered ☂️

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/hono/utils.ts (1)

35-42: Consider logging errors for debugging.

While the silent catch is intentional for cross-platform compatibility, logging unexpected errors could aid debugging when running outside Cloudflare Workers.

🔎 Suggested improvement with optional logging
 export function tryWaitUntil(c: Context, promise: Promise<unknown>) {
   try {
     c.executionCtx.waitUntil(promise);
   } catch (error) {
     // Execution context is only available in Cloudflare Workers,
     // but not on other platforms or in unit tests.
+    // Silently ignore expected errors in non-Cloudflare environments
   }
 }

Or if you want to aid debugging:

 export function tryWaitUntil(c: Context, promise: Promise<unknown>) {
   try {
     c.executionCtx.waitUntil(promise);
   } catch (error) {
     // Execution context is only available in Cloudflare Workers,
     // but not on other platforms or in unit tests.
+    // Uncomment for debugging: console.debug("waitUntil not available:", error);
   }
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d24520d and ad5dfce.

📒 Files selected for processing (9)
  • LICENSE (1 hunks)
  • eslint.config.js (1 hunks)
  • src/common/bytes.ts (1 hunks)
  • src/common/output.ts (1 hunks)
  • src/hono/utils.ts (1 hunks)
  • tests/hono/app.test.ts (1 hunks)
  • tests/hono/app.ts (1 hunks)
  • tsconfig.json (1 hunks)
  • vitest.config.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • LICENSE
🚧 Files skipped from review as they are similar to previous changes (3)
  • vitest.config.ts
  • tsconfig.json
  • tests/hono/app.test.ts
🧰 Additional context used
🧬 Code graph analysis (3)
tests/hono/app.ts (3)
src/hono/middleware.ts (1)
  • useApitally (17-96)
src/hono/index.ts (2)
  • useApitally (3-3)
  • setConsumer (4-4)
src/hono/utils.ts (1)
  • setConsumer (24-29)
src/common/output.ts (2)
src/common/consumers.ts (1)
  • ApitallyConsumer (1-5)
src/common/bytes.ts (1)
  • bytesToBase64 (20-27)
src/hono/utils.ts (1)
src/common/consumers.ts (2)
  • ApitallyConsumer (1-5)
  • consumerFromStringOrObject (17-43)
🔇 Additional comments (13)
src/common/bytes.ts (5)

1-3: LGTM!

Standard TextEncoder usage for converting strings to bytes.


5-7: LGTM!

Standard TextDecoder usage for converting bytes to strings.


9-18: LGTM!

Efficient implementation using set() for concatenating multiple Uint8Array instances.


20-27: Excellent fix addressing the previous stack overflow concern!

The chunked implementation with 4096-byte segments safely handles large byte arrays by avoiding excessive function arguments. The approach is both correct and efficient.


29-36: LGTM!

Correct base64 decoding implementation.

src/hono/utils.ts (3)

8-22: LGTM!

The endpoint discovery logic correctly filters out middleware and non-standard HTTP methods (ALL, HEAD, OPTIONS).


24-29: LGTM!

Correctly stores the consumer in the Hono context with proper falsy value normalization.


31-33: LGTM!

Correctly retrieves and normalizes the consumer from the context.

src/common/output.ts (3)

4-27: LGTM!

Comprehensive type definition covering all necessary fields for the log payload.


29-38: Excellent fix resolving the previous stack overflow concern!

By delegating to the now-chunked bytesToBase64 function, this implementation safely handles large compressed payloads without risking call stack limits.


40-63: LGTM!

The implementation correctly handles large payloads by defensively removing bodies when the compressed message exceeds the safe threshold. The toJSON approach provides elegant custom serialization.

eslint.config.js (1)

1-19: LGTM!

Standard ESLint flat config setup with reasonable rule customizations for TypeScript. The unused variable patterns appropriately ignore placeholder args and standard error variable names.

tests/hono/app.ts (1)

8-61: LGTM!

Well-structured test application covering various scenarios: validation, path/query params, JSON body, error handling, and streaming responses. The comprehensive logging configuration is appropriate for testing the Apitally middleware.

@itssimon itssimon merged commit b26b23d into main Dec 21, 2025
7 checks passed
@itssimon itssimon deleted the hono branch December 21, 2025 11:07
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