Skip to content

Conversation

marcalexiei
Copy link

@marcalexiei marcalexiei commented Oct 9, 2025


  • Refines scalar option property to make all properties from ApiReferenceConfiguration optional
  • To allow scalar option function properties I adapted the code from scalar core package getScriptTags function
    Basically now instead of using a tag with a stringified json a script is generated with both static and function properties

Summary by CodeRabbit

  • New Features
    • Requests are now logged (method and URL) before they are handled.
  • Refactor
    • Streamlined how the API Reference scripts are injected, improving reliability of embedding and CDN loading.
    • Configuration for the API Reference is more flexible; most fields are now optional and support complex values (including arrays and functions) where applicable.
  • Chores
    • Removed an obsolete schema-embedding option from the example configuration (no impact on routes or responses).

Copy link

coderabbitai bot commented Oct 9, 2025

Walkthrough

Introduces a pre-request hook for Scalar via scalar.onBeforeRequest in the example, removes embedSchema usage, refactors Scalar script injection by adding getScriptTags and delegating rendering to it, and relaxes the scalar configuration type to Partial while keeping version/cdn optional.

Changes

Cohort / File(s) Summary
Example usage adjustments
example/index.ts
Removed embedSchema option. Kept mapJsonSchema.zod mapping. Added scalar.onBeforeRequest to log request method and URL before handling.
Scalar rendering refactor
src/scalar/index.ts
Added non-exported serializeArrayWithFunctions. Added exported getScriptTags to generate script tags from config (handling function values/arrays). Updated ScalarRender to use getScriptTags and inject div#app plus generated scripts, passing content via embedSpec. Removed direct ApiReferenceConfiguration import usage in this file.
Type shape update for scalar config
src/types.ts
Changed ElysiaOpenAPIConfig.scalar from ApiReferenceConfiguration & { version?; cdn? } to Partial & { version?; cdn? }, making ApiReferenceConfiguration fields optional.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant Server
  participant ScalarHook as scalar.onBeforeRequest
  participant Handler as Route Handler

  Client->>Server: HTTP Request
  Server->>ScalarHook: Invoke with { request }
  Note right of ScalarHook: Logs method and URL
  ScalarHook-->>Server: Return (no response change)
  Server->>Handler: Proceed to route handling
  Handler-->>Client: Response
Loading
sequenceDiagram
  autonumber
  participant ScalarRender
  participant getScriptTags
  participant Browser

  ScalarRender->>getScriptTags: Call with { cdn, ...config, content: embedSpec }
  getScriptTags-->>ScalarRender: HTML script tags (config + loader)
  ScalarRender-->>Browser: Render <div id="app"> + script tags
  Note over Browser: Browser bootstraps API Reference using injected config
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Refactor Swagger to OpenAPI #235 — Also modifies src/scalar/index.ts and scalar config typing in src/types.ts, aligning script generation and configuration handling.

Poem

A bunny taps the keys—click, clack, click—
New hooks that log each traveler quick.
Scripts now gather, tidy, neat,
Configs optional—such a treat!
I hop through tags, with whiskers bright,
Refactors done—the docs take flight. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The title accurately describes the main feature but includes a leading emoji, which counts as unnecessary noise under the guidelines recommending avoidance of emojis in titles. Please remove the leading emoji and ensure the title remains concise and descriptive, for example: “feat(scalar): support function options.”
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues Check ✅ Passed The implementation makes all ApiReferenceConfiguration properties optional, adds serializeArrayWithFunctions and getScriptTags to preserve and execute function-valued scalar options, and updates the example to demonstrate onBeforeRequest logging, fully addressing issue #280’s requirements for runtime function support and type fixes.
Out of Scope Changes Check ✅ Passed All modifications are directly related to enabling function support in the scalar configuration and adjusting typings, with only a minor example update that does not introduce unrelated functionality.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 (3)
src/scalar/index.ts (3)

170-178: Simplify indentation logic to improve robustness.

The current indentation approach using string splitting, mapping, and regex replacement is fragile. Consider these edge cases:

  1. Empty restConfig produces { without proper structure until functionPropsString closes it
  2. Multi-line string values in config may break indentation alignment
  3. The regex .replace(/\s*}$/, '') assumes the closing brace is always at the end

Consider a simpler approach:

-  const configString = JSON.stringify(restConfig, null, 2)
-    .split('\n')
-    .map((line, index) => (index === 0 ? line : '      ' + line))
-    .join('\n')
-    .replace(/\s*}$/, '') // Remove the closing brace and any whitespace before it
-
-  const functionPropsString = functionProps.length
-    ? `,\n        ${functionProps.join(',\n        ')}\n      }`
-    : '}'
+  // Build config object parts
+  const restConfigString = JSON.stringify(restConfig, null, 2)
+  const configParts = [
+    restConfigString.slice(0, -1).trim(), // Remove closing brace
+    ...functionProps.map(prop => `  ${prop}`)
+  ]
+  
+  const finalConfig = configParts.length > 1 
+    ? `${configParts[0]},\n${configParts.slice(1).join(',\n')}\n}`
+    : '{}'

