Skip to content

fix: Send a SeamHttpRequest at most once - #1002

Merged
razor-x merged 1 commit into
mainfrom
claude/js-sdk-audit-6x115l
Aug 24, 2026
Merged

fix: Send a SeamHttpRequest at most once#1002
razor-x merged 1 commit into
mainfrom
claude/js-sdk-audit-6x115l

Conversation

@razor-x

@razor-x razor-x commented Aug 24, 2026

Copy link
Copy Markdown
Member

Problem

SDK audit finding C1 (critical, runtime-verified): a SeamHttpRequest re-executes the HTTP request every time it is awaited or given a then, catch, or finally callback — nothing memoizes the in-flight promise. Since the class implements Promise, once-only semantics are the reasonable assumption.

const r = seam.locks.unlockDoor({ device_id }, opts)
r.catch(logError)   // ← this alone issues a second real unlock POST
const aa = await r  // await + await + .catch() = 3 server-observed POSTs

Any defensive-logging idiom, Promise.all([r, r]), or awaiting a stored request twice duplicates writes: two access codes created, a door unlocked twice. For action-attempt routes each re-execution also restarts a full poll loop.

Fix

Memoize the promises from execute() and fetchResponse() in private fields, so the request is sent at most once and every consumer observes the first execution (including a cached rejection). Documented the at-most-once contract in the class JSDoc and the README's Inspecting the Request section.

Tests

New tests count real server hits via a request interceptor against fake-seam-connect:

  • double await + Promise.all([r, r]) → 1 request
  • .catch() after await → 1 request
  • .finally() → 1 request
  • write route (locks.unlockDoor) awaited twice + .catch() → 1 POST, same action_attempt_id
  • rejected request stays rejected, same error instance, 1 request

Verified per the audit-notes recipe: with the source fix reverted, all 5 new tests fail with the audit's exact symptom (multiple deliveries); with the fix they pass. Full suite: 130 tests, lint, and typecheck green.

Part of applying the rev-3 SDK audit to this SDK (one PR per finding, mirroring seamapi/php#465#479).

🤖 Generated with Claude Code

https://claude.ai/code/session_01B8xeJm2Hd923k8uo6eoFd2


Generated by Claude Code

A SeamHttpRequest re-executed the HTTP request every time it was
awaited or given a then, catch, or finally callback. Any defensive
logging idiom, Promise.all with the same request, or awaiting a stored
request twice silently repeated the request, duplicating writes such as
door unlocks or access code creation.

Memoize the promises from execute and fetchResponse so the request is
sent at most once and every consumer observes the first execution.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B8xeJm2Hd923k8uo6eoFd2
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