Skip to content

Fix broken sources panel, stream-quota crash, token clipboard leak + code-review findings#14

Merged
frydzewski merged 6 commits into
Validic:mainfrom
selmapi:fix/testing-guide-findings
Jul 10, 2026
Merged

Fix broken sources panel, stream-quota crash, token clipboard leak + code-review findings#14
frydzewski merged 6 commits into
Validic:mainfrom
selmapi:fix/testing-guide-findings

Conversation

@selmapi

@selmapi selmapi commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Five commits from an end-to-end testing pass of both sample apps against a live sandbox org, followed by a full-repo code review. Every fix was verified against the production Inform API; test suite grew 16 → 23 (all passing), explorer lint/build clean.

Quickstart app

  • Connected-sources panel never worked: it called GET /users/:id/apps, which does not exist in the Inform API (404 always). It now reads the sources[] array from the uid-keyed single-user GET, matching the documented API. The copy-curl command and README were updated to match.
  • Stream quota no longer crashes startup: orgs cap at 5 streams; a 422 on stream creation previously exited the app. It now starts with live streaming disabled, logs cleanup instructions, and returns 503 from /api/stream/connect. Replay keeps working.
  • SSE errors are no longer silent: non-2xx upstream responses (e.g. bad token) surface as a stream_error event rendered in the feed instead of being dropped.
  • EventSource lifecycle: finished replays and fatal stream errors no longer trigger native auto-reconnect loops (infinite re-replay / repeating error rows).
  • Correctness/hardening: XSS escape on source.type in the data table, stale-response guards when switching users mid-fetch, API errors render as errors instead of cached empty states, user_id URL-encoded at upstream interpolation sites, clearer boot error if a user payload lacks a marketplace token.

Explorer app

  • Missing DATAGEN_URL no longer blocks the whole app: only the Generate Data panel requires it; the panel is disabled with an inline explanation until it's configured. /api/config and the config upload accept token/org/API URL alone.
  • Copy JSON no longer leaks the live org token: copies are redacted the same way the on-screen JSON view already was. The config panel's token-storage note now accurately describes localStorage persistence, and the copy-curl button warns that the copied command contains the live token.
  • Pagination total fixed: read meta.total (the actual response envelope) instead of summary.total_count, which was always undefined; hasMore uses the real total when known.
  • Dead code removed (~400 lines): the unwired "Fetch All Users" feature and the unreachable "Impact" help mode; fixed a help step describing a control that doesn't exist.
  • Route hardening: caller-supplied URLs validated (http/https) in the proxy/stream/health routes, plus a README note that these are local dev tools and the explorer shouldn't be deployed to a public URL as-is.
  • Added the missing apps/explorer/LICENSE.

Verified

  • All API behaviors confirmed against a live sandbox org (uid-keyed user GET, sources[], meta envelope, stream quota).
  • Quickstart: 23/23 jest. Explorer: eslint 0 errors, production build clean. Both apps booted and exercised end to end against the live API.

🤖 Generated with Claude Code

selmapi and others added 6 commits July 8, 2026 20:37
…face stream errors

- /api/apps called GET /users/:id/apps, which does not exist in the Inform
  API (404 always). Connected sources live on the single-user GET, keyed by
  uid: the panel now calls GET /organizations/:org/users/:uid and renders
  the sources[] array (type, connected_at, last_processed_at). The copy-curl
  command was updated to match.
- Stream setup no longer crashes the app. Sandbox orgs cap at 5 streams
  (documented limit); a 422 quota failure now logs a warning with cleanup
  curl commands and the app starts without live streaming. /api/stream/connect
  returns 503 and the Connect button is disabled; Replay keeps working.
- sseProxy: non-2xx upstream responses (e.g. bad token) were silently
  dropped because plain JSON bodies never match the SSE frame delimiter.
  They now surface as a stream_error SSE event rendered in the feed.
- Remove leftover debug console.log in /api/users.

Verified against the live Inform API: sources render for a real user,
replay streams real events, 20/20 jest tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- A missing DATAGEN_URL locked the entire workbench behind the
  "Configuration Required" screen (three separate gates: config file
  parsing, localStorage re-validation, and the /api/config dev route).
  Only the Generate Data panel actually needs it: the app now unlocks
  with just ORG_TOKEN, ORG_ID, and API_URL, and the Generate Data
  service is disabled with an inline explanation until a Data Generator
  URL is configured. executeApiRequest refuses generate-data requests
  with no datagen URL rather than firing at an empty base.