150-156: Improve type safety for configuration entry iteration.

The type assertion as [keyof typeof configuration, unknown][] is necessary because Object.entries loses type information, but this could be made safer.

Consider using a type guard or more explicit typing:

for (const key in configuration) {
  if (!configuration.hasOwnProperty(key)) continue
  const value = configuration[key as keyof typeof configuration]
  
  if (typeof value === 'function') {
    functionProps.push(`"${key}": ${value.toString()}`)
    delete restConfig[key as keyof typeof restConfig]
  } else if (Array.isArray(value) && value.some((item) => typeof item === 'function')) {
    functionProps.push(`"${key}": ${serializeArrayWithFunctions(value)}`)
    delete restConfig[key as keyof typeof restConfig]
  }
}

220-221: Avoid mutating the config object.

Object.assign(config, { content: embedSpec }) mutates the original config object, which could cause issues if the config is reused elsewhere.

Use object spread to create a new object:

-    ${getScriptTags(Object.assign(config, { content: embedSpec }) )}
+    ${getScriptTags({ ...config, content: embedSpec })}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 070919e and b1f692e.

📒 Files selected for processing (3)
  • example/index.ts (1 hunks)
  • src/scalar/index.ts (2 hunks)
  • src/types.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/scalar/index.ts (1)
src/types.ts (1)
  • ElysiaOpenAPIConfig (37-215)
🔇 Additional comments (3)
src/types.ts (1)

134-134: LGTM! Type change enables flexible scalar configuration.

The change from ApiReferenceConfiguration to Partial<ApiReferenceConfiguration> successfully addresses the type error issue mentioned in #280 while preserving the optional version and cdn fields. This allows users to provide only the scalar configuration properties they need, including function-valued properties.

src/scalar/index.ts (1)

180-188: LGTM! Script generation correctly implements Scalar initialization.

The template correctly generates both the CDN script tag and the initialization script. The use of Scalar.createApiReference with the merged configuration (including function properties) aligns with the Scalar API.

example/index.ts (1)

13-17: LGTM! Example correctly demonstrates function property support.

The addition of onBeforeRequest successfully demonstrates the new capability to pass function-valued properties in the scalar configuration. With the changes in getScriptTags, this function will be properly serialized and executed in the browser context, resolving the issue described in #280.

Comment on lines +125 to +127
const serializeArrayWithFunctions = (arr: unknown[]): string => {
return `[${arr.map((item) => (typeof item === 'function' ? item.toString() : JSON.stringify(item))).join(', ')}]`
}
Copy link

@coderabbitai coderabbitai bot Oct 9, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

❓ Verification inconclusive

Verify function serialization handles all function types.

The serializeArrayWithFunctions helper uses toString() to serialize functions. Ensure this approach correctly handles:

  • Arrow functions
  • Async functions
  • Generator functions
  • Methods with complex closures

Additionally, consider that toString() does not capture the closure scope, so functions referencing external variables may not work as expected when deserialized.

Run the following script to check if there are existing tests for function serialization:


🏁 Script executed:

#!/bin/bash
# Description: Search for tests related to function serialization in scalar configuration

# Search for test files that might cover getScriptTags or serializeArrayWithFunctions
rg -n "getScriptTags|serializeArrayWithFunctions|onBeforeRequest" --type ts --glob '*test*' --glob '*spec*'

Length of output: 0


Add unit tests for serializeArrayWithFunctions covering all function forms
serializeArrayWithFunctions relies on toString(), which may not serialize async/generator/arrow functions consistently and doesn’t capture closure scope. Add tests for:

  • Arrow functions
  • Async functions
  • Generator functions
  • Functions referencing external variables

Copy link
Author

Choose a reason for hiding this comment

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

I imagine this is a common pattern where you declare a function on the runtime (Bun) side that then gets injected into the webpage.
So, I wouldn’t add extra checks that could make the code more complex to maintain.

Copy link

Choose a reason for hiding this comment

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

@marcalexiei, understood! Your point about this being a common pattern for runtime-to-webpage function injection makes sense, and keeping the implementation straightforward is a reasonable trade-off.

🐰


✏️ Learnings added
Learnt from: marcalexiei
PR: elysiajs/elysia-openapi#281
File: src/scalar/index.ts:125-127
Timestamp: 2025-10-09T09:45:40.847Z
Learning: In the elysia-openapi repository, when serializing functions for injection from the runtime (Bun) side into webpages (like in serializeArrayWithFunctions in src/scalar/index.ts), the team prefers to keep the code simple and maintainable rather than adding extensive checks for different function types (arrow, async, generator) or edge cases, as this is a common pattern in their codebase.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

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.

scalar function options are not supported

1 participant