Skip to content

Commit dc659c2

Browse files
justin-layervclaude
andcommitted
fix: address latest review — status union, error info leak, create test
1. Drop the "active,revoked" literal from ListInput.status. Reviewer flagged the asymmetry: listing one CSV combination implies it's canonical, but the API accepts any order ("active,revoked" or "revoked,active") and may add filter-only values. Simplified to `"active" | "revoked" | (string & {})`. The JSDoc already documents the CSV form, so autocomplete for the canonical single values stays and arbitrary strings still type-check. 2. Remove the raw response-body snippet from the batchCreate shape validation error. The previous message embedded up to 200 chars of the unexpected body, which could leak sensitive data (auth details, request echoes) into client-side error logs. Replaced with a static "Unexpected response shape from POST /v1/qurls/batch" message. Added a comment explaining why the snippet is intentionally absent. 3. Add qurl_id and label assertions to the existing "creates a QURL" test. The PR adds both fields to CreateOutput but the existing test only checked resource_id and qurl_link, leaving the new required fields without a regression guard. Tests: 63 (unchanged — #3 tightens an existing test rather than adding a new one). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
1 parent 1f3cd6d commit dc659c2

3 files changed

Lines changed: 21 additions & 10 deletions

File tree

src/client.test.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,12 @@ describe("QURLClient", () => {
4343
status: 201,
4444
body: {
4545
data: {
46+
qurl_id: "q_3a7f2c8e91b",
4647
resource_id: "r_abc123def45",
4748
qurl_link: "https://qurl.link/#at_test",
4849
qurl_site: "https://r_abc123def45.qurl.site",
4950
expires_at: "2026-03-15T10:00:00Z",
51+
label: "Test create",
5052
},
5153
meta: { request_id: "req_1" },
5254
},
@@ -56,10 +58,13 @@ describe("QURLClient", () => {
5658
const result = await client.create({
5759
target_url: "https://example.com",
5860
expires_in: "24h",
61+
label: "Test create",
5962
});
6063

64+
expect(result.qurl_id).toBe("q_3a7f2c8e91b");
6165
expect(result.resource_id).toBe("r_abc123def45");
6266
expect(result.qurl_link).toBe("https://qurl.link/#at_test");
67+
expect(result.label).toBe("Test create");
6368
expect(fetch).toHaveBeenCalledWith(
6469
"https://api.test.layerv.ai/v1/qurls",
6570
expect.objectContaining({ method: "POST" }),
@@ -1562,7 +1567,11 @@ describe("QURLClient", () => {
15621567
.catch((e: unknown) => e as ValidationError);
15631568
expect(error).toBeInstanceOf(ValidationError);
15641569
expect((error as ValidationError).code).toBe("client_validation");
1565-
expect((error as ValidationError).detail).toContain("Unexpected batchCreate response shape");
1570+
// Error message is intentionally static (no raw body embedded) to
1571+
// avoid leaking sensitive response content into client-side logs.
1572+
expect((error as ValidationError).detail).toBe(
1573+
"Unexpected response shape from POST /v1/qurls/batch",
1574+
);
15661575
});
15671576

15681577
it("batch create still throws on non-400 error statuses (401, 429, 5xx)", async () => {

src/client.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,16 +202,17 @@ export class QURLClient {
202202
// if the API ever returns 400 with a different body (e.g., a top-level
203203
// malformed-request error) the caller would silently get undefined
204204
// fields. Validate the shape before returning and surface a clear
205-
// error otherwise.
205+
// error otherwise. The error intentionally does not embed the raw
206+
// response body — an unexpected body could contain sensitive data
207+
// (auth details, request echoes) and error messages may end up in
208+
// client-side logs.
206209
if (
207210
!result ||
208211
typeof result.succeeded !== "number" ||
209212
typeof result.failed !== "number" ||
210213
!Array.isArray(result.results)
211214
) {
212-
throw clientValidationError(
213-
`Unexpected batchCreate response shape: ${JSON.stringify(result).slice(0, 200)}`,
214-
);
215+
throw clientValidationError("Unexpected response shape from POST /v1/qurls/batch");
215216
}
216217
return result;
217218
}

src/types.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,13 @@ export interface ListInput {
8181
limit?: number;
8282
cursor?: string;
8383
/**
84-
* Filter by status. Accepts comma-separated values to combine multiple,
85-
* e.g. `"active,revoked"`. The union includes common literal values for
86-
* autocomplete while still accepting any string (via the `(string & {})`
87-
* trick) to support CSV and any filter-only values the API may add.
84+
* Filter by status. Accepts a single value or comma-separated values to
85+
* combine multiple (e.g. `"active,revoked"`). The union lists the
86+
* canonical single values for autocomplete and uses the `(string & {})`
87+
* trick to still accept arbitrary strings — CSV combinations in any
88+
* order, plus any filter-only values the API may add later.
8889
*/
89-
status?: "active" | "revoked" | "active,revoked" | (string & {});
90+
status?: "active" | "revoked" | (string & {});
9091
/** Free-text search over description and target_url. */
9192
q?: string;
9293
/**

0 commit comments

Comments
 (0)