chore: deep overhaul — fix on-device AI streaming, add tests + CI, modernize tooling & docs#27
Open
kuraydev wants to merge 6 commits into
Open
chore: deep overhaul — fix on-device AI streaming, add tests + CI, modernize tooling & docs#27kuraydev wants to merge 6 commits into
kuraydev wants to merge 6 commits into
Conversation
React Native's fetch has no streaming ReadableStream body, so every provider's response.body.getReader() threw 'No response body' and no tokens ever arrived. Add an XMLHttpRequest-based SSE transport with a cross-chunk line buffer (SSEParser) so tokens split across network packets are no longer dropped, and route all three providers through it. Also: type the OpenAI/Anthropic/Gemini wire formats (drop 'as any'), map stream errors to AIError with statusCode, honor the documented (but previously ignored) AIConfig.systemPrompt, and let Gemini respect config.baseURL. Public callback/contract shapes are unchanged.
- EventEmitter no longer imports Node's 'events' (not polyfilled by Metro and not installed); replace with a dependency-free impl, same public API. - Add missing tsconfig paths (@models, @Event-Emitter, bare @theme/@services/ @screens/@shared-components) and real stub folders for @api/@Local-Storage so every documented alias type-resolves. - Fix the tsconfig vs RN-base moduleResolution conflict (node -> bundler) that made tsc --noEmit error before it ever reached source. - Move *.png ambient module declarations to assets.d.ts so they stay global pattern-ambient modules (@assets/logo.png now type-resolves). - Drop 'route: any' and 'useEffect((): any =>' smells; scope LogBox down from ignoreAllLogs() to an explicit list.
Cover the SSE line buffer, all three providers (request shaping, response parsing, error mapping, streaming via a mock XHR), the service layer + system-prompt injection, the useAIChat/useAICompletion hooks, and the event emitter. Add a jest setup (native module mocks + gesture-handler/ reanimated) so the App smoke test runs, scope testMatch to *.test.*, and fix transformIgnorePatterns for the ESM-published deps. 46 tests, 9 suites.
Remove unused/dead deps (event, metro-react-native-babel-preset, eslint-plugin-flowtype, eslint-plugin-ft-flow, redundant @trivago import sorter) and the chalk/ora-based runner scripts + duplicate .prettierrc.js. Promote the transitively-relied-on @typescript-eslint parser/plugin and eslint-plugin-jest to direct devDeps. Add typecheck/lint:fix/format:check scripts, package metadata (description/license/author/repo/bugs/keywords), and run typecheck+lint in the husky pre-commit.
Add the missing MIT LICENSE, a Keep-a-Changelog CHANGELOG, CONTRIBUTING, and GitHub issue/PR templates. Fix the README: replace the misleading @Freakycoder npm badges with a CI badge + clone-and-go note, add Requirements + New Architecture/Expo notes, a Streaming & Security section documenting the RN fetch-streaming caveat and baseURL proxy guidance, a Troubleshooting section, accurate path-alias notes, and Contributing/ Changelog links.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This is a full overhaul of the React Native + TypeScript boilerplate. The headline fix: the advertised AI streaming feature never actually worked on a device/simulator — all three providers used
response.body.getReader(), but React Native'sfetchdoes not expose a streamingReadableStreambody, sostreamMessagethrew"No response body"immediately and no tokens ever arrived. Streaming now works through a new RN-compatibleXMLHttpRequest-based SSE transport, backed by a real test suite (46 tests, up from a single smoke test) and CI (there was none).Scope: the de-facto public surface of a clone-and-go template — the documented path aliases, the
useAIChat/useAICompletionreturn shapes, and thesendAIMessage/streamAIMessage/buildUserMessage/buildSystemMessageexports — is preserved. New exports are additive only.Commits (conventional, thematic):
fix(ai)— RN-compatible SSE streaming transport + buffering + typed wire bodiesfix(ts)— working event emitter, resolvable path aliases, clean typecheck gatetest— AI service / providers / hooks / SSE / event-emitter suitesci— typecheck/lint/format/test workflow (Node 22 & 24)chore(deps)— drop dead deps, make eslint deps explicit, fix metadatadocs— overhaul README + add LICENSE/CHANGELOG/CONTRIBUTING/templatesNon-breaking improvements
AI layer (source fixes)
src/services/ai/sse.ts: anXMLHttpRequestSSE transport whoseSSEParserholds a cross-chunk line buffer, fixing the second latent bug where adata:line split across two network reads was silently dropped (garbled/lost tokens).openai/anthropic/gemini) now share that transport and gained typed request/response wire shapes, replacingas anycasts.SSEError) into a typedAIErrorcarryingprovider+ HTTPstatusCode.Type & code-quality
src/navigation/index.tsx: removedroute: anyandReact.useEffect((): any => …), and dropped the no-op{(props) => <DetailScreen {...props} />}double-spread (DetailScreenis propless).App.tsx: replacedLogBox.ignoreAllLogs()(which hides real warnings) with a scopedLogBox.ignoreLogs([]).pathsnow match the babel aliases —@models,@event-emitter,@api,@local-storageresolve, with@api/@local-storagestub dirs added.*.png/jpg/…ambient modules moved toassets.d.tsso they stay global.Tooling & deps
typecheck(tsc --noEmit) andformat:checkscripts; husky pre-commit now runs typecheck + lint instead of the removed customsrc/scripts/terminal/*.mjsrunners.@typescript-eslint/parser,@typescript-eslint/eslint-plugin, andeslint-plugin-jestexplicit devDeps (latent break removed). Consolidated duplicate prettier configs.kuraydev. npm name unchanged;private: trueretained.CI / tests / docs / hygiene
.github/workflows/ci.yml: install + typecheck + lint +prettier --check+ test on a Node 22.x/24.x matrix.baseURL), Troubleshooting (the fetch-streaming caveat), corrected path-alias table, kuraydev badges + CI badge.LICENSE(MIT — README claimed it but no file existed),CHANGELOG.md,CONTRIBUTING.md, issue templates, and a PR template.Behavioral changes (recommend a MINOR template bump 0.0.1 → 0.1.0; do NOT publish to npm)
onError/"No response body".IAIProviderand theonToken/onComplete/onErrorshapes are unchanged.events(Metro never polyfilled it, so it never worked); it's now a dependency-freeEventEmitterwith the same public API, and the unusedeventdependency was removed.AIConfig.systemPromptis now injected forsendAIMessagetoo (was a no-op there); duplicate injection is guarded.metro-react-native-babel-preset,eslint-plugin-flowtype,eslint-plugin-ft-flow,@trivago/prettier-plugin-sort-imports,event).Deliberately not done (per the backward-compat risk list): no
exports/files/peerDependencieslibrary fields (this is an app template, not a published lib), no ESLint 9 flat-config migration (RN 0.84's@react-native/eslint-configis eslintrc-based; a migration risks breaking forkers' pre-commit), and no native-dep version bumps /newArchEnabledflips (the current pin set builds as a unit).Verification
npm install --legacy-peer-deps→ 1023 pkgs (EBADENGINE warning only; local Node 20 vs engines >=22.11, CI uses 22/24)tsc --noEmit(project-pinned TypeScript 5.9.3), 0 errorseslint .exit 0 (ESLint 8.57.1)prettier --check "src/**/*.{js,ts,tsx}"Native iOS/Android device builds were not run here (no SDK/simulator in the environment); the streaming fix is unit-tested against a mocked
XMLHttpRequest. New CI validates the JS toolchain on every push/PR.No open issues/PRs to close.