@tokenoftrust/storefront-runner 4.11.7 → 4.11.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -50,14 +50,19 @@ export class FetchShipGrantTransport implements ViewerShipGrantGetTransport {
50
50
  // every admin tab), so an unbounded hang stalls the whole document stream. A
51
51
  // timeout aborts to the documented deny-default (the resolver maps any transport/
52
52
  // timeout fault to `preview-access`) — never a render-blocking wait on a slow MCP.
53
- // `redirect: "error"`: a redirect would carry the service-secret header to another origin,
54
- // so it rejects (a transport fault, so a deny) and nothing is sent to the redirect target.
53
+ // `redirect: "manual"`, then a 3xx throws: a followed redirect would carry the service-secret
54
+ // header to another origin, so a redirect is a transport fault (a deny) and nothing is sent to
55
+ // its target. Not `redirect: "error"`: the Workers runtime rejects that value before the request
56
+ // leaves, so every lookup would deny without ever reaching tot-mcp.
55
57
  const res = await fetch(url, {
56
58
  method: "GET",
57
59
  headers,
58
- redirect: "error",
60
+ redirect: "manual",
59
61
  signal: AbortSignal.timeout(2500),
60
62
  });
63
+ if ((res.status >= 300 && res.status < 400) || res.type === "opaqueredirect") {
64
+ throw new Error("grant authority answered a redirect; refused");
65
+ }
61
66
  return { status: res.status, json: () => res.json() };
62
67
  }
63
68
  }
@@ -14,7 +14,10 @@
14
14
  *
15
15
  * Every outcome but `ok` logs one `[mcp-service-seam]` line naming the method, the contract path
16
16
  * and a fixed reason: the env name for `not-configured` / `bad-url`, the error class (from a fixed
17
- * list) for `transport-error`, the status otherwise. Never the URL, query, secret or thrown text.
17
+ * list) for `transport-error`, the status otherwise. A `TypeError` also carries its message and its
18
+ * cause's message, scrubbed of URLs, email addresses and the secret, because fetch's wording is
19
+ * what separates a lost connection from one refused before connecting or a TLS trust failure.
20
+ * Never the URL, query or secret, and no other thrown value's text.
18
21
  *
19
22
  * A 401 or 404 from a seam route is misconfiguration, never a business answer: tot-mcp's
20
23
  * routes answer 200 for every absent/unknown case, so 401 means the secret or URL is wrong
@@ -87,6 +90,33 @@ export function transportErrorClass(thrown: unknown): string {
87
90
  return thrown instanceof Error && TRANSPORT_ERROR_CLASSES.has(thrown.name) ? thrown.name : "Error";
88
91
  }
89
92
 
93
+ /** A thrown text with every URL, email address and the given secret removed, whitespace folded, capped. */
94
+ function scrubbed(text: string, secret: string): string {
95
+ let out = text;
96
+ if (secret) out = out.split(secret).join("<secret>");
97
+ return out
98
+ .replace(/\b[a-z][a-z0-9+.-]*:\/\/[^\s'"<>)]+/gi, "<url>")
99
+ .replace(/[^\s@'"<>(),;:]+@[^\s@'"<>(),;:]+\.[a-z]{2,}/gi, "<email>")
100
+ .replace(/\s+/g, " ")
101
+ .trim()
102
+ .slice(0, 160);
103
+ }
104
+
105
+ /**
106
+ * For a `TypeError` (how a Workers fetch reports a connection it could not make: lost, refused
107
+ * before connecting, TLS trust), its message and its cause's message, scrubbed of URLs, email
108
+ * addresses and the service secret. Those words are what tell the network causes apart. Any other
109
+ * thrown value yields nothing: its text is not a fetch's own wording.
110
+ */
111
+ export function transportErrorDetail(thrown: unknown, secret: string): string {
112
+ if (!(thrown instanceof TypeError)) return "";
113
+ const cause = (thrown as { cause?: unknown }).cause;
114
+ const causeText = cause instanceof Error ? cause.message : typeof cause === "string" ? cause : "";
115
+ const parts = [`message "${scrubbed(thrown.message, secret)}"`];
116
+ if (causeText) parts.push(`cause "${scrubbed(causeText, secret)}"`);
117
+ return `: ${parts.join(", ")}`;
118
+ }
119
+
90
120
  export async function callMcpService(req: McpServiceRequest): Promise<McpServiceOutcome> {
91
121
  const urlEnv = AUTHORITY_ENV[req.authority ?? "binding"];
92
122
  const authorityUrl = await readEnv(urlEnv);
@@ -125,7 +155,9 @@ export async function callMcpService(req: McpServiceRequest): Promise<McpService
125
155
  res = await req.transport.post(url, headers, req.body);
126
156
  }
127
157
  } catch (thrown) {
128
- console.error(`[mcp-service-seam] ${req.method} ${req.path} transport-error: ${transportErrorClass(thrown)} (${urlEnv})`);
158
+ console.error(
159
+ `[mcp-service-seam] ${req.method} ${req.path} transport-error: ${transportErrorClass(thrown)} (${urlEnv})${transportErrorDetail(thrown, secret)}`,
160
+ );
129
161
  return { kind: "transport-error", message: MCP_SERVICE_TRANSPORT_FAILURE };
130
162
  }
131
163
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/storefront-runner",
3
- "version": "4.11.7",
3
+ "version": "4.11.9",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
5
  "description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
6
6
  "packageManager": "pnpm@11.9.0",