@camstack/types 1.2.430 → 1.2.432

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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.2.432] - 2026-10-28
11
+
12
+ ### Changed
13
+
14
+ - The vendor login proxy is served on one shared vendor-login prefix that cannot shadow an addon's own routes
15
+
16
+ ## [1.2.431] - 2026-10-27
17
+
18
+ ### Changed
19
+
20
+ - Addons can now offer a vendor sign-in that opens the vendor's login page through the hub and finishes on the CamStack result page.
21
+
10
22
  ## [1.2.430] - 2026-10-27
11
23
 
12
24
  ### Added
package/dist/index.d.ts CHANGED
@@ -353,6 +353,7 @@ export { errMsg } from './utils/err-msg.js';
353
353
  export { hfModelUrl } from './utils/hf-url.js';
354
354
  export { asBoolean, asJsonArray, asJsonObject, asNumber, asString, parseJsonArray, parseJsonObject, parseJsonUnknown, } from './utils/json-safe.js';
355
355
  export { maskUrlCredentials } from './utils/mask-url.js';
356
+ export { VendorLoginProxy, rewriteBody as rewriteLoginProxyBody, rewriteLocation as rewriteLoginProxyLocation, rewriteReferer as rewriteLoginProxyReferer, rewriteSetCookie as rewriteLoginProxySetCookie, type LoginProxyAttemptSpec, type LoginProxyResponseView, type LoginProxyRoute, type LoginProxyScheme, type LoginProxyUpstream, type LoginProxyVerdict, type VendorLoginProxyOptions, VENDOR_LOGIN_PROXY_PREFIX, } from './vendor-login-proxy/index.js';
356
357
  export { OAUTH_LANDING_PATH, OAUTH_LANDING_REASONS, isOauthLandingReason, oauthLandingUrl, parseOauthLandingFragment, type OauthLandingOutcome, type OauthLandingReason, } from './utils/oauth-landing.js';
357
358
  export { commitWatchdogRestart, DEFAULT_POOL_MEMORY_POLICY, evaluatePoolMemory, initialPoolMemoryState, type PoolMemoryAction, type PoolMemoryPolicy, type PoolMemoryState, type PoolMemoryTelemetry, type PoolMemoryVerdict, PoolMemoryWatchdog, type PoolMemoryWatchdogOptions, type ProcMemory, parseProcStatus, pickRestartCandidate, poolMemoryThreshold, type RestartCandidate, resetPoolBaseline, resolvePoolMemoryPolicy, } from './utils/pool-memory-watchdog.js';
358
359
  export { cellsToRects, type NormRect, rectsToCells } from './utils/privacy-grid-raster.js';
package/dist/index.js CHANGED
@@ -43694,7 +43694,7 @@ function autoRankFor(role, endpoint) {
43694
43694
  if (isPlausibleLan(endpoint)) return 3;
43695
43695
  return null;
43696
43696
  }
43697
- var LOOPBACK_HOSTS = new Set([
43697
+ var LOOPBACK_HOSTS$1 = new Set([
43698
43698
  "localhost",
43699
43699
  "0.0.0.0",
43700
43700
  "::1",
@@ -43702,9 +43702,9 @@ var LOOPBACK_HOSTS = new Set([
43702
43702
  "::",
43703
43703
  "[::]"
43704
43704
  ]);
43705
- function isLoopbackHost(hostname) {
43705
+ function isLoopbackHost$1(hostname) {
43706
43706
  const h = hostname.toLowerCase();
43707
- if (LOOPBACK_HOSTS.has(h) || h.endsWith(".localhost")) return true;
43707
+ if (LOOPBACK_HOSTS$1.has(h) || h.endsWith(".localhost")) return true;
43708
43708
  return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(h);
43709
43709
  }
43710
43710
  /**
@@ -43739,7 +43739,7 @@ function validateCustomAddress(raw) {
43739
43739
  ok: false,
43740
43740
  reason: "The address has no host."
43741
43741
  };
43742
- if (isLoopbackHost(parsed.hostname)) return {
43742
+ if (isLoopbackHost$1(parsed.hostname)) return {
43743
43743
  ok: false,
43744
43744
  reason: "A loopback address names whoever dials it, never this hub — use an address other machines reach."
43745
43745
  };
@@ -117778,6 +117778,19 @@ function setNestedValue(obj, path, value) {
117778
117778
  current[parts[parts.length - 1]] = value;
117779
117779
  }
117780
117780
  //#endregion
117781
+ //#region src/vendor-login-proxy/prefix.ts
117782
+ /**
117783
+ * The data-plane prefix every `VendorLoginProxy` is served under
117784
+ * (`/addon/<addonId>/vendor-login/<attemptId>/<vendorHost>/…`).
117785
+ *
117786
+ * The hub consults an addon's data-plane prefixes BEFORE its HTTP routes, so a
117787
+ * prefix equal to the first segment of a route the addon serves swallows that
117788
+ * route. A sign-in addon serves `/login/start|status|commit|cancel`; the proxy
117789
+ * therefore lives on a segment no addon route uses. One constant, so the entry
117790
+ * path, the mount and the docs cannot drift apart.
117791
+ */
117792
+ var VENDOR_LOGIN_PROXY_PREFIX = "vendor-login";
117793
+ //#endregion
117781
117794
  //#region src/utils/oauth-landing.ts
117782
117795
  /**
117783
117796
  * Where an OAuth round-trip ends: the admin-ui completion route
@@ -117845,6 +117858,419 @@ function parseOauthLandingFragment(hash) {
117845
117858
  };
117846
117859
  }
117847
117860
  //#endregion
117861
+ //#region src/vendor-login-proxy/url-rewrite.ts
117862
+ var LOOPBACK_HOSTS = new Set([
117863
+ "127.0.0.1",
117864
+ "localhost",
117865
+ "[::1]"
117866
+ ]);
117867
+ function hostWithoutPort(host) {
117868
+ if (host.startsWith("[")) return host.slice(0, host.indexOf("]") + 1);
117869
+ const colon = host.indexOf(":");
117870
+ return colon === -1 ? host : host.slice(0, colon);
117871
+ }
117872
+ function isLoopbackHost(host) {
117873
+ return LOOPBACK_HOSTS.has(hostWithoutPort(host.toLowerCase()));
117874
+ }
117875
+ /** An allow-list entry is valid when it is a bare host[:port] and `http` only targets loopback. */
117876
+ function isValidUpstream(upstream) {
117877
+ if (upstream.host.length === 0 || !/^[a-z0-9.[\]:-]+$/.test(upstream.host)) return false;
117878
+ return upstream.scheme === "https" || isLoopbackHost(upstream.host);
117879
+ }
117880
+ function findUpstream(route, host) {
117881
+ const wanted = host.toLowerCase();
117882
+ return route.upstreams.find((upstream) => upstream.host === wanted);
117883
+ }
117884
+ /** `<basePath>/<host><pathname><search>` — what the browser is sent to. */
117885
+ function proxyPathFor(route, host, pathname, search = "") {
117886
+ return `${route.basePath}/${host}${pathname.startsWith("/") ? pathname : `/${pathname}`}${search}`;
117887
+ }
117888
+ /** Split `/<attemptId>/<host>/<rest…>[?query]` (the data-plane view of a request). */
117889
+ function parseProxyPath(rawUrl) {
117890
+ const queryAt = rawUrl.indexOf("?");
117891
+ const path = queryAt === -1 ? rawUrl : rawUrl.slice(0, queryAt);
117892
+ const search = queryAt === -1 ? "" : rawUrl.slice(queryAt);
117893
+ const match = /^\/([A-Za-z0-9_-]{8,128})\/([^/]+)(\/.*)?$/.exec(path);
117894
+ if (match === null) return null;
117895
+ const attemptId = match[1];
117896
+ const host = match[2];
117897
+ if (attemptId === void 0 || host === void 0) return null;
117898
+ return {
117899
+ attemptId,
117900
+ host: host.toLowerCase(),
117901
+ pathname: match[3] ?? "/",
117902
+ search
117903
+ };
117904
+ }
117905
+ /** The vendor URL a proxied request targets, or null when the host would not stay `host`. */
117906
+ function upstreamUrlFor(upstream, parsed) {
117907
+ try {
117908
+ const url = new URL(`${upstream.scheme}://${upstream.host}${parsed.pathname}${parsed.search}`);
117909
+ return url.host.toLowerCase() === upstream.host ? url : null;
117910
+ } catch {
117911
+ return null;
117912
+ }
117913
+ }
117914
+ function escapeRegex(value) {
117915
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
117916
+ }
117917
+ /** A Location (or any URL value) to a vendor host becomes a proxy path; anything else is unchanged. */
117918
+ function rewriteLocation(location, currentUpstream, route) {
117919
+ let resolved;
117920
+ try {
117921
+ resolved = new URL(location, `${currentUpstream.scheme}://${currentUpstream.host}/`);
117922
+ } catch {
117923
+ return location;
117924
+ }
117925
+ if (resolved.protocol !== "https:" && resolved.protocol !== "http:") return location;
117926
+ const target = findUpstream(route, resolved.host);
117927
+ if (target === void 0) return location;
117928
+ return `${proxyPathFor(route, target.host, resolved.pathname, resolved.search)}${resolved.hash}`;
117929
+ }
117930
+ /** Characters that may precede a URL that STARTS a value (never `=`, `?`, `&`, `%3D`). */
117931
+ var VALUE_START = String.raw`(?<=^|["'(\s>,\[{])`;
117932
+ /**
117933
+ * Rewrite vendor URLs in a text body: absolute (`https://host/…`), protocol-relative
117934
+ * (`//host/…`) and JSON-escaped (`https:\/\/host\/…`) when they start a value; and, in
117935
+ * HTML/CSS, root-relative references (`href="/x"`, `url(/x)`) which would otherwise
117936
+ * escape the proxy mount and land on the hub.
117937
+ */
117938
+ function rewriteBody(body, kind, currentUpstream, route) {
117939
+ let out = body;
117940
+ for (const upstream of route.upstreams) {
117941
+ const host = escapeRegex(upstream.host);
117942
+ const plain = new RegExp(`${VALUE_START}(?:https?:)?//${host}(?=[/"'?#\\s)\\]},<]|$)`, "g");
117943
+ out = out.replace(plain, `${route.basePath}/${upstream.host}`);
117944
+ const escapedBase = `${route.basePath}/${upstream.host}`.replace(/\//g, "\\/");
117945
+ const escaped = new RegExp(`${VALUE_START}(?:https?:)?\\\\/\\\\/${host}(?=\\\\/|["'?#\\s)\\]},<]|$)`, "g");
117946
+ out = out.replace(escaped, escapedBase);
117947
+ }
117948
+ const current = `${route.basePath}/${currentUpstream.host}/`;
117949
+ const notMounted = `(?!/)(?!${escapeRegex(route.basePath.slice(1))}/)`;
117950
+ if (kind === "html") {
117951
+ const attribute = String.raw`(\b(?:href|src|action|formaction|poster|data-href|data-src)\s*=\s*["'])/`;
117952
+ out = out.replace(new RegExp(`${attribute}${notMounted}`, "gi"), `$1${current}`);
117953
+ }
117954
+ if (kind === "html" || kind === "css") {
117955
+ const cssUrl = String.raw`(url\(\s*["']?)/`;
117956
+ out = out.replace(new RegExp(`${cssUrl}${notMounted}`, "gi"), `$1${current}`);
117957
+ }
117958
+ return out;
117959
+ }
117960
+ /**
117961
+ * A vendor `Set-Cookie` for the browser: no `Domain` (the cookie belongs to the hub
117962
+ * origin), `Path` narrowed to this attempt's prefix, `Secure` kept (the hub is HTTPS).
117963
+ */
117964
+ function rewriteSetCookie(cookie, route) {
117965
+ const kept = cookie.split(";").map((part) => part.trim()).filter((part, index) => {
117966
+ if (index === 0) return true;
117967
+ const name = part.split("=")[0]?.trim().toLowerCase() ?? "";
117968
+ return name !== "domain" && name !== "path";
117969
+ });
117970
+ kept.push(`Path=${route.basePath}/`);
117971
+ return kept.join("; ");
117972
+ }
117973
+ /**
117974
+ * A browser `Referer` that points into this proxy, turned back into the vendor URL it
117975
+ * stands for. A Referer that is not ours is dropped (null): it names the hub.
117976
+ */
117977
+ function rewriteReferer(referer, route) {
117978
+ let url;
117979
+ try {
117980
+ url = new URL(referer, "http://hub.invalid");
117981
+ } catch {
117982
+ return null;
117983
+ }
117984
+ if (!url.pathname.startsWith(`${route.basePath}/`)) return null;
117985
+ const rest = /^\/([^/]+)(\/.*)?$/.exec(url.pathname.slice(route.basePath.length));
117986
+ const host = rest?.[1]?.toLowerCase();
117987
+ if (host === void 0) return null;
117988
+ const upstream = findUpstream(route, host);
117989
+ if (upstream === void 0) return null;
117990
+ return `${upstream.scheme}://${upstream.host}${rest?.[2] ?? "/"}${url.search}`;
117991
+ }
117992
+ /** Cookies the browser holds for the hub itself must never reach a vendor. */
117993
+ function filterForwardedCookies(cookieHeader) {
117994
+ return cookieHeader.split(";").map((part) => part.trim()).filter((part) => part.length > 0 && !part.toLowerCase().startsWith("camstack_")).join("; ");
117995
+ }
117996
+ //#endregion
117997
+ //#region src/vendor-login-proxy/login-proxy.ts
117998
+ var MAX_REQUEST_BODY_BYTES = 2 * 1024 * 1024;
117999
+ var MAX_TEXT_BODY_BYTES = 4 * 1024 * 1024;
118000
+ /** Request headers that are never forwarded to the vendor. */
118001
+ var DROP_REQUEST_HEADERS = new Set([
118002
+ "host",
118003
+ "connection",
118004
+ "keep-alive",
118005
+ "proxy-authorization",
118006
+ "proxy-connection",
118007
+ "te",
118008
+ "trailer",
118009
+ "transfer-encoding",
118010
+ "upgrade",
118011
+ "content-length",
118012
+ "accept-encoding",
118013
+ "authorization",
118014
+ "cookie",
118015
+ "origin",
118016
+ "referer",
118017
+ "x-camstack-dataplane-secret",
118018
+ "x-forwarded-for",
118019
+ "x-forwarded-host",
118020
+ "x-forwarded-proto",
118021
+ "x-forwarded-prefix",
118022
+ "x-real-ip",
118023
+ "forwarded"
118024
+ ]);
118025
+ /**
118026
+ * Response headers dropped: framing/CSP/HSTS that break a page under another origin, and
118027
+ * the ones that no longer describe the (decoded, possibly rewritten) body.
118028
+ */
118029
+ var DROP_RESPONSE_HEADERS = new Set([
118030
+ "content-security-policy",
118031
+ "content-security-policy-report-only",
118032
+ "x-frame-options",
118033
+ "strict-transport-security",
118034
+ "public-key-pins",
118035
+ "report-to",
118036
+ "nel",
118037
+ "content-encoding",
118038
+ "content-length",
118039
+ "transfer-encoding",
118040
+ "connection",
118041
+ "keep-alive",
118042
+ "set-cookie",
118043
+ "location"
118044
+ ]);
118045
+ function bodyKindOf(contentType) {
118046
+ const type = contentType.toLowerCase();
118047
+ if (type.includes("text/html") || type.includes("application/xhtml")) return "html";
118048
+ if (type.includes("text/css")) return "css";
118049
+ if (type.startsWith("text/") || type.includes("javascript") || type.includes("json") || type.includes("xml")) return "other";
118050
+ return null;
118051
+ }
118052
+ function firstHeader(value) {
118053
+ return Array.isArray(value) ? value[0] : value;
118054
+ }
118055
+ async function readRequestBody(req) {
118056
+ const chunks = [];
118057
+ let size = 0;
118058
+ for await (const chunk of req) {
118059
+ const buf = typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk);
118060
+ size += buf.length;
118061
+ if (size > MAX_REQUEST_BODY_BYTES) return "too-large";
118062
+ chunks.push(buf);
118063
+ }
118064
+ return Buffer.concat(chunks);
118065
+ }
118066
+ function sendPlain(res, status, text) {
118067
+ res.writeHead(status, {
118068
+ "content-type": "text/plain; charset=utf-8",
118069
+ "cache-control": "no-store"
118070
+ });
118071
+ res.end(text);
118072
+ }
118073
+ var VendorLoginProxy = class {
118074
+ #addonId;
118075
+ #logger;
118076
+ #now;
118077
+ #fetch;
118078
+ #attempts = /* @__PURE__ */ new Map();
118079
+ constructor(options) {
118080
+ this.#addonId = options.addonId;
118081
+ this.#logger = options.logger;
118082
+ this.#now = options.now ?? (() => Date.now());
118083
+ this.#fetch = options.fetchFn ?? globalThis.fetch.bind(globalThis);
118084
+ }
118085
+ /** The `DataPlaneHandler` to pass to `ctx.dataPlane.serve({ prefix: VENDOR_LOGIN_PROXY_PREFIX, access: 'admin', … })`. */
118086
+ handler = (req, res) => this.#handle(req, res);
118087
+ /** Register an attempt. A one-shot capability: refused hosts are rejected up front. */
118088
+ open(spec) {
118089
+ for (const upstream of spec.upstreams) if (!isValidUpstream(upstream)) throw new Error(`vendor login proxy: upstream ${upstream.host} is not allowed`);
118090
+ this.#attempts.set(spec.attemptId, {
118091
+ expiresAtMs: spec.expiresAtMs,
118092
+ upstreams: spec.upstreams,
118093
+ evaluate: async (view) => {
118094
+ const verdict = await spec.terminal(view);
118095
+ if (verdict.kind === "continue") return verdict;
118096
+ return {
118097
+ kind: "done",
118098
+ landing: await spec.onDone(verdict.payload)
118099
+ };
118100
+ }
118101
+ });
118102
+ }
118103
+ close(attemptId) {
118104
+ this.#attempts.delete(attemptId);
118105
+ }
118106
+ isOpen(attemptId) {
118107
+ const attempt = this.#attempts.get(attemptId);
118108
+ return attempt !== void 0 && this.#now() <= attempt.expiresAtMs;
118109
+ }
118110
+ /**
118111
+ * The hub-relative path that opens `vendorUrl` through the proxy for this attempt:
118112
+ * the URL the browser is sent to. Null when the host is not in the attempt's list.
118113
+ */
118114
+ entryPath(attemptId, vendorUrl) {
118115
+ const attempt = this.#attempts.get(attemptId);
118116
+ if (attempt === void 0) return null;
118117
+ const url = new URL(vendorUrl);
118118
+ if (findUpstream({
118119
+ basePath: "",
118120
+ upstreams: attempt.upstreams
118121
+ }, url.host) === void 0) return null;
118122
+ return `/addon/${this.#addonId}/${VENDOR_LOGIN_PROXY_PREFIX}/${attemptId}/${url.host}${url.pathname}${url.search}`;
118123
+ }
118124
+ #routeFor(attemptId, attempt, forwardedPrefix) {
118125
+ return {
118126
+ basePath: `${forwardedPrefix}/addon/${this.#addonId}/${VENDOR_LOGIN_PROXY_PREFIX}/${attemptId}`,
118127
+ upstreams: attempt.upstreams
118128
+ };
118129
+ }
118130
+ async #handle(req, res) {
118131
+ try {
118132
+ await this.#serve(req, res);
118133
+ } catch (err) {
118134
+ this.#logger.warn("vendor login proxy: request failed", { meta: {
118135
+ addonId: this.#addonId,
118136
+ error: err instanceof Error ? err.name : "unknown"
118137
+ } });
118138
+ if (!res.headersSent) sendPlain(res, 502, "The sign-in page could not be reached.");
118139
+ else res.end();
118140
+ }
118141
+ }
118142
+ async #serve(req, res) {
118143
+ const parsed = parseProxyPath(req.url ?? "/");
118144
+ if (parsed === null) {
118145
+ sendPlain(res, 404, "Not found");
118146
+ return;
118147
+ }
118148
+ const forwardedPrefix = (firstHeader(req.headers["x-forwarded-prefix"]) ?? "").replace(/\/+$/, "");
118149
+ const attempt = this.#attempts.get(parsed.attemptId);
118150
+ if (attempt === void 0 || this.#now() > attempt.expiresAtMs) {
118151
+ if (attempt !== void 0) this.#attempts.delete(parsed.attemptId);
118152
+ this.#logger.info("vendor login proxy: attempt refused", { meta: {
118153
+ addonId: this.#addonId,
118154
+ reason: attempt === void 0 ? "missing" : "expired"
118155
+ } });
118156
+ this.#redirect(res, forwardedPrefix, {
118157
+ status: "error",
118158
+ addonId: this.#addonId,
118159
+ reason: "invalid_state"
118160
+ });
118161
+ return;
118162
+ }
118163
+ const route = this.#routeFor(parsed.attemptId, attempt, forwardedPrefix);
118164
+ const upstream = findUpstream(route, parsed.host);
118165
+ const target = upstream === void 0 ? null : upstreamUrlFor(upstream, parsed);
118166
+ if (upstream === void 0 || target === null) {
118167
+ this.#logger.warn("vendor login proxy: host refused", { meta: {
118168
+ addonId: this.#addonId,
118169
+ host: parsed.host
118170
+ } });
118171
+ sendPlain(res, 404, "Not found");
118172
+ return;
118173
+ }
118174
+ const method = (req.method ?? "GET").toUpperCase();
118175
+ let body;
118176
+ if (method !== "GET" && method !== "HEAD") {
118177
+ const read = await readRequestBody(req);
118178
+ if (read === "too-large") {
118179
+ sendPlain(res, 413, "Request too large");
118180
+ return;
118181
+ }
118182
+ body = read;
118183
+ }
118184
+ const headers = this.#requestHeaders(req, route, upstream);
118185
+ const upstreamRes = await this.#fetch(target, {
118186
+ method,
118187
+ headers,
118188
+ redirect: "manual",
118189
+ ...body !== void 0 && body.length > 0 ? { body: new Uint8Array(body) } : {}
118190
+ });
118191
+ const location = upstreamRes.headers.get("location");
118192
+ const setCookies = upstreamRes.headers.getSetCookie();
118193
+ const kind = bodyKindOf(upstreamRes.headers.get("content-type") ?? "");
118194
+ const declaredLength = Number(upstreamRes.headers.get("content-length") ?? "0");
118195
+ const text = kind !== null && !(declaredLength > MAX_TEXT_BODY_BYTES) && method !== "HEAD" ? await upstreamRes.text() : null;
118196
+ this.#logger.debug("vendor login proxy: upstream answered", { meta: {
118197
+ addonId: this.#addonId,
118198
+ host: upstream.host,
118199
+ status: upstreamRes.status,
118200
+ method
118201
+ } });
118202
+ const settled = await attempt.evaluate({
118203
+ status: upstreamRes.status,
118204
+ url: target.toString(),
118205
+ location,
118206
+ setCookies,
118207
+ bodyText: text
118208
+ });
118209
+ if (settled.kind === "done") {
118210
+ this.#attempts.delete(parsed.attemptId);
118211
+ this.#logger.info("vendor login proxy: sign-in finished", { meta: {
118212
+ addonId: this.#addonId,
118213
+ status: settled.landing.status
118214
+ } });
118215
+ this.#redirect(res, forwardedPrefix, settled.landing);
118216
+ return;
118217
+ }
118218
+ const out = {};
118219
+ upstreamRes.headers.forEach((value, name) => {
118220
+ if (!DROP_RESPONSE_HEADERS.has(name.toLowerCase())) out[name] = value;
118221
+ });
118222
+ if (setCookies.length > 0) out["set-cookie"] = setCookies.map((c) => rewriteSetCookie(c, route));
118223
+ if (location !== null) out["location"] = rewriteLocation(location, upstream, route);
118224
+ out["cache-control"] = "no-store";
118225
+ if (text !== null && kind !== null) {
118226
+ const rewritten = rewriteBody(text, kind, upstream, route);
118227
+ res.writeHead(upstreamRes.status, out);
118228
+ res.end(rewritten);
118229
+ return;
118230
+ }
118231
+ res.writeHead(upstreamRes.status, out);
118232
+ await this.#pipeBody(upstreamRes, res);
118233
+ }
118234
+ #requestHeaders(req, route, upstream) {
118235
+ const headers = {};
118236
+ for (const [name, value] of Object.entries(req.headers)) {
118237
+ const lower = name.toLowerCase();
118238
+ if (DROP_REQUEST_HEADERS.has(lower) || value === void 0) continue;
118239
+ headers[lower] = Array.isArray(value) ? value.join(", ") : value;
118240
+ }
118241
+ const cookie = filterForwardedCookies(firstHeader(req.headers["cookie"]) ?? "");
118242
+ if (cookie.length > 0) headers["cookie"] = cookie;
118243
+ if (req.headers["origin"] !== void 0) headers["origin"] = `${upstream.scheme}://${upstream.host}`;
118244
+ const referer = firstHeader(req.headers["referer"]);
118245
+ if (referer !== void 0) {
118246
+ const back = rewriteReferer(referer, route);
118247
+ if (back !== null) headers["referer"] = back;
118248
+ }
118249
+ return headers;
118250
+ }
118251
+ async #pipeBody(upstreamRes, res) {
118252
+ if (upstreamRes.body === null) {
118253
+ res.end();
118254
+ return;
118255
+ }
118256
+ for await (const chunk of upstreamRes.body) {
118257
+ if (!res.write(chunk)) await new Promise((resolve) => {
118258
+ res.once("drain", resolve);
118259
+ res.once("close", resolve);
118260
+ });
118261
+ if (res.destroyed) return;
118262
+ }
118263
+ res.end();
118264
+ }
118265
+ #redirect(res, forwardedPrefix, outcome) {
118266
+ res.writeHead(302, {
118267
+ location: `${forwardedPrefix}${oauthLandingUrl(null, outcome)}`,
118268
+ "cache-control": "no-store"
118269
+ });
118270
+ res.end();
118271
+ }
118272
+ };
118273
+ //#endregion
117848
118274
  //#region src/utils/pool-memory-watchdog.ts
