Skip to content

chore: deep overhaul — fix on-device AI streaming, add tests + CI, modernize tooling & docs#27

Open
kuraydev wants to merge 6 commits into
mainfrom
chore/deep-overhaul-2026
Open

chore: deep overhaul — fix on-device AI streaming, add tests + CI, modernize tooling & docs#27
kuraydev wants to merge 6 commits into
mainfrom
chore/deep-overhaul-2026

Conversation

@kuraydev

@kuraydev kuraydev commented Jun 29, 2026

Copy link
Copy Markdown
Owner

Automated deep-overhaul PR — review before merge.

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's fetch does not expose a streaming ReadableStream body, so streamMessage threw "No response body" immediately and no tokens ever arrived. Streaming now works through a new RN-compatible XMLHttpRequest-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/useAICompletion return shapes, and the sendAIMessage/streamAIMessage/buildUserMessage/buildSystemMessage exports — is preserved. New exports are additive only.

Commits (conventional, thematic):

  • fix(ai) — RN-compatible SSE streaming transport + buffering + typed wire bodies
  • fix(ts) — working event emitter, resolvable path aliases, clean typecheck gate
  • test — AI service / providers / hooks / SSE / event-emitter suites
  • ci — typecheck/lint/format/test workflow (Node 22 & 24)
  • chore(deps) — drop dead deps, make eslint deps explicit, fix metadata
  • docs — overhaul README + add LICENSE/CHANGELOG/CONTRIBUTING/templates

Non-breaking improvements

AI layer (source fixes)

  • New src/services/ai/sse.ts: an XMLHttpRequest SSE transport whose SSEParser holds a cross-chunk line buffer, fixing the second latent bug where a data: line split across two network reads was silently dropped (garbled/lost tokens).
  • All three providers (openai/anthropic/gemini) now share that transport and gained typed request/response wire shapes, replacing as any casts.
  • Error handling normalizes any thrown value (incl. SSEError) into a typed AIError carrying provider + HTTP statusCode.

Type & code-quality

  • src/navigation/index.tsx: removed route: any and React.useEffect((): any => …), and dropped the no-op {(props) => <DetailScreen {...props} />} double-spread (DetailScreen is propless).
  • App.tsx: replaced LogBox.ignoreAllLogs() (which hides real warnings) with a scoped LogBox.ignoreLogs([]).
  • tsconfig paths now match the babel aliases — @models, @event-emitter, @api, @local-storage resolve, with @api/@local-storage stub dirs added. *.png/jpg/… ambient modules moved to assets.d.ts so they stay global.

Tooling & deps

  • Added typecheck (tsc --noEmit) and format:check scripts; husky pre-commit now runs typecheck + lint instead of the removed custom src/scripts/terminal/*.mjs runners.
  • Made previously-transitive @typescript-eslint/parser, @typescript-eslint/eslint-plugin, and eslint-plugin-jest explicit devDeps (latent break removed). Consolidated duplicate prettier configs.
  • package.json metadata (description/author/homepage/repository/bugs/keywords) repointed to kuraydev. npm name unchanged; private: true retained.

CI / tests / docs / hygiene

  • .github/workflows/ci.yml: install + typecheck + lint + prettier --check + test on a Node 22.x/24.x matrix.
  • 8 new jest suites (AI service, 3 providers, SSE parser, both hooks, event-emitter) + a hardened App smoke test (fake timers stop the stack-animation teardown from crashing the worker).
  • README rewritten: Requirements, New Architecture note, Streaming & Security (proxy via baseURL), Troubleshooting (the fetch-streaming caveat), corrected path-alias table, kuraydev badges + CI badge.
  • Added 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)

This repo is private: true and is not the artifact behind react-native-typescript-boilerplate@4.1.0 on npm. None of the documented public API (aliases, hook/service signatures) breaks. The items below change observable runtime/contributor behavior and are bug fixes restoring documented behavior:

  • Streaming now emits tokens instead of always firing onError/"No response body". IAIProvider and the onToken/onComplete/onError shapes are unchanged.
  • event-emitter no longer imports Node's events (Metro never polyfilled it, so it never worked); it's now a dependency-free EventEmitter with the same public API, and the unused event dependency was removed.
  • AIConfig.systemPrompt is now injected for sendAIMessage too (was a no-op there); duplicate injection is guarded.
  • husky pre-commit now enforces typecheck + lint, which may gate commits that previously slipped through.
  • Dead devDeps removed (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/peerDependencies library fields (this is an app template, not a published lib), no ESLint 9 flat-config migration (RN 0.84's @react-native/eslint-config is eslintrc-based; a migration risks breaking forkers' pre-commit), and no native-dep version bumps / newArchEnabled flips (the current pin set builds as a unit).

Verification

Check Result
Install npm install --legacy-peer-deps → 1023 pkgs (EBADENGINE warning only; local Node 20 vs engines >=22.11, CI uses 22/24)
Typecheck passtsc --noEmit (project-pinned TypeScript 5.9.3), 0 errors
Lint passeslint . exit 0 (ESLint 8.57.1)
Format passprettier --check "src/**/*.{js,ts,tsx}"
Tests pass — jest 9 suites / 46 tests

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.

kuraydev added 6 commits June 29, 2026 16:11
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.
@kuraydev kuraydev changed the title Deep overhaul: working on-device AI streaming, test suite + CI, resolvable types, and docs/hygiene chore: deep overhaul — fix on-device AI streaming, add tests + CI, modernize tooling & docs Jun 29, 2026
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