- Pagination total read summary.total_count, but the API returns
  { data, meta: { offset, limit, total, sort } } — total was always
  undefined. Now reads meta.total (summary kept as fallback), and
  hasMore compares against the real total when known.
- Document that offset is a 0-indexed page index per the Inform API
  docs, and mark DATAGEN_URL as optional in both READMEs and the
  config-upload help text.

Verified live: workbench unlocks without DATAGEN_URL with Generate Data
disabled; with it, generation works end to end through /api/proxy;
users list shows "Showing 1-50 of 137 users". Lint 0 errors, build clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…license

- LICENSE: root copyright said "Chartspan Medical Technologies" (template
  artifact); now Validic, Inc. Added the missing apps/explorer/LICENSE.
- app.js renderTable: escape source.type — the one unescaped interpolation.
- EventSource lifecycle: a finished replay (or a fatal stream_error like a
  bad token) ends the server response, which native EventSource treats as a
  drop and reconnects forever — re-replaying history or stacking identical
  error rows every ~3s. Streams are now mode-aware: replay closes on
  error/end (with an explicit end-of-replay row), live connect keeps native
  reconnect but closes on stream_error.
- Stale-response guards: switching users mid-fetch no longer lets the slow
  response render (or cache) under the newly selected user.
- API errors render as errors: failed /api/apps and /api/data responses
  showed the empty state ("No data found") and were cached, so retries
  never happened. Now they show the error and skip the cache.
- server.js: user_id is URL-encoded at all upstream interpolation sites
  (path-traversal hardening); ensureUser throws a clear message if a user
  row lacks a marketplace token instead of an unguarded TypeError.
- README (sub-app): setup steps now work from the monorepo layout; removed
  the nonexistent GET /users/:id/apps endpoint from the feature table.

Tests: 20 → 23, all passing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, races

- ResponsePanel: Copy JSON copied the raw response object, putting the live
  org token (actualRequest.url / Token header) on the clipboard while the
  on-screen view showed it redacted. Copies are now redacted the same way.
- ConfigPanel: "stored in browser memory only" was false — uploaded configs
  persist to localStorage. The note now says so truthfully.
- ResponsePanel: user-list detection keyed off marketplace/mobile presence;
  now keys off uid so user cards and pagination survive any real payload.
- Deleted dead code: the never-wired "Fetch All Users" feature (130-line
  lib + unreachable buttons) and the unreachable "Impact" help mode; fixed
  a help step describing a source filter that doesn't exist for
  Get User Data.
- InformDemo: stale-request guard (switching services mid-flight no longer
  shows the old service's response) and history entries now capture the
  auto-generated create-user uid so re-running them reproduces the request.
- API routes: caller-supplied URLs are validated (well-formed, http/https)
  before fetching; root README notes these are local dev tools and the
  explorer should not be deployed publicly as-is.
- RequestPreview: copy-curl button tooltip warns the copied command
  contains the live token (the preview shows it redacted).

Lint 0 errors, build clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Chartspan Medical Technologies attribution is correct — ChartSpan
acquired Validic — not a template artifact as the earlier review-driven
change assumed. Root LICENSE restored verbatim; the new explorer LICENSE
now matches the root. apps/quickstart/LICENSE keeps its pre-existing
Validic, Inc. line (attribution consistency across sub-licenses is a
legal call, not a code fix).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Lead with what Inform is and what this repo is for, not just an app table
- Numbered setup with the expected boot output, so readers know it worked
- Panel-by-API-call table and the provision → connect → wait → verify
  pattern every integration follows
- Repo layout, troubleshooting table (uid vs id 404s, stream quota,
  first-sync delay, explorer config, rate limit)
- Fix dead links: developer.validic.com/agents.txt → llms.txt (the live
  agent index), api.dashboard.validic.com → dashboard.validic.com; same
  fixes in agents.txt
- Add Node + CI badges

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@frydzewski frydzewski merged commit bc14b33 into Validic:main Jul 10, 2026
3 checks passed
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.

2 participants