Skip to content

growth(day-24): @byok-relay/solid — SolidJS reactive stores for BYOK AI#62

Open
alokit-bot wants to merge 1 commit into
mainfrom
feat/growth-2026-07-03
Open

growth(day-24): @byok-relay/solid — SolidJS reactive stores for BYOK AI#62
alokit-bot wants to merge 1 commit into
mainfrom
feat/growth-2026-07-03

Conversation

@alokit-bot

@alokit-bot alokit-bot commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Growth Day 24 — @byok-relay/solid

SolidJS 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 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) — 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.jsonworkspaces: ["packages/*"] added.

Metrics (2026-07-03): stars=52 forks=0 watchers=1 clones=110 views=20.

Next for Avi

cd packages/solid && npm publish --access public

Summary by CodeRabbit

  • New Features

    • Added SolidJS package support for BYOK Relay, including chat, streaming chat, relay setup, and health tracking.
    • Introduced workspace support for the repository, making the package setup more flexible.
  • Documentation

    • Expanded the main docs and package README with SolidJS installation, quick-start, API usage, SSR-safe guidance, and deployment notes.
    • Added package catalog details for the new SolidJS integration.
  • Tests

    • Added automated coverage for SolidJS store behavior, including message flow, streaming updates, token handling, and health checks.

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
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new @byok-relay/solid package providing SolidJS reactive store factories (createByokRelayStore, createChatStore, createStreamingChatStore, createRelayHealthStore) for BYOK token/key management, chat, streaming chat, and health polling, along with package metadata, a test suite, workspace configuration, and documentation updates.

Changes

SolidJS package addition

Layer / File(s) Summary
Workspace and package metadata setup
package.json, packages/solid/package.json
Root package.json adds a workspaces array for packages/*; new packages/solid/package.json defines identity, entry points, exports, published files, test script, and solid-js peer dependency.
SolidJS store implementations
packages/solid/src/index.js
Implements signal/store shims, localStorage helpers, SSE parsing/delta extraction, and four exported store factories covering token/key registration, non-streaming chat, SSE streaming chat with abort support, and relay health polling.
Store test suite
packages/solid/test/stores.test.js
Adds a Node-only smoke test suite with fetch/SSE mocks, a custom test runner, and coverage for all four store factories including error and rollback paths.
Documentation updates
README.md, llms.txt, packages/solid/README.md
Adds SolidJS usage sections to root docs and a full package README covering install, quick-start, SSR usage, store APIs, providers, self-hosting, and license.

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
Loading

Suggested reviewers: avikalpg

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding SolidJS reactive stores for @byok-relay/solid.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/growth-2026-07-03

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (2)
packages/solid/test/stores.test.js (2)

342-348: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test doesn't actually cover mid-stream abort.

Despite the name "aborts in-flight stream," this only calls stopStreaming() on an idle store (no sendMessage() in flight), verifying just the no-op path. The mocked AbortController here also never propagates an AbortError through fetch/reader.read(), so the actual abort-and-commit-partial-content logic in createStreamingChatStore.stopStreaming() (packages/solid/src/index.js, stopStreaming) remains untested.

Consider driving a real in-flight scenario: delay reader.read() resolution, call stopStreaming() mid-await, and assert the partial streamingContent() is committed to messages().

🤖 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 win

Fixed-delay waits for async polling are flake-prone.

Tests rely on hardcoded setTimeout delays (Lines 374, 384, 409, 412) to assume the initial async refetch() 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

📥 Commits

Reviewing files that changed from the base of the PR and between f0e1a43 and 973137d.

📒 Files selected for processing (7)
  • README.md
  • llms.txt
  • package.json
  • packages/solid/README.md
  • packages/solid/package.json
  • packages/solid/src/index.js
  • packages/solid/test/stores.test.js

Comment on lines +24 to +28
"main": "src/index.js",
"module": "src/index.js",
"exports": {
".": "./src/index.js"
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ 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.js

Repository: 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",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 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.

Suggested change
"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.

Comment thread packages/solid/README.md
Comment on lines +30 to +79
```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>
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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.

  • For and Show are 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 exposes openai, anthropic, groq, mistral, and openrouter.
🤖 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`.

Comment thread packages/solid/README.md
Comment on lines +198 to +207
## 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 |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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.

Suggested change
## 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.

Comment on lines +42 to +45
function createSignal(initial) {
// Use native SolidJS signals when available (works inside components/stores)
if (typeof globalThis !== 'undefined' && globalThis.__solidCreateSignal) {
return globalThis.__solidCreateSignal(initial);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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.

Comment on lines +113 to +120
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 */ }
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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.

Comment on lines +416 to +425
// 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();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 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.

Comment thread README.md
Comment on lines +28 to +47
```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>
</>
);
}
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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.

Suggested change
```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.

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