growth(day-24): @byok-relay/solid — SolidJS reactive stores for BYOK AI#62
growth(day-24): @byok-relay/solid — SolidJS reactive stores for BYOK AI#62alokit-bot wants to merge 1 commit into
Conversation
packages/solid/src/index.js: four stores with SolidJS signal contract ([getter,setter]): - createByokRelayStore: token registration + key CRUD + logout (localStorage persistence, SolidStart SSR-safe via typeof window guard) - createChatStore: stateful message list, non-streaming, openai/anthropic/groq/mistral/openrouter, systemPrompt + extraParams - createStreamingChatStore: SSE streaming with AbortController, stopStreaming(), streamingContent() live signal, partial-commit on abort - createRelayHealthStore: polls /health on configurable interval, refetch(), destroy() for onCleanup() lifecycle management Signal shim: all stores work without solid-js installed (signals degrade to plain getter/setter pairs for testing and non-Solid environments). 20 smoke tests passing (node test/stores.test.js). packages/solid/README.md: full API docs, SolidJS quick-start, SolidStart SSR guide, all four stores documented, provider table, self-hosting section, related packages table. llms.txt: SolidJS Signals section + npm link. README.md: SolidJS stores section before 'For AI coding agents' with install + streaming example + links to other framework packages. package.json: workspaces added. metrics/daily.jsonl: 2026-07-03 snapshot (stars=52 forks=0 watchers=1 clones=110 views=20). Next for Avi: cd packages/solid && npm publish --access public
📝 WalkthroughWalkthroughAdds a new ChangesSolidJS package addition
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Component
participant createStreamingChatStore
participant RelayServer
Component->>createStreamingChatStore: sendMessage(text)
createStreamingChatStore->>RelayServer: fetch /relay/:provider (SSE)
RelayServer-->>createStreamingChatStore: SSE data chunks
createStreamingChatStore->>createStreamingChatStore: parseSSE + extractDelta
createStreamingChatStore-->>Component: update streamingContent()
Component->>createStreamingChatStore: stopStreaming()
createStreamingChatStore->>RelayServer: AbortController.abort()
createStreamingChatStore-->>Component: commit partial assistant message
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (2)
packages/solid/test/stores.test.js (2)
342-348: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winTest doesn't actually cover mid-stream abort.
Despite the name "aborts in-flight stream," this only calls
stopStreaming()on an idle store (nosendMessage()in flight), verifying just the no-op path. The mockedAbortControllerhere also never propagates anAbortErrorthroughfetch/reader.read(), so the actual abort-and-commit-partial-content logic increateStreamingChatStore.stopStreaming()(packages/solid/src/index.js,stopStreaming) remains untested.Consider driving a real in-flight scenario: delay
reader.read()resolution, callstopStreaming()mid-await, and assert the partialstreamingContent()is committed tomessages().🤖 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 `@packages/solid/test/stores.test.js` around lines 342 - 348, The current test only exercises the idle no-op path for stopStreaming() and does not verify a real in-flight abort. Update the test in stores.test.js to drive an active sendMessage() flow through createStreamingChatStore, pause or delay the mocked reader.read() so stopStreaming() can be called mid-stream, and then assert that stopStreaming() causes the partial streaming content to be committed into messages() while loading() is cleared. Use the existing createStreamingChatStore and stopStreaming() symbols so the test covers the actual abort-and-commit behavior in packages/solid/src/index.js.
367-415: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winFixed-delay waits for async polling are flake-prone.
Tests rely on hardcoded
setTimeoutdelays (Lines 374, 384, 409, 412) to assume the initial asyncrefetch()or interval tick has completed. Under CI load these arbitrary sleep durations can race with the actual fetch resolution, causing intermittent failures.Example: poll for condition instead of fixed sleep
- const s = createRelayHealthStore({ intervalMs: 0 }); - // Wait for the initial async fetch - await new Promise(r => setTimeout(r, 10)); - assertEqual(s.status(), 'ok'); + const s = createRelayHealthStore({ intervalMs: 0 }); + for (let i = 0; i < 50 && s.status() === 'loading'; i++) { + await new Promise(r => setTimeout(r, 5)); + } + assertEqual(s.status(), 'ok');🤖 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 `@packages/solid/test/stores.test.js` around lines 367 - 415, The tests in createRelayHealthStore are using fixed setTimeout sleeps to wait for async polling, which can race under load. Replace the hardcoded waits in the initial fetch, fetch failure, refetch(), and destroy() cases with polling/assertion loops that wait until the store state changes (for example, status(), health(), or callCount) before continuing. Keep the existing test structure and symbols like createRelayHealthStore, refetch, and destroy, but make the waits condition-based instead of time-based.
🤖 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 `@packages/solid/package.json`:
- Line 54: The package metadata in packages/solid/package.json has a license
value that conflicts with the repository’s SPDX declaration. Update the
package’s license field to match the root package metadata so the published
package uses the same license identifier and avoids inconsistent metadata.
- Around line 24-28: The package manifest still exposes a stale module field in
the package.json for the solid package, and it points to the CommonJS entry used
by src/index.js. Remove the module entry from package.json, or replace it with a
real ESM build and expose that through exports; keep the main and exports fields
consistent with the actual entrypoint.
In `@packages/solid/README.md`:
- Around line 198-207: The Supported providers table in the Solid README
includes an unsupported google provider entry, which does not match the package
contract. Remove the Google Gemini row from the table in README.md and keep only
the provider values exposed by the current API: openai, anthropic, groq,
mistral, and openrouter. Use the Supported providers section to verify the list
stays aligned with the actual Solid provider contract.
- Around line 30-79: The SolidJS quick-start example is missing the `For` and
`Show` imports even though `App` uses them, so update the snippet’s import from
`solid-js` to include those control-flow helpers. Also correct the
supported-providers table in the README example to remove `google` and list only
the providers actually exposed by this package: `openai`, `anthropic`, `groq`,
`mistral`, and `openrouter`.
In `@packages/solid/src/index.js`:
- Around line 416-425: The sendMessage flow is clearing shared streaming state
from an older aborted request; in the sendMessage/finally path, capture the
current AbortController in a local variable before replacing abortController and
only set abortController to null and loading to false if that controller is
still the active one. Update the same guard in the stopStreaming-related cleanup
so a prior request finishing cannot clobber the newer stream’s state.
- Around line 113-120: The parseSSE generator currently treats each chunk
independently, so partial SSE frames split across reader.read() calls are lost
or misparsed. Update parseSSE to keep an internal buffer across invocations,
accumulate decoded text until a full SSE frame boundary is available, and only
then process data: lines; preserve any trailing incomplete frame in the buffer
for the next chunk. Use the existing parseSSE function and its data parsing flow
as the place to implement the streaming-safe framing logic.
- Around line 42-45: The Solid shim currently depends on host-injected globals
in createSignal and createStore, which breaks reactivity unless those globals
are preloaded. Update the logic in packages/solid/src/index.js to import and use
Solid’s real APIs from solid-js and solid-js/store when available, and only fall
back to the existing plain getter/setter implementations for non-Solid
environments. Keep the current function names createSignal and createStore as
the lookup points for the change.
In `@README.md`:
- Around line 28-47: The README example is missing the Solid control-flow
imports needed by the App snippet. Update the example that uses
createByokRelayStore and createStreamingChatStore so it also imports For and
Show from the Solid library, ensuring the JSX compiles and runs as shown.
---
Nitpick comments:
In `@packages/solid/test/stores.test.js`:
- Around line 342-348: The current test only exercises the idle no-op path for
stopStreaming() and does not verify a real in-flight abort. Update the test in
stores.test.js to drive an active sendMessage() flow through
createStreamingChatStore, pause or delay the mocked reader.read() so
stopStreaming() can be called mid-stream, and then assert that stopStreaming()
causes the partial streaming content to be committed into messages() while
loading() is cleared. Use the existing createStreamingChatStore and
stopStreaming() symbols so the test covers the actual abort-and-commit behavior
in packages/solid/src/index.js.
- Around line 367-415: The tests in createRelayHealthStore are using fixed
setTimeout sleeps to wait for async polling, which can race under load. Replace
the hardcoded waits in the initial fetch, fetch failure, refetch(), and
destroy() cases with polling/assertion loops that wait until the store state
changes (for example, status(), health(), or callCount) before continuing. Keep
the existing test structure and symbols like createRelayHealthStore, refetch,
and destroy, but make the waits condition-based instead of time-based.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 46e1b3bb-fa6d-47ba-a65e-ee573871121f
📒 Files selected for processing (7)
README.mdllms.txtpackage.jsonpackages/solid/README.mdpackages/solid/package.jsonpackages/solid/src/index.jspackages/solid/test/stores.test.js
| "main": "src/index.js", | ||
| "module": "src/index.js", | ||
| "exports": { | ||
| ".": "./src/index.js" | ||
| }, |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify package entrypoint formats and exported files.
printf '\nPackage metadata:\n'
cat packages/solid/package.json | jq '{main,module,exports,type}'
printf '\nImplementation export style:\n'
rg -n -C2 'module\.exports|export\s+\{' packages/solid/src/index.jsRepository: avikalpg/byok-relay
Length of output: 538
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'packages/solid/package.json:\n'
cat -n packages/solid/package.json
printf '\npackages/solid/src/index.js exports:\n'
sed -n '555,570p' packages/solid/src/index.js
printf '\nroot package.json type/module hints:\n'
cat -n package.json | sed -n '1,120p'Repository: avikalpg/byok-relay
Length of output: 2786
Remove the stale module entry
Line 25 points module at a CommonJS file (module.exports). Remove the field or publish a real ESM entry and wire it through exports.
🤖 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 `@packages/solid/package.json` around lines 24 - 28, The package manifest still
exposes a stale module field in the package.json for the solid package, and it
points to the CommonJS entry used by src/index.js. Remove the module entry from
package.json, or replace it with a real ESM build and expose that through
exports; keep the main and exports fields consistent with the actual entrypoint.
| "bugs": { | ||
| "url": "https://github.com/avikalpg/byok-relay/issues" | ||
| }, | ||
| "license": "MIT", |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Resolve the package license mismatch.
Line 54 declares this package as MIT, while the root package metadata declares Apache-2.0. If this package is meant to inherit the repository license, publish it with the same SPDX value to avoid conflicting package metadata.
Proposed fix
- "license": "MIT",
+ "license": "Apache-2.0",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "license": "MIT", | |
| "license": "Apache-2.0", |
🤖 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 `@packages/solid/package.json` at line 54, The package metadata in
packages/solid/package.json has a license value that conflicts with the
repository’s SPDX declaration. Update the package’s license field to match the
root package metadata so the published package uses the same license identifier
and avoids inconsistent metadata.
| ```jsx | ||
| // App.jsx (SolidJS) | ||
| import { createSignal } from 'solid-js'; | ||
| import { createByokRelayStore, createStreamingChatStore } from '@byok-relay/solid'; | ||
|
|
||
| export default function App() { | ||
| const relay = createByokRelayStore({ appId: 'my-app' }); | ||
| const chat = createStreamingChatStore({ provider: 'openai', model: 'gpt-4o-mini' }); | ||
|
|
||
| const [apiKey, setApiKey] = createSignal(''); | ||
| const [input, setInput] = createSignal(''); | ||
|
|
||
| async function handleSaveKey() { | ||
| await relay.storeKey('openai', apiKey()); | ||
| } | ||
|
|
||
| async function handleSend() { | ||
| if (!relay.token()) await relay.register(); | ||
| await chat.sendMessage(input(), relay.token()); | ||
| setInput(''); | ||
| } | ||
|
|
||
| return ( | ||
| <div> | ||
| {/* API key setup */} | ||
| <Show when={!relay.providers().includes('openai')}> | ||
| <input | ||
| type="password" | ||
| placeholder="Paste your OpenAI API key" | ||
| onInput={e => setApiKey(e.target.value)} | ||
| /> | ||
| <button onClick={handleSaveKey}>Save key</button> | ||
| </Show> | ||
|
|
||
| {/* Chat */} | ||
| <For each={chat.messages()}> | ||
| {msg => <p><strong>{msg.role}</strong>: {msg.content}</p>} | ||
| </For> | ||
| <Show when={chat.streamingContent()}> | ||
| <p><strong>assistant</strong>: {chat.streamingContent()}<span>▋</span></p> | ||
| </Show> | ||
|
|
||
| <input value={input()} onInput={e => setInput(e.target.value)} /> | ||
| <button onClick={handleSend} disabled={chat.loading()}>Send</button> | ||
| <Show when={chat.loading()}> | ||
| <button onClick={chat.stopStreaming}>Stop</button> | ||
| </Show> | ||
| </div> | ||
| ); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the relevant README section with line numbers.
sed -n '1,140p' packages/solid/README.md | cat -n
# Look for provider mentions in the Solid package docs.
rg -n "Google Gemini|provider: google|openai|anthropic|groq|mistral|openrouter" packages/solid/README.md packages/solid -g '!**/node_modules/**'Repository: avikalpg/byok-relay
Length of output: 11550
Import the control-flow helpers and fix the provider table.
ForandShoware used in the quick-start example but never imported, so the snippet won’t run as written.- The supported-providers table also lists
google, but this package only exposesopenai,anthropic,groq,mistral, andopenrouter.
🤖 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 `@packages/solid/README.md` around lines 30 - 79, The SolidJS quick-start
example is missing the `For` and `Show` imports even though `App` uses them, so
update the snippet’s import from `solid-js` to include those control-flow
helpers. Also correct the supported-providers table in the README example to
remove `google` and list only the providers actually exposed by this package:
`openai`, `anthropic`, `groq`, `mistral`, and `openrouter`.
| ## Supported providers | ||
|
|
||
| | Provider | `provider` value | Notes | | ||
| |---|---|---| | ||
| | OpenAI | `openai` | GPT-4o, GPT-4o-mini, o1, o3-mini | | ||
| | Anthropic | `anthropic` | Claude 3.5 Haiku, Sonnet, Opus | | ||
| | Google Gemini | `google` | gemini-1.5-flash, gemini-2.0-flash | | ||
| | Groq | `groq` | Llama 3, Mixtral (fast inference) | | ||
| | Mistral | `mistral` | mistral-large-latest | | ||
| | OpenRouter | `openrouter` | Any model via openrouter.ai | |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Remove the unsupported google provider row.
The Solid package contract documented in the source only exposes openai, anthropic, groq, mistral, and openrouter. Listing google here advertises a provider value the current API does not accept.
Fix
-| Google Gemini | `google` | gemini-1.5-flash, gemini-2.0-flash |📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ## Supported providers | |
| | Provider | `provider` value | Notes | | |
| |---|---|---| | |
| | OpenAI | `openai` | GPT-4o, GPT-4o-mini, o1, o3-mini | | |
| | Anthropic | `anthropic` | Claude 3.5 Haiku, Sonnet, Opus | | |
| | Google Gemini | `google` | gemini-1.5-flash, gemini-2.0-flash | | |
| | Groq | `groq` | Llama 3, Mixtral (fast inference) | | |
| | Mistral | `mistral` | mistral-large-latest | | |
| | OpenRouter | `openrouter` | Any model via openrouter.ai | | |
| ## Supported providers | |
| | Provider | `provider` value | Notes | | |
| |---|---|---| | |
| | OpenAI | `openai` | GPT-4o, GPT-4o-mini, o1, o3-mini | | |
| | Anthropic | `anthropic` | Claude 3.5 Haiku, Sonnet, Opus | | |
| | Groq | `groq` | Llama 3, Mixtral (fast inference) | | |
| | Mistral | `mistral` | mistral-large-latest | | |
| | OpenRouter | `openrouter` | Any model via openrouter.ai | |
🤖 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 `@packages/solid/README.md` around lines 198 - 207, The Supported providers
table in the Solid README includes an unsupported google provider entry, which
does not match the package contract. Remove the Google Gemini row from the table
in README.md and keep only the provider values exposed by the current API:
openai, anthropic, groq, mistral, and openrouter. Use the Supported providers
section to verify the list stays aligned with the actual Solid provider
contract.
| function createSignal(initial) { | ||
| // Use native SolidJS signals when available (works inside components/stores) | ||
| if (typeof globalThis !== 'undefined' && globalThis.__solidCreateSignal) { | ||
| return globalThis.__solidCreateSignal(initial); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether the repository defines the globals this shim expects.
rg -n -C3 '__solidCreateSignal|__solidCreateStore|createSignal|createStore' .Repository: avikalpg/byok-relay
Length of output: 5574
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== package manifests ==\n'
git ls-files 'packages/solid/**/package.json' 'package.json' 'packages/solid/README.md' 'packages/solid/src/index.js'
printf '\n== root package.json ==\n'
cat -n package.json | sed -n '1,220p'
printf '\n== packages/solid/package.json ==\n'
cat -n packages/solid/package.json | sed -n '1,220p'
printf '\n== packages/solid/src/index.js (1-140) ==\n'
cat -n packages/solid/src/index.js | sed -n '1,140p'
printf '\n== packages/solid/README.md (1-120) ==\n'
cat -n packages/solid/README.md | sed -n '1,120p'Repository: avikalpg/byok-relay
Length of output: 12616
Import Solid's APIs instead of relying on host-injected globals. Both shims only become reactive when globalThis.__solidCreateSignal / globalThis.__solidCreateStore are preloaded; otherwise Solid apps silently fall back to plain getters/setters and won't rerender. Load solid-js / solid-js/store when available and keep the fallback for non-Solid usage.
🤖 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 `@packages/solid/src/index.js` around lines 42 - 45, The Solid shim currently
depends on host-injected globals in createSignal and createStore, which breaks
reactivity unless those globals are preloaded. Update the logic in
packages/solid/src/index.js to import and use Solid’s real APIs from solid-js
and solid-js/store when available, and only fall back to the existing plain
getter/setter implementations for non-Solid environments. Keep the current
function names createSignal and createStore as the lookup points for the change.
| function* parseSSE(chunk) { | ||
| const lines = chunk.split('\n'); | ||
| for (const line of lines) { | ||
| if (!line.startsWith('data:')) continue; | ||
| const data = line.slice(5).trim(); | ||
| if (data === '[DONE]') { yield null; continue; } | ||
| try { yield JSON.parse(data); } catch { /* skip malformed */ } | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Preserve partial SSE frames across chunks.
parseSSE() parses each decoded chunk independently, so a JSON data: frame split across two reader.read() calls is treated as malformed and dropped. Keep a buffer and parse only complete SSE frames.
Proposed fix direction
- while (true) {
+ let sseBuffer = '';
+ while (true) {
const { done, value } = await reader.read();
if (done) break;
- const chunk = decoder.decode(value, { stream: true });
- for (const event of parseSSE(chunk)) {
+ sseBuffer += decoder.decode(value, { stream: true });
+ const frames = sseBuffer.split(/\r?\n\r?\n/);
+ sseBuffer = frames.pop() || '';
+ for (const event of frames.flatMap(frame => [...parseSSE(frame)])) {
const delta = extractDelta(event, provider);
if (delta) {
accumulated += delta;
setStreamingContent(accumulated);
}
}
}Also applies to: 459-470
🤖 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 `@packages/solid/src/index.js` around lines 113 - 120, The parseSSE generator
currently treats each chunk independently, so partial SSE frames split across
reader.read() calls are lost or misparsed. Update parseSSE to keep an internal
buffer across invocations, accumulate decoded text until a full SSE frame
boundary is available, and only then process data: lines; preserve any trailing
incomplete frame in the buffer for the next chunk. Use the existing parseSSE
function and its data parsing flow as the place to implement the streaming-safe
framing logic.
| // Cancel any in-flight stream | ||
| if (abortController) abortController.abort(); | ||
|
|
||
| const userMsg = { role: 'user', content: content.trim() }; | ||
| setMessages(prev => [...prev, userMsg]); | ||
| setLoading(true); | ||
| setStreamingContent(''); | ||
| setError(null); | ||
|
|
||
| abortController = new AbortController(); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Avoid letting an aborted prior stream clear the active stream.
A second sendMessage() aborts the previous controller, then replaces abortController. When the old request reaches finally, it unconditionally sets abortController = null and loading = false, which can break stopStreaming() and loading state for the newer request. Capture the controller locally and only clear state if it is still current.
Proposed fix
- abortController = new AbortController();
+ const controller = new AbortController();
+ abortController = controller;
@@
- signal: abortController.signal,
+ signal: controller.signal,
@@
- abortController = null;
- setLoading(false);
+ if (abortController === controller) {
+ abortController = null;
+ setLoading(false);
+ }Also applies to: 481-483
🧰 Tools
🪛 ast-grep (0.44.0)
[error] 419-419: React's useState should not be directly called
Context: setMessages(prev => [...prev, userMsg])
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.
(usestate-direct-usage)
[error] 420-420: React's useState should not be directly called
Context: setLoading(true)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.
(usestate-direct-usage)
[error] 421-421: React's useState should not be directly called
Context: setStreamingContent('')
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.
(usestate-direct-usage)
[error] 422-422: React's useState should not be directly called
Context: setError(null)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.
(usestate-direct-usage)
🤖 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 `@packages/solid/src/index.js` around lines 416 - 425, The sendMessage flow is
clearing shared streaming state from an older aborted request; in the
sendMessage/finally path, capture the current AbortController in a local
variable before replacing abortController and only set abortController to null
and loading to false if that controller is still the active one. Update the same
guard in the stopStreaming-related cleanup so a prior request finishing cannot
clobber the newer stream’s state.
| ```jsx | ||
| import { createByokRelayStore, createStreamingChatStore } from '@byok-relay/solid'; | ||
|
|
||
| function App() { | ||
| const relay = createByokRelayStore({ appId: 'my-app' }); | ||
| const chat = createStreamingChatStore({ provider: 'openai', model: 'gpt-4o-mini' }); | ||
|
|
||
| async function send(text) { | ||
| if (!relay.token()) await relay.register(); | ||
| await chat.sendMessage(text, relay.token()); | ||
| } | ||
|
|
||
| return ( | ||
| <> | ||
| <For each={chat.messages()}>{msg => <p>{msg.role}: {msg.content}</p>}</For> | ||
| <Show when={chat.streamingContent()}><p>assistant: {chat.streamingContent()}▋</p></Show> | ||
| </> | ||
| ); | ||
| } | ||
| ``` |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the README and inspect the relevant section with line numbers.
git ls-files README.md
echo '---'
cat -n README.md | sed -n '1,90p'Repository: avikalpg/byok-relay
Length of output: 4741
Import the missing Solid control-flow helpers.
The example uses <For> and <Show>, but the snippet only imports the BYOK relay stores, so it won’t run as written in a standard Solid app.
Fix
+import { For, Show } from 'solid-js';
import { createByokRelayStore, createStreamingChatStore } from '`@byok-relay/solid`';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ```jsx | |
| import { createByokRelayStore, createStreamingChatStore } from '@byok-relay/solid'; | |
| function App() { | |
| const relay = createByokRelayStore({ appId: 'my-app' }); | |
| const chat = createStreamingChatStore({ provider: 'openai', model: 'gpt-4o-mini' }); | |
| async function send(text) { | |
| if (!relay.token()) await relay.register(); | |
| await chat.sendMessage(text, relay.token()); | |
| } | |
| return ( | |
| <> | |
| <For each={chat.messages()}>{msg => <p>{msg.role}: {msg.content}</p>}</For> | |
| <Show when={chat.streamingContent()}><p>assistant: {chat.streamingContent()}▋</p></Show> | |
| </> | |
| ); | |
| } | |
| ``` | |
| import { For, Show } from 'solid-js'; | |
| import { createByokRelayStore, createStreamingChatStore } from '`@byok-relay/solid`'; | |
| function App() { | |
| const relay = createByokRelayStore({ appId: 'my-app' }); | |
| const chat = createStreamingChatStore({ provider: 'openai', model: 'gpt-4o-mini' }); | |
| async function send(text) { | |
| if (!relay.token()) await relay.register(); | |
| await chat.sendMessage(text, relay.token()); | |
| } | |
| return ( | |
| <> | |
| <For each={chat.messages()}>{msg => <p>{msg.role}: {msg.content}</p>}</For> | |
| <Show when={chat.streamingContent()}><p>assistant: {chat.streamingContent()}▋</p></Show> | |
| </> | |
| ); | |
| } |
🤖 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 `@README.md` around lines 28 - 47, The README example is missing the Solid
control-flow imports needed by the App snippet. Update the example that uses
createByokRelayStore and createStreamingChatStore so it also imports For and
Show from the Solid library, ensuring the JSX compiles and runs as shown.
Growth Day 24 —
@byok-relay/solidSolidJS reactive stores for byok-relay. Natural follow-on to
@byok-relay/react(Day 21),@byok-relay/vue(Day 22), and@byok-relay/svelte(Day 23). Targets the SolidJS/SolidStart ecosystem.What ships
packages/solid/src/index.js— four stores with SolidJS[getter, setter]signal contract:createByokRelayStore— token registration + key CRUD + logout, localStorage persistence, SolidStart SSR-safe (typeof windowguard)createChatStore— stateful message list, non-streaming, openai/anthropic/groq/mistral/openrouter,systemPrompt+extraParamscreateStreamingChatStore— SSE streaming with AbortController,stopStreaming(),streamingContent()live signal, partial-commit on abortcreateRelayHealthStore— polls/healthon configurable interval,refetch(),destroy()foronCleanup()lifecycle managementSignal shim: all stores work without
solid-jsinstalled (signals degrade to plain getter/setter pairs) — enables testing in plain Node and use in non-Solid environments.packages/solid/test/stores.test.js— 20 smoke tests, all passing (node test/stores.test.js).packages/solid/README.md— full API docs, SolidJS quick-start, SolidStart SSR guide, all four stores documented, provider table, self-hosting section, related packages table.llms.txt— SolidJS Signals section + npm link.README.md— SolidJS stores section before "For AI coding agents" with install snippet + streaming example + links to other framework packages.package.json—workspaces: ["packages/*"]added.Metrics (2026-07-03): stars=52 forks=0 watchers=1 clones=110 views=20.
Next for Avi
Summary by CodeRabbit
New Features
Documentation
Tests