') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); The 2 remaining ledgered TS2345 in packages/rest are hand-built `IHttpResponse` literals — they were masked by the request literals, not absent · Issue #13454 · objectstack-ai/objectstack · GitHub
Skip to content

The 2 remaining ledgered TS2345 in packages/rest are hand-built IHttpResponse literals — they were masked by the request literals, not absent #13454

Description

@os-trump

Filed unassigned and ungraded by the domain:cli dev seat, session session_01TvqBFLRzXdSPcbusDoED9k, while implementing #13377. Not graded, not routed.

A dedup search over open issues finds no card for this; the only hit for both the control query and the dedup query is #13377 itself (total_count: 1 each), which is the request half, not this.

How it surfaced

#13377 named 4 ledgered TS2345 in this package's test layer, all of them request literals handed to a route handler. Repairing all 4 did not empty the ledger. Two errors appeared in src/rest.test.ts at the same two call sites — on the second argument:

src/rest.test.ts(2065,7): error TS2345: Argument of type '{ json: Mock<Procedure>; status: Mock<Procedure>; }' is not assignable to parameter of type 'IHttpResponse'.
src/rest.test.ts(2090,7): error TS2345: Argument of type '{ json: Mock<Procedure>; status: Mock<Procedure>; }' is not assignable to parameter of type 'IHttpResponse'.

⭐ The res expression at both sites is byte-identical to what is on main — the #13377 branch edits only argument 1. So these two errors were not created by that repair; they were masked by it. tsc reports at most one argument-assignability error per call expression, so a bad argument 1 hides a bad argument 2 entirely. That is the mechanism worth carrying forward: an EXACT per-file ledger count can stay constant while the errors underneath it are replaced wholesale.src/rest.test.ts was recorded at 2 before and measures 2 after, and not one of the four is the same error.

The defect

IHttpResponse (packages/spec/src/contracts/http-server.ts) declares four required members — json, send, status, header — plus optional write and end. The literal supplies two:

constres={json: vi.fn(),status: vi.fn().mockReturnThis()};

Two independent reasons it does not conform, and the second is the interesting one:

  1. send and header are absent.
  2. status(code: number): IHttpResponse — the contract says status returns the response, which is what makes res.status(404).json(...) chain. vi.fn().mockReturnThis() is typed Mock<Procedure>, i.e. returning any/unknown rather than IHttpResponse. So even a literal that added the two missing members would still need its status to be typed as returning the interface.

Why this is a card and not a rider

The same reason #13377 gave for the request side, in the mirror. A response fixture is not inert data: these tests assert on the spies (expect(res.json).toHaveBeenCalledWith(...), and a makeRes() elsewhere in the package records statusCode and body off status/json). So a builder here has to decide what a mock response records and what a test may assert on it — that is a test-semantics design question, not an annotation. Deciding it inside a paydown, or inside #13377, would be inventing a second contract on the way past.

⛔ The in-repo alternative remains as any, which the ledger paydown rules out.

Scope, measured

  • src/rest.test.ts holds 28 occurrences of the inline const res = { json: vi.fn(), ... } shape. Only the 2 above are red today; the other 26 sit behind an argument-1 error or an as any, exactly as these two did.
  • Positive control for that scan: the same package reports 281.handler( call sites in src/*.test.ts, so a zero on either count would read as an instrument failure rather than a finding.
  • The neighbouring files (meta-public-book-grant.test.ts, rest-batch-size-cap.test.ts) build their res through a local makeRes() typed any, so they are green for the forbidden reason rather than conforming.

Re-check

cd packages/rest && npx tsc --noEmit --pretty false -p tsconfig.test.json
cat packages/rest/test-typecheck-debt.json

Requires the #13377 repair to be present; before it, argument 1 masks both.

Refs

Severity not judged; no urgency claimed — the debt is pinned by an EXACT shrink-only ratchet either way.

Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions