A Fetch-based MonetizationOS Proxy core: Request in, Response out, runtime-agnostic.
This package contains the shared pipeline that powers the MonetizationOS proxy workers on Cloudflare, Fastly, Akamai, and any other runtime that speaks the Fetch API. Platform-specific concerns — origin dispatch, config/secret loading, HTML rewriting — are supplied by each consumer as adapters.
npm install @monetizationos/proxyimport { MOSProxyBuilder } from "@monetizationos/proxy";
const proxy = new MOSProxyBuilder()
.withConfig({
originUrl: "https://news.example.com",
surfaceSlug: "web",
mosHost: "https://api.monetizationos.com",
mosSecretKey: process.env.MONETIZATION_OS_SECRET_KEY!,
mosEndpointsPrefix: "/mos-endpoints/",
anonymousSessionCookieName: "anon-session-id",
authenticatedUserJwtCookieName: "__session",
createAnonymousIdentifierFallback: true,
injectScriptUrl: "https://assets.monetizationos.com/web-components-latest.js",
originRequestHeaders: { "X-Api-Key": process.env.ORIGIN_API_KEY! },
})
.withHtmlRewriter(myHtmlRewriterAdapter)
.build();
export default {
fetch: (request: Request) => proxy.handle(request),
};- Custom endpoint routing (
/mos-endpoints/*→ MOS API) - Origin fetch
- Link rewriting and
<meta>extraction - Surface decisions
- Surface behavior (HTTP-level mutations)
- Surface components (DOM-level transforms)
Stages 3–6 run on HTML responses only and auto-skip for everything else. Call .withoutHtmlTransformation() to disable them entirely.
Optional fields on MOSConfigInput:
| Field | Description |
|---|---|
mosEndpointsPrefix |
Path prefix routed to the MonetizationOS endpoint proxy. Default: /mos-endpoints/. |
surfaceDecisionsIgnorePaths |
Comma-separated regex patterns. Matching request pathnames skip the surface-decisions call. |
surfaceDecisionsCookies |
Comma-separated regex patterns. Matching cookies from the incoming request and the origin Set-Cookie headers are forwarded to the surface-decisions API as http.cookies (Record<string, string>). Origin values win when the same name appears in both. Omitted when unset or when no cookies match. |
createAnonymousIdentifierFallback |
When true (default), JWT surface-decision requests ask MonetizationOS to mint an anonymous identifier if JWT auth fails. |
originRequestHeaders |
Headers added to or replacing client headers on every origin request. |
injectScriptUrl |
Script URL injected into the <head> of HTML responses. |
Example — forward specific cookies to surface decisions:
.withConfig({
// ...
surfaceDecisionsCookies: "^__session$, ^theme$, ^mos_",
})Each entry is a regex tested against the cookie name. Plain names like ^__session$ match exactly; prefixes like ^mos_ match any cookie whose name starts with mos_.
.withConfig(...) also accepts a ConfigFactory — (request: Request) => MOSConfigInput | Promise<MOSConfigInput> — so one deployment can front several brands. The factory returns the complete config for each request; look it up however you like (KV, env JSON, a host table). hostPathMatcher is a ready-made factory that picks a rule by host and path prefix and shallow-merges its config over a shared base:
import { MOSProxyBuilder, hostPathMatcher, type MOSConfigInput } from "@monetizationos/proxy";
const base = {
mosHost: "https://api.monetizationos.com",
mosSecretKey: process.env.MONETIZATION_OS_SECRET_KEY!,
anonymousSessionCookieName: "anon-session-id",
authenticatedUserJwtCookieName: "__session",
} satisfies Partial<MOSConfigInput>;
const proxy = new MOSProxyBuilder()
.withConfig(
hostPathMatcher(
[
{
host: "news.example.com",
config: { originUrl: "https://origin.news.example.com", surfaceSlug: "news-web" },
},
{
host: "news.example.com",
pathPrefix: "/sports",
config: { originUrl: "https://origin.sports.example.com", surfaceSlug: "sports-web" },
},
],
base,
),
)
.withUnresolvedConfigHandler(() => new Response("Not found", { status: 404 }))
.withHtmlRewriter(myHtmlRewriterAdapter)
.build();Fetcher—(request: Request) => Promise<Response>. Configure separately for origin traffic (.withOriginFetcher) and MOS API traffic (.withApiFetcher). Both default toglobalThis.fetch; override on runtimes that need a backend binding (e.g. Fastly Compute) or custom dispatch.HtmlRewriterAdapter— wraps the platform's lol-html binding.ClientMetadataProvider— optional; exposes platform-specific request metadata (Cloudflare'scfobject, Fastly'sevent.client, etc.).
Skip HTML transformation entirely when the proxy only needs to handle API traffic:
const proxy = new MOSProxyBuilder().withConfig(config).withoutHtmlTransformation().build();The HTML pipeline fails open by default: on error, the proxy logs a structured event and returns the last safe response so the origin page still gets served. Register an error handler to shape that response yourself — render a custom error page, return a 503, or rethrow so the host runtime's error middleware takes over.
const proxy = new MOSProxyBuilder()
.withConfig(config)
.withHtmlRewriter(myHtmlRewriterAdapter)
.withHtmlPipelineErrorHandler(({ error, stage, lastSafeResponse }) => {
// Inspect `error` / `stage`, or return your own Response.
return lastSafeResponse;
})
.withLogger({
log(event) {
console[event.level](event.message, event.context, event.error);
},
})
.build();MIT