Skip to content

Commit 361922b

Browse files
sweetmantechclaude
andcommitted
refactor(sandbox): extract getStateExpiresAt to its own SRP file
Per review: the inline helper in getSandboxReconnectHandler.ts is its own concern (read epoch-ms expiresAt off a sandbox state) and belongs in a dedicated file alongside the other state predicates. Adds `lib/sandbox/getStateExpiresAt.ts` with a focused test (numeric match, non-numeric reject, null/scalar guard). Reconnect handler now imports from the new path; no behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8631b8e commit 361922b

3 files changed

Lines changed: 42 additions & 6 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import{describe,it,expect}from"vitest";
2+
import{getStateExpiresAt}from"@/lib/sandbox/getStateExpiresAt";
3+
4+
describe("getStateExpiresAt",()=>{
5+
it("returns the numeric expiresAt when present",()=>{
6+
expect(getStateExpiresAt({type: "vercel",expiresAt: 4_102_444_800_000})).toBe(
7+
4_102_444_800_000,
8+
);
9+
});
10+
11+
it("returns undefined when expiresAt is not a number",()=>{
12+
expect(getStateExpiresAt({type: "vercel",expiresAt: "soon"})).toBeUndefined();
13+
expect(getStateExpiresAt({type: "vercel"})).toBeUndefined();
14+
});
15+
16+
it("returns undefined for null / undefined / non-object inputs",()=>{
17+
expect(getStateExpiresAt(null)).toBeUndefined();
18+
expect(getStateExpiresAt(undefined)).toBeUndefined();
19+
expect(getStateExpiresAt("nope")).toBeUndefined();
20+
expect(getStateExpiresAt(42)).toBeUndefined();
21+
});
22+
});

‎lib/sandbox/getSandboxReconnectHandler.ts‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { buildLifecycle } from "@/lib/sandbox/buildLifecycle";
44
import{clearUnavailableSandboxState}from"@/lib/sandbox/clearUnavailableSandboxState";
55
import{connectSandbox}from"@/lib/sandbox/factory";
66
import{getSandboxExpiresAtDate}from"@/lib/sandbox/getSandboxExpiresAtDate";
7+
import{getStateExpiresAt}from"@/lib/sandbox/getStateExpiresAt";
78
import{hasRuntimeSandboxState}from"@/lib/sandbox/hasRuntimeSandboxState";
89
import{isSandboxUnavailableError}from"@/lib/sandbox/isSandboxUnavailableError";
910
import{noSandboxResponse}from"@/lib/sandbox/noSandboxResponse";
@@ -21,12 +22,6 @@ interface ReconnectBody {
2122
lifecycle: ReturnType<typeofbuildLifecycle>;
2223
}
2324

24-
functiongetStateExpiresAt(state: unknown): number|undefined{
25-
if(!state||typeofstate!=="object")returnundefined;
26-
constexpiresAt=(stateas{expiresAt?: unknown}).expiresAt;
27-
returntypeofexpiresAt==="number" ? expiresAt : undefined;
28-
}
29-
3025
/**
3126
* Handles `GET /api/sandbox/reconnect`. Live runtime probe — actually
3227
* runs a quick command inside the sandbox to verify it is reachable.

‎lib/sandbox/getStateExpiresAt.ts‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* Reads the runtime `expiresAt` field (epoch ms) off a sandbox state.
3+
* Returns undefined when the input is not an object or when
4+
* `expiresAt` is missing or a non-number — so callers can treat the
5+
* absence of an expiry as "unknown" without coercing to NaN/0.
6+
*
7+
* Distinct from `getSandboxExpiresAtDate`, which formats the same
8+
* field as an ISO-8601 string for persistence to
9+
* `sessions.sandbox_expires_at`.
10+
*
11+
* @param state - The `sandbox_state` JSON value, typically from
12+
* `sandbox.getState()` or the persisted session row.
13+
* @returns Epoch ms expiry, or undefined.
14+
*/
15+
exportfunctiongetStateExpiresAt(state: unknown): number|undefined{
16+
if(!state||typeofstate!=="object")returnundefined;
17+
constexpiresAt=(stateas{expiresAt?: unknown}).expiresAt;
18+
returntypeofexpiresAt==="number" ? expiresAt : undefined;
19+
}

0 commit comments

Comments
 (0)