117849
118275
  var MB = 1024 * 1024;
117850
118276
  var DEFAULT_POOL_MEMORY_POLICY = {
@@ -120925,6 +121351,7 @@ exports.UserRecordSchema = UserRecordSchema;
120925
121351
  exports.UserScopeSchema = UserScopeSchema;
120926
121352
  exports.UserSummarySchema = UserSummarySchema;
120927
121353
  exports.VECTOR_INDEX_DIM_CONFLICT = VECTOR_INDEX_DIM_CONFLICT;
121354
+ exports.VENDOR_LOGIN_PROXY_PREFIX = VENDOR_LOGIN_PROXY_PREFIX;
120928
121355
  exports.VIDEOCLIPS_MAX_OFFER_BYTES = VIDEOCLIPS_MAX_OFFER_BYTES;
120929
121356
  exports.VIDEOCLIPS_MAX_READ_BYTES = VIDEOCLIPS_MAX_READ_BYTES;
120930
121357
  exports.VIEWER_FEATURES = VIEWER_FEATURES;
@@ -120952,6 +121379,7 @@ exports.VectorStatsInputSchema = VectorStatsInputSchema;
120952
121379
  exports.VectorStatsResultSchema = VectorStatsResultSchema;
120953
121380
  exports.VectorUpsertInputSchema = VectorUpsertInputSchema;
120954
121381
  exports.VectorUpsertResultSchema = VectorUpsertResultSchema;
121382
+ exports.VendorLoginProxy = VendorLoginProxy;
120955
121383
  exports.VibrationStatusSchema = VibrationStatusSchema;
120956
121384
  exports.VideoEncodeSchema = VideoEncodeSchema;
120957
121385
  exports.ViewerDeviceAllowSchema = ViewerDeviceAllowSchema;
@@ -121849,6 +122277,10 @@ exports.resultCellKey = resultCellKey;
121849
122277
  exports.resultStandings = resultStandings;
121850
122278
  exports.retrainBoxIou = retrainBoxIou;
121851
122279
  exports.retrainRevisionConflictMessage = retrainRevisionConflictMessage;
122280
+ exports.rewriteLoginProxyBody = rewriteBody;
122281
+ exports.rewriteLoginProxyLocation = rewriteLocation;
122282
+ exports.rewriteLoginProxyReferer = rewriteReferer;
122283
+ exports.rewriteLoginProxySetCookie = rewriteSetCookie;
121852
122284
  exports.roleSpec = roleSpec;
121853
122285
  exports.rollingDurationMs = require_composition.rollingDurationMs;
121854
122286
  exports.roomCompositionExample = roomCompositionExample;