@monetizationos/proxy 1.4.2 → 1.4.3

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/README.md CHANGED
@@ -52,6 +52,30 @@ export default {
52
52
 
53
53
  Stages 3–6 run on HTML responses only and auto-skip for everything else. Call `.withoutHtmlTransformation()` to disable them entirely.
54
54
 
55
+ ## Configuration
56
+
57
+ Optional fields on `MOSConfigInput`:
58
+
59
+ | Field | Description |
60
+ | --- | --- |
61
+ | `mosEndpointsPrefix` | Path prefix routed to the MonetizationOS endpoint proxy. Default: `/mos-endpoints/`. |
62
+ | `surfaceDecisionsIgnorePaths` | Comma-separated regex patterns. Matching request pathnames skip the surface-decisions call. |
63
+ | `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. |
64
+ | `createAnonymousIdentifierFallback` | When `true` (default), JWT surface-decision requests ask MonetizationOS to mint an anonymous identifier if JWT auth fails. |
65
+ | `originRequestHeaders` | Headers added to or replacing client headers on every origin request. |
66
+ | `injectScriptUrl` | Script URL injected into the `<head>` of HTML responses. |
67
+
68
+ Example — forward specific cookies to surface decisions:
69
+
70
+ ```ts
71
+ .withConfig({
72
+ // ...
73
+ surfaceDecisionsCookies: "^__session$, ^theme$, ^mos_",
74
+ })
75
+ ```
76
+
77
+ 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_`.
78
+
55
79
  ## Adapters
56
80
 
57
81
  - `Fetcher` — `(request: Request) => Promise<Response>`. Configure separately for origin traffic (`.withOriginFetcher`) and MOS API traffic (`.withApiFetcher`). Both default to `globalThis.fetch`; override on runtimes that need a backend binding (e.g. Fastly Compute) or custom dispatch.
@@ -29,6 +29,8 @@ import type { MOSConfigInput, MosAuthenticatedApiRoute } from './types';
29
29
  * authenticatedUserJwtCookieName: '__session',
30
30
  * injectScriptUrl: 'https://assets.monetizationos.com/web-components-latest.js',
31
31
  * surfaceDecisionsIgnorePaths: '',
32
+ * // Optional: forward specific cookies to surface decisions
33
+ * surfaceDecisionsCookies: '^__session$, ^theme$',
32
34
  * originRequestHeaders: { 'X-Api-Key': process.env.ORIGIN_API_KEY! },
33
35
  * createAnonymousIdentifierFallback: true,
34
36
  * })
@@ -22,6 +22,8 @@ import { MOSProxy } from './MOSProxy';
22
22
  * authenticatedUserJwtCookieName: '__session',
23
23
  * injectScriptUrl: 'https://assets.monetizationos.com/web-components-latest.js',
24
24
  * surfaceDecisionsIgnorePaths: '',
25
+ * // Optional: forward specific cookies to surface decisions
26
+ * surfaceDecisionsCookies: '^__session$, ^theme$',
25
27
  * originRequestHeaders: { 'X-Api-Key': process.env.ORIGIN_API_KEY! },
26
28
  * createAnonymousIdentifierFallback: true,
27
29
  * })
@@ -1 +1 @@
1
- {"version":3,"file":"MOSProxyBuilder.js","sourceRoot":"","sources":["../src/MOSProxyBuilder.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ClientMetadataProvider } from './adapters/ClientMetadataProvider'\nimport type { Fetcher } from './adapters/Fetcher'\nimport type { HtmlRewriterAdapter } from './adapters/HtmlRewriterAdapter'\nimport type { IdentityProvider } from './adapters/IdentityProvider'\nimport type { ResourceProvider } from './adapters/ResourceProvider'\nimport type { MOSProxyLogger } from './logger'\nimport { MOSProxy, type MOSProxyHtmlPipelineErrorHandler, type MOSProxyOptions } from './MOSProxy'\nimport type { MOSConfigInput, MosAuthenticatedApiRoute } from './types'\n\n/**\n * Fluent builder for `MOSProxy`. Provide configuration and platform adapters, then `build()`.\n *\n * Defaults:\n * - custom endpoint routing: enabled\n * - link rewriting: enabled\n * - surface decisions: enabled\n * - HTML transformation: enabled (auto-skipped per request for non-HTML responses)\n * - origin/API fetcher: `globalThis.fetch` (override on runtimes that need a backend binding, e.g. Fastly)\n *\n * @example\n * ```ts\n * const proxy = new MOSProxyBuilder()\n * .withConfig({\n * originUrl: 'https://news.example.com',\n * surfaceSlug: 'web',\n * mosHost: 'https://api.monetizationos.com',\n * mosSecretKey: process.env.MONETIZATION_OS_SECRET_KEY!,\n * mosEndpointsPrefix: '/mos-endpoints/',\n * anonymousSessionCookieName: 'anon-session-id',\n * authenticatedUserJwtCookieName: '__session',\n * injectScriptUrl: 'https://assets.monetizationos.com/web-components-latest.js',\n * surfaceDecisionsIgnorePaths: '',\n * originRequestHeaders: { 'X-Api-Key': process.env.ORIGIN_API_KEY! },\n * createAnonymousIdentifierFallback: true,\n * })\n * .withHtmlRewriter(myHtmlRewriterAdapter)\n * .build()\n *\n * export default { fetch: (request: Request) => proxy.handle(request) }\n * ```\n */\nexport class MOSProxyBuilder {\n private _config: MOSConfigInput | null = null\n private _originFetcher: Fetcher = globalThis.fetch\n private _apiFetcher: Fetcher = globalThis.fetch\n private _htmlRewriter: HtmlRewriterAdapter | null = null\n private _clientMetadataProvider: ClientMetadataProvider | null = null\n private _identityProvider: IdentityProvider | null = null\n private _resourceProvider: ResourceProvider | null = null\n private _logger: MOSProxyLogger | null = null\n private _onHtmlPipelineError: MOSProxyHtmlPipelineErrorHandler | null = null\n private _customEndpoints = true\n private _linkRewriting = true\n private _surfaceDecisions = true\n private _htmlTransformation = true\n private _additionalAuthenticatedApiRoutes: MosAuthenticatedApiRoute[] = []\n\n withConfig(config: MOSConfigInput): this {\n this._config = config\n return this\n }\n\n withOriginFetcher(fetcher: Fetcher): this {\n this._originFetcher = fetcher\n return this\n }\n\n withApiFetcher(fetcher: Fetcher): this {\n this._apiFetcher = fetcher\n return this\n }\n\n withHtmlRewriter(rewriter: HtmlRewriterAdapter): this {\n this._htmlRewriter = rewriter\n return this\n }\n\n withClientMetadata(provider: ClientMetadataProvider): this {\n this._clientMetadataProvider = provider\n return this\n }\n\n /**\n * Override identity provision for the surface-decisions API. Provide `resolve` to control the\n * identity payload sent to the API (e.g. resolved from a request header instead of cookies),\n * and/or `persist` to control how identity is recorded back on the response (e.g. suppress the\n * default anonymous-session cookie or write it elsewhere). Either method is optional; omitted\n * methods fall back to the built-in defaults.\n */\n withIdentityProvider(provider: IdentityProvider): this {\n this._identityProvider = provider\n return this\n }\n\n /**\n * Override the resource object sent to the surface-decisions API per request. `build` receives\n * only the `Request`; the proxy derives defaults (`{ id: pathname, meta: pageMetadata }`) and\n * shallow-merges your returned record over them. Return only the keys you want to add or\n * override (e.g. a content tier or canonical id) — `id`/`meta` are preserved unless you set them.\n */\n withResourceProvider(provider: ResourceProvider): this {\n this._resourceProvider = provider\n return this\n }\n\n withLogger(logger: MOSProxyLogger): this {\n this._logger = logger\n return this\n }\n\n /**\n * Register a callback to handle HTML pipeline errors. The proxy fails open by default and\n * returns the last safe (unconsumed) response — provide this if you want to shape that\n * response yourself (e.g. render a custom error page, return a 503, or re-throw so your\n * platform's error middleware handles it). If the callback throws or returns a non-Response\n * value, the proxy logs a warning and falls back to the last safe response.\n */\n withHtmlPipelineErrorHandler(handler: MOSProxyHtmlPipelineErrorHandler): this {\n this._onHtmlPipelineError = handler\n return this\n }\n\n withoutCustomEndpoints(): this {\n this._customEndpoints = false\n return this\n }\n\n withoutLinkRewriting(): this {\n this._linkRewriting = false\n return this\n }\n\n withoutSurfaceDecisions(): this {\n this._surfaceDecisions = false\n return this\n }\n\n /**\n * Disable the HTML transformation pipeline entirely (stages 3–6).\n *\n * Use for API-only proxies: neither an HTML rewriter adapter nor an API fetcher is required\n * if custom endpoints and surface decisions are both disabled alongside this.\n */\n withoutHtmlTransformation(): this {\n this._htmlTransformation = false\n return this\n }\n\n /**\n * Add an additional route which, when matched by an incoming request, will be forwarded to the MonetizationOS API\n * with authentication handled by the configured identity provider.\n * This is in addition to the default `/mos-api/offer-redemptions` route.\n */\n withMosAuthenticatedApiRoutes(...routes: MosAuthenticatedApiRoute[]): this {\n this._additionalAuthenticatedApiRoutes.push(...routes)\n return this\n }\n\n build(): MOSProxy {\n if (!this._config) {\n throw new Error('MOSProxyBuilder: withConfig(...) is required')\n }\n\n const needsHtmlRewriter = this._htmlTransformation && this._surfaceDecisions\n if (needsHtmlRewriter && !this._htmlRewriter) {\n throw new Error(\n 'MOSProxyBuilder: withHtmlRewriter(...) is required when surface decisions is enabled; call withoutSurfaceDecisions() or withoutHtmlTransformation() if you do not need it',\n )\n }\n\n const opts: MOSProxyOptions = {\n config: this._config,\n originFetcher: this._originFetcher,\n apiFetcher: this._apiFetcher,\n htmlRewriter: this._htmlRewriter,\n clientMetadataProvider: this._clientMetadataProvider,\n identityProvider: this._identityProvider,\n resourceProvider: this._resourceProvider,\n logger: this._logger ?? undefined,\n onHtmlPipelineError: this._onHtmlPipelineError ?? undefined,\n customEndpointsEnabled: this._customEndpoints,\n linkRewritingEnabled: this._linkRewriting,\n surfaceDecisionsEnabled: this._surfaceDecisions,\n htmlTransformationEnabled: this._htmlTransformation,\n mosAuthenticatedApiRoutes: [\n ...this._additionalAuthenticatedApiRoutes,\n {\n matchPath: '/mos-api/offer-redemptions',\n method: 'POST',\n mosPath: '/api/v1/offer-redemptions',\n },\n ],\n }\n return new MOSProxy(opts)\n }\n}\n"]}
1
+ {"version":3,"file":"MOSProxyBuilder.js","sourceRoot":"","sources":["../src/MOSProxyBuilder.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ClientMetadataProvider } from './adapters/ClientMetadataProvider'\nimport type { Fetcher } from './adapters/Fetcher'\nimport type { HtmlRewriterAdapter } from './adapters/HtmlRewriterAdapter'\nimport type { IdentityProvider } from './adapters/IdentityProvider'\nimport type { ResourceProvider } from './adapters/ResourceProvider'\nimport type { MOSProxyLogger } from './logger'\nimport { MOSProxy, type MOSProxyHtmlPipelineErrorHandler, type MOSProxyOptions } from './MOSProxy'\nimport type { MOSConfigInput, MosAuthenticatedApiRoute } from './types'\n\n/**\n * Fluent builder for `MOSProxy`. Provide configuration and platform adapters, then `build()`.\n *\n * Defaults:\n * - custom endpoint routing: enabled\n * - link rewriting: enabled\n * - surface decisions: enabled\n * - HTML transformation: enabled (auto-skipped per request for non-HTML responses)\n * - origin/API fetcher: `globalThis.fetch` (override on runtimes that need a backend binding, e.g. Fastly)\n *\n * @example\n * ```ts\n * const proxy = new MOSProxyBuilder()\n * .withConfig({\n * originUrl: 'https://news.example.com',\n * surfaceSlug: 'web',\n * mosHost: 'https://api.monetizationos.com',\n * mosSecretKey: process.env.MONETIZATION_OS_SECRET_KEY!,\n * mosEndpointsPrefix: '/mos-endpoints/',\n * anonymousSessionCookieName: 'anon-session-id',\n * authenticatedUserJwtCookieName: '__session',\n * injectScriptUrl: 'https://assets.monetizationos.com/web-components-latest.js',\n * surfaceDecisionsIgnorePaths: '',\n * // Optional: forward specific cookies to surface decisions\n * surfaceDecisionsCookies: '^__session$, ^theme$',\n * originRequestHeaders: { 'X-Api-Key': process.env.ORIGIN_API_KEY! },\n * createAnonymousIdentifierFallback: true,\n * })\n * .withHtmlRewriter(myHtmlRewriterAdapter)\n * .build()\n *\n * export default { fetch: (request: Request) => proxy.handle(request) }\n * ```\n */\nexport class MOSProxyBuilder {\n private _config: MOSConfigInput | null = null\n private _originFetcher: Fetcher = globalThis.fetch\n private _apiFetcher: Fetcher = globalThis.fetch\n private _htmlRewriter: HtmlRewriterAdapter | null = null\n private _clientMetadataProvider: ClientMetadataProvider | null = null\n private _identityProvider: IdentityProvider | null = null\n private _resourceProvider: ResourceProvider | null = null\n private _logger: MOSProxyLogger | null = null\n private _onHtmlPipelineError: MOSProxyHtmlPipelineErrorHandler | null = null\n private _customEndpoints = true\n private _linkRewriting = true\n private _surfaceDecisions = true\n private _htmlTransformation = true\n private _additionalAuthenticatedApiRoutes: MosAuthenticatedApiRoute[] = []\n\n withConfig(config: MOSConfigInput): this {\n this._config = config\n return this\n }\n\n withOriginFetcher(fetcher: Fetcher): this {\n this._originFetcher = fetcher\n return this\n }\n\n withApiFetcher(fetcher: Fetcher): this {\n this._apiFetcher = fetcher\n return this\n }\n\n withHtmlRewriter(rewriter: HtmlRewriterAdapter): this {\n this._htmlRewriter = rewriter\n return this\n }\n\n withClientMetadata(provider: ClientMetadataProvider): this {\n this._clientMetadataProvider = provider\n return this\n }\n\n /**\n * Override identity provision for the surface-decisions API. Provide `resolve` to control the\n * identity payload sent to the API (e.g. resolved from a request header instead of cookies),\n * and/or `persist` to control how identity is recorded back on the response (e.g. suppress the\n * default anonymous-session cookie or write it elsewhere). Either method is optional; omitted\n * methods fall back to the built-in defaults.\n */\n withIdentityProvider(provider: IdentityProvider): this {\n this._identityProvider = provider\n return this\n }\n\n /**\n * Override the resource object sent to the surface-decisions API per request. `build` receives\n * only the `Request`; the proxy derives defaults (`{ id: pathname, meta: pageMetadata }`) and\n * shallow-merges your returned record over them. Return only the keys you want to add or\n * override (e.g. a content tier or canonical id) — `id`/`meta` are preserved unless you set them.\n */\n withResourceProvider(provider: ResourceProvider): this {\n this._resourceProvider = provider\n return this\n }\n\n withLogger(logger: MOSProxyLogger): this {\n this._logger = logger\n return this\n }\n\n /**\n * Register a callback to handle HTML pipeline errors. The proxy fails open by default and\n * returns the last safe (unconsumed) response — provide this if you want to shape that\n * response yourself (e.g. render a custom error page, return a 503, or re-throw so your\n * platform's error middleware handles it). If the callback throws or returns a non-Response\n * value, the proxy logs a warning and falls back to the last safe response.\n */\n withHtmlPipelineErrorHandler(handler: MOSProxyHtmlPipelineErrorHandler): this {\n this._onHtmlPipelineError = handler\n return this\n }\n\n withoutCustomEndpoints(): this {\n this._customEndpoints = false\n return this\n }\n\n withoutLinkRewriting(): this {\n this._linkRewriting = false\n return this\n }\n\n withoutSurfaceDecisions(): this {\n this._surfaceDecisions = false\n return this\n }\n\n /**\n * Disable the HTML transformation pipeline entirely (stages 3–6).\n *\n * Use for API-only proxies: neither an HTML rewriter adapter nor an API fetcher is required\n * if custom endpoints and surface decisions are both disabled alongside this.\n */\n withoutHtmlTransformation(): this {\n this._htmlTransformation = false\n return this\n }\n\n /**\n * Add an additional route which, when matched by an incoming request, will be forwarded to the MonetizationOS API\n * with authentication handled by the configured identity provider.\n * This is in addition to the default `/mos-api/offer-redemptions` route.\n */\n withMosAuthenticatedApiRoutes(...routes: MosAuthenticatedApiRoute[]): this {\n this._additionalAuthenticatedApiRoutes.push(...routes)\n return this\n }\n\n build(): MOSProxy {\n if (!this._config) {\n throw new Error('MOSProxyBuilder: withConfig(...) is required')\n }\n\n const needsHtmlRewriter = this._htmlTransformation && this._surfaceDecisions\n if (needsHtmlRewriter && !this._htmlRewriter) {\n throw new Error(\n 'MOSProxyBuilder: withHtmlRewriter(...) is required when surface decisions is enabled; call withoutSurfaceDecisions() or withoutHtmlTransformation() if you do not need it',\n )\n }\n\n const opts: MOSProxyOptions = {\n config: this._config,\n originFetcher: this._originFetcher,\n apiFetcher: this._apiFetcher,\n htmlRewriter: this._htmlRewriter,\n clientMetadataProvider: this._clientMetadataProvider,\n identityProvider: this._identityProvider,\n resourceProvider: this._resourceProvider,\n logger: this._logger ?? undefined,\n onHtmlPipelineError: this._onHtmlPipelineError ?? undefined,\n customEndpointsEnabled: this._customEndpoints,\n linkRewritingEnabled: this._linkRewriting,\n surfaceDecisionsEnabled: this._surfaceDecisions,\n htmlTransformationEnabled: this._htmlTransformation,\n mosAuthenticatedApiRoutes: [\n ...this._additionalAuthenticatedApiRoutes,\n {\n matchPath: '/mos-api/offer-redemptions',\n method: 'POST',\n mosPath: '/api/v1/offer-redemptions',\n },\n ],\n }\n return new MOSProxy(opts)\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  export declare const MOS_PROXY_VERSION_HEADER = "X-MOS-Proxy-Version";
2
2
  export declare const MOS_PROXY_CLIENT_HEADER = "X-MOS-Proxy-Client";
3
- export declare const MOS_PROXY_PACKAGE_VERSION = "1.4.2";
3
+ export declare const MOS_PROXY_PACKAGE_VERSION = "1.4.3";
4
4
  export declare const setMosProxyHeaders: (headers: Headers) => void;
5
5
  export declare const withMosProxyHeaders: (headers?: HeadersInit) => Headers;
@@ -1,7 +1,7 @@
1
1
  import { formatClientRuntimeHeader } from './clientRuntime';
2
2
  export const MOS_PROXY_VERSION_HEADER = 'X-MOS-Proxy-Version';
3
3
  export const MOS_PROXY_CLIENT_HEADER = 'X-MOS-Proxy-Client';
4
- export const MOS_PROXY_PACKAGE_VERSION = '1.4.2';
4
+ export const MOS_PROXY_PACKAGE_VERSION = '1.4.3';
5
5
  export const setMosProxyHeaders = (headers) => {
6
6
  headers.set(MOS_PROXY_VERSION_HEADER, MOS_PROXY_PACKAGE_VERSION);
7
7
  headers.set(MOS_PROXY_CLIENT_HEADER, formatClientRuntimeHeader());
@@ -1 +1 @@
1
- {"version":3,"file":"apiRequestHeaders.js","sourceRoot":"","sources":["../src/apiRequestHeaders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAA;AAE3D,MAAM,CAAC,MAAM,wBAAwB,GAAG,qBAAqB,CAAA;AAC7D,MAAM,CAAC,MAAM,uBAAuB,GAAG,oBAAoB,CAAA;AAC3D,MAAM,CAAC,MAAM,yBAAyB,GAAG,OAAO,CAAA;AAEhD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,OAAgB,EAAQ,EAAE;IACzD,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,yBAAyB,CAAC,CAAA;IAChE,OAAO,CAAC,GAAG,CAAC,uBAAuB,EAAE,yBAAyB,EAAE,CAAC,CAAA;AACrE,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,OAAqB,EAAW,EAAE;IAClE,MAAM,IAAI,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IACjC,kBAAkB,CAAC,IAAI,CAAC,CAAA;IACxB,OAAO,IAAI,CAAA;AACf,CAAC,CAAA","sourcesContent":["import { formatClientRuntimeHeader } from './clientRuntime'\n\nexport const MOS_PROXY_VERSION_HEADER = 'X-MOS-Proxy-Version'\nexport const MOS_PROXY_CLIENT_HEADER = 'X-MOS-Proxy-Client'\nexport const MOS_PROXY_PACKAGE_VERSION = '1.4.2'\n\nexport const setMosProxyHeaders = (headers: Headers): void => {\n headers.set(MOS_PROXY_VERSION_HEADER, MOS_PROXY_PACKAGE_VERSION)\n headers.set(MOS_PROXY_CLIENT_HEADER, formatClientRuntimeHeader())\n}\n\nexport const withMosProxyHeaders = (headers?: HeadersInit): Headers => {\n const next = new Headers(headers)\n setMosProxyHeaders(next)\n return next\n}\n"]}
1
+ {"version":3,"file":"apiRequestHeaders.js","sourceRoot":"","sources":["../src/apiRequestHeaders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAA;AAE3D,MAAM,CAAC,MAAM,wBAAwB,GAAG,qBAAqB,CAAA;AAC7D,MAAM,CAAC,MAAM,uBAAuB,GAAG,oBAAoB,CAAA;AAC3D,MAAM,CAAC,MAAM,yBAAyB,GAAG,OAAO,CAAA;AAEhD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,OAAgB,EAAQ,EAAE;IACzD,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,yBAAyB,CAAC,CAAA;IAChE,OAAO,CAAC,GAAG,CAAC,uBAAuB,EAAE,yBAAyB,EAAE,CAAC,CAAA;AACrE,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,OAAqB,EAAW,EAAE;IAClE,MAAM,IAAI,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IACjC,kBAAkB,CAAC,IAAI,CAAC,CAAA;IACxB,OAAO,IAAI,CAAA;AACf,CAAC,CAAA","sourcesContent":["import { formatClientRuntimeHeader } from './clientRuntime'\n\nexport const MOS_PROXY_VERSION_HEADER = 'X-MOS-Proxy-Version'\nexport const MOS_PROXY_CLIENT_HEADER = 'X-MOS-Proxy-Client'\nexport const MOS_PROXY_PACKAGE_VERSION = '1.4.3'\n\nexport const setMosProxyHeaders = (headers: Headers): void => {\n headers.set(MOS_PROXY_VERSION_HEADER, MOS_PROXY_PACKAGE_VERSION)\n headers.set(MOS_PROXY_CLIENT_HEADER, formatClientRuntimeHeader())\n}\n\nexport const withMosProxyHeaders = (headers?: HeadersInit): Headers => {\n const next = new Headers(headers)\n setMosProxyHeaders(next)\n return next\n}\n"]}
package/dist/config.d.ts CHANGED
@@ -11,6 +11,7 @@ export interface MOSConfig {
11
11
  authenticatedUserJwtCookieName: string;
12
12
  injectScriptUrl: string | undefined;
13
13
  surfaceDecisionsIgnorePathPatterns: RegExp[];
14
+ surfaceDecisionsCookiePatterns: RegExp[];
14
15
  originRequestHeaders: Record<string, string>;
15
16
  createAnonymousIdentifierFallback: boolean;
16
17
  }
package/dist/config.js CHANGED
@@ -1,26 +1,32 @@
1
1
  import { consoleLogger } from './logger';
2
2
  const DEFAULT_ENDPOINTS_PREFIX = '/mos-endpoints/';
3
- export function normalizeMOSConfig(config, logger = consoleLogger) {
4
- const ignorePathPatterns = [];
5
- if (config.surfaceDecisionsIgnorePaths) {
6
- for (const raw of config.surfaceDecisionsIgnorePaths.split(',')) {
7
- const pattern = raw.trim();
8
- if (!pattern)
9
- continue;
10
- try {
11
- ignorePathPatterns.push(new RegExp(pattern));
12
- }
13
- catch (error) {
14
- logger.log({
15
- level: 'warn',
16
- code: 'invalid-ignore-path-pattern',
17
- message: `Invalid surfaceDecisionsIgnorePaths regex pattern: ${pattern}`,
18
- context: { pattern },
19
- error,
20
- });
21
- }
3
+ function parseCommaSeparatedRegexPatterns(value, invalidPatternCode, invalidPatternMessage, logger) {
4
+ const patterns = [];
5
+ if (!value) {
6
+ return patterns;
7
+ }
8
+ for (const raw of value.split(',')) {
9
+ const pattern = raw.trim();
10
+ if (!pattern)
11
+ continue;
12
+ try {
13
+ patterns.push(new RegExp(pattern));
14
+ }
15
+ catch (error) {
16
+ logger.log({
17
+ level: 'warn',
18
+ code: invalidPatternCode,
19
+ message: invalidPatternMessage(pattern),
20
+ context: { pattern },
21
+ error,
22
+ });
22
23
  }
23
24
  }
25
+ return patterns;
26
+ }
27
+ export function normalizeMOSConfig(config, logger = consoleLogger) {
28
+ const ignorePathPatterns = parseCommaSeparatedRegexPatterns(config.surfaceDecisionsIgnorePaths, 'invalid-ignore-path-pattern', (pattern) => `Invalid surfaceDecisionsIgnorePaths regex pattern: ${pattern}`, logger);
29
+ const cookiePatterns = parseCommaSeparatedRegexPatterns(config.surfaceDecisionsCookies, 'invalid-surface-decisions-cookie-pattern', (pattern) => `Invalid surfaceDecisionsCookies regex pattern: ${pattern}`, logger);
24
30
  const secretKeyParts = config.mosSecretKey.split('_');
25
31
  return {
26
32
  originUrl: new URL(config.originUrl),
@@ -33,6 +39,7 @@ export function normalizeMOSConfig(config, logger = consoleLogger) {
33
39
  authenticatedUserJwtCookieName: config.authenticatedUserJwtCookieName,
34
40
  injectScriptUrl: config.injectScriptUrl,
35
41
  surfaceDecisionsIgnorePathPatterns: ignorePathPatterns,
42
+ surfaceDecisionsCookiePatterns: cookiePatterns,
36
43
  originRequestHeaders: { ...(config.originRequestHeaders ?? {}) },
37
44
  createAnonymousIdentifierFallback: config.createAnonymousIdentifierFallback !== false,
38
45
  };
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAuB,MAAM,UAAU,CAAA;AAG7D,MAAM,wBAAwB,GAAG,iBAAiB,CAAA;AAiBlD,MAAM,UAAU,kBAAkB,CAAC,MAAsB,EAAE,SAAyB,aAAa;IAC7F,MAAM,kBAAkB,GAAa,EAAE,CAAA;IACvC,IAAI,MAAM,CAAC,2BAA2B,EAAE,CAAC;QACrC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,2BAA2B,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9D,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;YAC1B,IAAI,CAAC,OAAO;gBAAE,SAAQ;YACtB,IAAI,CAAC;gBACD,kBAAkB,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;YAChD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,MAAM,CAAC,GAAG,CAAC;oBACP,KAAK,EAAE,MAAM;oBACb,IAAI,EAAE,6BAA6B;oBACnC,OAAO,EAAE,sDAAsD,OAAO,EAAE;oBACxE,OAAO,EAAE,EAAE,OAAO,EAAE;oBACpB,KAAK;iBACR,CAAC,CAAA;YACN,CAAC;QACL,CAAC;IACL,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAErD,OAAO;QACH,SAAS,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC;QACpC,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,OAAO,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC;QAChC,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,cAAc,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,cAAc,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE;QACvE,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,IAAI,wBAAwB;QACzE,0BAA0B,EAAE,MAAM,CAAC,0BAA0B;QAC7D,8BAA8B,EAAE,MAAM,CAAC,8BAA8B;QACrE,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,kCAAkC,EAAE,kBAAkB;QACtD,oBAAoB,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,oBAAoB,IAAI,EAAE,CAAC,EAAE;QAChE,iCAAiC,EAAE,MAAM,CAAC,iCAAiC,KAAK,KAAK;KACxF,CAAA;AACL,CAAC","sourcesContent":["import { consoleLogger, type MOSProxyLogger } from './logger'\nimport type { MOSConfigInput } from './types'\n\nconst DEFAULT_ENDPOINTS_PREFIX = '/mos-endpoints/'\n\nexport interface MOSConfig {\n originUrl: URL\n surfaceSlug: string\n mosHost: URL\n mosSecretKey: string\n mosEnvironment: string\n mosEndpointsPrefix: string\n anonymousSessionCookieName: string\n authenticatedUserJwtCookieName: string\n injectScriptUrl: string | undefined\n surfaceDecisionsIgnorePathPatterns: RegExp[]\n originRequestHeaders: Record<string, string>\n createAnonymousIdentifierFallback: boolean\n}\n\nexport function normalizeMOSConfig(config: MOSConfigInput, logger: MOSProxyLogger = consoleLogger): MOSConfig {\n const ignorePathPatterns: RegExp[] = []\n if (config.surfaceDecisionsIgnorePaths) {\n for (const raw of config.surfaceDecisionsIgnorePaths.split(',')) {\n const pattern = raw.trim()\n if (!pattern) continue\n try {\n ignorePathPatterns.push(new RegExp(pattern))\n } catch (error) {\n logger.log({\n level: 'warn',\n code: 'invalid-ignore-path-pattern',\n message: `Invalid surfaceDecisionsIgnorePaths regex pattern: ${pattern}`,\n context: { pattern },\n error,\n })\n }\n }\n }\n\n const secretKeyParts = config.mosSecretKey.split('_')\n\n return {\n originUrl: new URL(config.originUrl),\n surfaceSlug: config.surfaceSlug,\n mosHost: new URL(config.mosHost),\n mosSecretKey: config.mosSecretKey,\n mosEnvironment: `${secretKeyParts[1] ?? ''}_${secretKeyParts[2] ?? ''}`,\n mosEndpointsPrefix: config.mosEndpointsPrefix || DEFAULT_ENDPOINTS_PREFIX,\n anonymousSessionCookieName: config.anonymousSessionCookieName,\n authenticatedUserJwtCookieName: config.authenticatedUserJwtCookieName,\n injectScriptUrl: config.injectScriptUrl,\n surfaceDecisionsIgnorePathPatterns: ignorePathPatterns,\n originRequestHeaders: { ...(config.originRequestHeaders ?? {}) },\n createAnonymousIdentifierFallback: config.createAnonymousIdentifierFallback !== false,\n }\n}\n"]}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA6C,MAAM,UAAU,CAAA;AAGnF,MAAM,wBAAwB,GAAG,iBAAiB,CAAA;AAElD,SAAS,gCAAgC,CACrC,KAAyB,EACzB,kBAAmC,EACnC,qBAAkD,EAClD,MAAsB;IAEtB,MAAM,QAAQ,GAAa,EAAE,CAAA;IAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;QACT,OAAO,QAAQ,CAAA;IACnB,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;QAC1B,IAAI,CAAC,OAAO;YAAE,SAAQ;QACtB,IAAI,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;QACtC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,CAAC,GAAG,CAAC;gBACP,KAAK,EAAE,MAAM;gBACb,IAAI,EAAE,kBAAkB;gBACxB,OAAO,EAAE,qBAAqB,CAAC,OAAO,CAAC;gBACvC,OAAO,EAAE,EAAE,OAAO,EAAE;gBACpB,KAAK;aACR,CAAC,CAAA;QACN,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAA;AACnB,CAAC;AAkBD,MAAM,UAAU,kBAAkB,CAAC,MAAsB,EAAE,SAAyB,aAAa;IAC7F,MAAM,kBAAkB,GAAG,gCAAgC,CACvD,MAAM,CAAC,2BAA2B,EAClC,6BAA6B,EAC7B,CAAC,OAAO,EAAE,EAAE,CAAC,sDAAsD,OAAO,EAAE,EAC5E,MAAM,CACT,CAAA;IACD,MAAM,cAAc,GAAG,gCAAgC,CACnD,MAAM,CAAC,uBAAuB,EAC9B,0CAA0C,EAC1C,CAAC,OAAO,EAAE,EAAE,CAAC,kDAAkD,OAAO,EAAE,EACxE,MAAM,CACT,CAAA;IAED,MAAM,cAAc,GAAG,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAErD,OAAO;QACH,SAAS,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC;QACpC,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,OAAO,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC;QAChC,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,cAAc,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,cAAc,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE;QACvE,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,IAAI,wBAAwB;QACzE,0BAA0B,EAAE,MAAM,CAAC,0BAA0B;QAC7D,8BAA8B,EAAE,MAAM,CAAC,8BAA8B;QACrE,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,kCAAkC,EAAE,kBAAkB;QACtD,8BAA8B,EAAE,cAAc;QAC9C,oBAAoB,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,oBAAoB,IAAI,EAAE,CAAC,EAAE;QAChE,iCAAiC,EAAE,MAAM,CAAC,iCAAiC,KAAK,KAAK;KACxF,CAAA;AACL,CAAC","sourcesContent":["import { consoleLogger, type MOSProxyLogCode, type MOSProxyLogger } from './logger'\nimport type { MOSConfigInput } from './types'\n\nconst DEFAULT_ENDPOINTS_PREFIX = '/mos-endpoints/'\n\nfunction parseCommaSeparatedRegexPatterns(\n value: string | undefined,\n invalidPatternCode: MOSProxyLogCode,\n invalidPatternMessage: (pattern: string) => string,\n logger: MOSProxyLogger,\n): RegExp[] {\n const patterns: RegExp[] = []\n if (!value) {\n return patterns\n }\n\n for (const raw of value.split(',')) {\n const pattern = raw.trim()\n if (!pattern) continue\n try {\n patterns.push(new RegExp(pattern))\n } catch (error) {\n logger.log({\n level: 'warn',\n code: invalidPatternCode,\n message: invalidPatternMessage(pattern),\n context: { pattern },\n error,\n })\n }\n }\n\n return patterns\n}\n\nexport interface MOSConfig {\n originUrl: URL\n surfaceSlug: string\n mosHost: URL\n mosSecretKey: string\n mosEnvironment: string\n mosEndpointsPrefix: string\n anonymousSessionCookieName: string\n authenticatedUserJwtCookieName: string\n injectScriptUrl: string | undefined\n surfaceDecisionsIgnorePathPatterns: RegExp[]\n surfaceDecisionsCookiePatterns: RegExp[]\n originRequestHeaders: Record<string, string>\n createAnonymousIdentifierFallback: boolean\n}\n\nexport function normalizeMOSConfig(config: MOSConfigInput, logger: MOSProxyLogger = consoleLogger): MOSConfig {\n const ignorePathPatterns = parseCommaSeparatedRegexPatterns(\n config.surfaceDecisionsIgnorePaths,\n 'invalid-ignore-path-pattern',\n (pattern) => `Invalid surfaceDecisionsIgnorePaths regex pattern: ${pattern}`,\n logger,\n )\n const cookiePatterns = parseCommaSeparatedRegexPatterns(\n config.surfaceDecisionsCookies,\n 'invalid-surface-decisions-cookie-pattern',\n (pattern) => `Invalid surfaceDecisionsCookies regex pattern: ${pattern}`,\n logger,\n )\n\n const secretKeyParts = config.mosSecretKey.split('_')\n\n return {\n originUrl: new URL(config.originUrl),\n surfaceSlug: config.surfaceSlug,\n mosHost: new URL(config.mosHost),\n mosSecretKey: config.mosSecretKey,\n mosEnvironment: `${secretKeyParts[1] ?? ''}_${secretKeyParts[2] ?? ''}`,\n mosEndpointsPrefix: config.mosEndpointsPrefix || DEFAULT_ENDPOINTS_PREFIX,\n anonymousSessionCookieName: config.anonymousSessionCookieName,\n authenticatedUserJwtCookieName: config.authenticatedUserJwtCookieName,\n injectScriptUrl: config.injectScriptUrl,\n surfaceDecisionsIgnorePathPatterns: ignorePathPatterns,\n surfaceDecisionsCookiePatterns: cookiePatterns,\n originRequestHeaders: { ...(config.originRequestHeaders ?? {}) },\n createAnonymousIdentifierFallback: config.createAnonymousIdentifierFallback !== false,\n }\n}\n"]}
package/dist/logger.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export type MOSProxyLogLevel = 'warn' | 'error';
2
- export type MOSProxyLogCode = 'html-pipeline-failed' | 'html-pipeline-error-handler-invalid' | 'html-pipeline-error-handler-threw' | 'invalid-ignore-path-pattern' | 'response-clone-failed' | 'surface-decisions-api-failed' | 'identity-resolve-failed' | 'identity-persist-failed' | 'link-rewriting-header-failed' | 'link-rewriting-body-failed' | 'marker-pass-invalid-state' | 'marker-pass-selector-failed' | 'replacement-markers-identical' | 'replacement-state-missing' | 'element-render-failed' | 'element-type-unsupported' | 'page-metadata-parse-failed' | 'component-selector-unsupported' | 'component-range-replacement-skipped' | 'component-transform-failed';
2
+ export type MOSProxyLogCode = 'html-pipeline-failed' | 'html-pipeline-error-handler-invalid' | 'html-pipeline-error-handler-threw' | 'invalid-ignore-path-pattern' | 'invalid-surface-decisions-cookie-pattern' | 'response-clone-failed' | 'surface-decisions-api-failed' | 'identity-resolve-failed' | 'identity-persist-failed' | 'link-rewriting-header-failed' | 'link-rewriting-body-failed' | 'marker-pass-invalid-state' | 'marker-pass-selector-failed' | 'replacement-markers-identical' | 'replacement-state-missing' | 'element-render-failed' | 'element-type-unsupported' | 'page-metadata-parse-failed' | 'component-selector-unsupported' | 'component-range-replacement-skipped' | 'component-transform-failed';
3
3
  export interface MOSProxyLogEvent {
4
4
  level: MOSProxyLogLevel;
5
5
  code: MOSProxyLogCode;
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAoCA,MAAM,CAAC,MAAM,aAAa,GAAmB;IACzC,GAAG,CAAC,KAAK;QACL,MAAM,OAAO,GAAc,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;QAClF,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QAC7B,CAAC;QAED,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;YAC1B,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAA;YACzB,OAAM;QACV,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAA;IAC5B,CAAC;CACJ,CAAA","sourcesContent":["export type MOSProxyLogLevel = 'warn' | 'error'\n\nexport type MOSProxyLogCode =\n | 'html-pipeline-failed'\n | 'html-pipeline-error-handler-invalid'\n | 'html-pipeline-error-handler-threw'\n | 'invalid-ignore-path-pattern'\n | 'response-clone-failed'\n | 'surface-decisions-api-failed'\n | 'identity-resolve-failed'\n | 'identity-persist-failed'\n | 'link-rewriting-header-failed'\n | 'link-rewriting-body-failed'\n | 'marker-pass-invalid-state'\n | 'marker-pass-selector-failed'\n | 'replacement-markers-identical'\n | 'replacement-state-missing'\n | 'element-render-failed'\n | 'element-type-unsupported'\n | 'page-metadata-parse-failed'\n | 'component-selector-unsupported'\n | 'component-range-replacement-skipped'\n | 'component-transform-failed'\n\nexport interface MOSProxyLogEvent {\n level: MOSProxyLogLevel\n code: MOSProxyLogCode\n message: string\n context?: Record<string, unknown>\n error?: unknown\n}\n\nexport interface MOSProxyLogger {\n log(event: MOSProxyLogEvent): void\n}\n\nexport const consoleLogger: MOSProxyLogger = {\n log(event) {\n const details: unknown[] = [event.message, { code: event.code, ...event.context }]\n if (event.error) {\n details.push(event.error)\n }\n\n if (event.level === 'error') {\n console.error(...details)\n return\n }\n\n console.warn(...details)\n },\n}\n"]}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAqCA,MAAM,CAAC,MAAM,aAAa,GAAmB;IACzC,GAAG,CAAC,KAAK;QACL,MAAM,OAAO,GAAc,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;QAClF,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QAC7B,CAAC;QAED,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;YAC1B,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAA;YACzB,OAAM;QACV,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAA;IAC5B,CAAC;CACJ,CAAA","sourcesContent":["export type MOSProxyLogLevel = 'warn' | 'error'\n\nexport type MOSProxyLogCode =\n | 'html-pipeline-failed'\n | 'html-pipeline-error-handler-invalid'\n | 'html-pipeline-error-handler-threw'\n | 'invalid-ignore-path-pattern'\n | 'invalid-surface-decisions-cookie-pattern'\n | 'response-clone-failed'\n | 'surface-decisions-api-failed'\n | 'identity-resolve-failed'\n | 'identity-persist-failed'\n | 'link-rewriting-header-failed'\n | 'link-rewriting-body-failed'\n | 'marker-pass-invalid-state'\n | 'marker-pass-selector-failed'\n | 'replacement-markers-identical'\n | 'replacement-state-missing'\n | 'element-render-failed'\n | 'element-type-unsupported'\n | 'page-metadata-parse-failed'\n | 'component-selector-unsupported'\n | 'component-range-replacement-skipped'\n | 'component-transform-failed'\n\nexport interface MOSProxyLogEvent {\n level: MOSProxyLogLevel\n code: MOSProxyLogCode\n message: string\n context?: Record<string, unknown>\n error?: unknown\n}\n\nexport interface MOSProxyLogger {\n log(event: MOSProxyLogEvent): void\n}\n\nexport const consoleLogger: MOSProxyLogger = {\n log(event) {\n const details: unknown[] = [event.message, { code: event.code, ...event.context }]\n if (event.error) {\n details.push(event.error)\n }\n\n if (event.level === 'error') {\n console.error(...details)\n return\n }\n\n console.warn(...details)\n },\n}\n"]}
@@ -10,6 +10,7 @@ export type FetchSurfaceDecisionsArgs = {
10
10
  resource: Resource;
11
11
  userAgent?: string | undefined;
12
12
  referer?: string | undefined;
13
+ cookies?: Record<string, string> | undefined;
13
14
  originStatus: number;
14
15
  };
15
16
  export type FetchSurfaceDecisionsFailureReason = 'request-failed' | 'invalid-json' | 'api-error' | 'http-error' | 'invalid-response';
@@ -23,4 +24,4 @@ export type FetchSurfaceDecisionsResult = {
23
24
  status?: number;
24
25
  statusCode?: number;
25
26
  };
26
- export default function fetchSurfaceDecisions(ctx: PipelineContext, { identity, url, clientMetadata, resource, userAgent, referer, originStatus }: FetchSurfaceDecisionsArgs, apiFetcher: Fetcher): Promise<FetchSurfaceDecisionsResult>;
27
+ export default function fetchSurfaceDecisions(ctx: PipelineContext, { identity, url, clientMetadata, resource, userAgent, referer, cookies, originStatus }: FetchSurfaceDecisionsArgs, apiFetcher: Fetcher): Promise<FetchSurfaceDecisionsResult>;
@@ -1,5 +1,5 @@
1
1
  import { withMosProxyHeaders } from '../apiRequestHeaders';
2
- export default async function fetchSurfaceDecisions(ctx, { identity, url, clientMetadata, resource, userAgent, referer, originStatus }, apiFetcher) {
2
+ export default async function fetchSurfaceDecisions(ctx, { identity, url, clientMetadata, resource, userAgent, referer, cookies, originStatus }, apiFetcher) {
3
3
  const { config } = ctx;
4
4
  const body = JSON.stringify({
5
5
  ...clientMetadata,
@@ -10,6 +10,7 @@ export default async function fetchSurfaceDecisions(ctx, { identity, url, client
10
10
  url,
11
11
  userAgent,
12
12
  referer,
13
+ cookies,
13
14
  proxyOrigin: {
14
15
  status: originStatus,
15
16
  },
@@ -1 +1 @@
1
- {"version":3,"file":"fetchSurfaceDecisions.js","sourceRoot":"","sources":["../../src/stages/fetchSurfaceDecisions.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Fetcher } from '../adapters/Fetcher'\nimport type { Identity } from '../adapters/IdentityProvider'\nimport type { Resource } from '../adapters/ResourceProvider'\nimport { withMosProxyHeaders } from '../apiRequestHeaders'\nimport type { PipelineContext } from '../context'\nimport type { SurfaceDecisionError, SurfaceDecisionResponse } from '../types'\n\nexport type FetchSurfaceDecisionsArgs = {\n identity: Identity\n url: string\n clientMetadata: Record<string, unknown>\n resource: Resource\n userAgent?: string | undefined\n referer?: string | undefined\n originStatus: number\n}\n\nexport type FetchSurfaceDecisionsFailureReason = 'request-failed' | 'invalid-json' | 'api-error' | 'http-error' | 'invalid-response'\n\nexport type FetchSurfaceDecisionsResult =\n | {\n ok: true\n data: SurfaceDecisionResponse\n }\n | {\n ok: false\n reason: FetchSurfaceDecisionsFailureReason\n error: unknown\n status?: number\n statusCode?: number\n }\n\nexport default async function fetchSurfaceDecisions(\n ctx: PipelineContext,\n { identity, url, clientMetadata, resource, userAgent, referer, originStatus }: FetchSurfaceDecisionsArgs,\n apiFetcher: Fetcher,\n): Promise<FetchSurfaceDecisionsResult> {\n const { config } = ctx\n const body = JSON.stringify({\n ...clientMetadata,\n surfaceSlug: config.surfaceSlug,\n identity,\n resource,\n http: {\n url,\n userAgent,\n referer,\n proxyOrigin: {\n status: originStatus,\n },\n },\n })\n\n const request = new Request(new URL('/api/v1/surface-decisions', config.mosHost), {\n method: 'POST',\n body,\n headers: withMosProxyHeaders({\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.mosSecretKey}`,\n }),\n })\n\n let response: Response\n try {\n response = await apiFetcher(request)\n } catch (error) {\n return { ok: false, reason: 'request-failed', error }\n }\n\n let data: unknown\n try {\n data = await response.json()\n } catch (error) {\n return { ok: false, reason: 'invalid-json', error, status: response.status }\n }\n\n if (isSurfaceDecisionError(data)) {\n return {\n ok: false,\n reason: 'api-error',\n error: new Error(data.message),\n status: response.status,\n statusCode: data.statusCode,\n }\n }\n\n if (!response.ok) {\n return {\n ok: false,\n reason: 'http-error',\n error: new Error(`Surface decisions API returned HTTP ${response.status}`),\n status: response.status,\n }\n }\n\n if (!isSurfaceDecisionResponse(data)) {\n return {\n ok: false,\n reason: 'invalid-response',\n error: new Error('Surface decisions API returned an invalid response shape'),\n status: response.status,\n }\n }\n\n return { ok: true, data }\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null\n\nconst isSurfaceDecisionError = (value: unknown): value is SurfaceDecisionError =>\n isRecord(value) && value.status === 'error' && typeof value.message === 'string' && typeof value.statusCode === 'number'\n\nconst isSurfaceDecisionResponse = (value: unknown): value is SurfaceDecisionResponse =>\n isRecord(value) &&\n value.status === 'success' &&\n isRecord(value.identity) &&\n isRecord(value.features) &&\n isRecord(value.customer) &&\n isRecord(value.surfaceBehavior) &&\n typeof value.componentsSkipped === 'boolean' &&\n isRecord(value.componentBehaviors)\n"]}
1
+ {"version":3,"file":"fetchSurfaceDecisions.js","sourceRoot":"","sources":["../../src/stages/fetchSurfaceDecisions.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Fetcher } from '../adapters/Fetcher'\nimport type { Identity } from '../adapters/IdentityProvider'\nimport type { Resource } from '../adapters/ResourceProvider'\nimport { withMosProxyHeaders } from '../apiRequestHeaders'\nimport type { PipelineContext } from '../context'\nimport type { SurfaceDecisionError, SurfaceDecisionResponse } from '../types'\n\nexport type FetchSurfaceDecisionsArgs = {\n identity: Identity\n url: string\n clientMetadata: Record<string, unknown>\n resource: Resource\n userAgent?: string | undefined\n referer?: string | undefined\n cookies?: Record<string, string> | undefined\n originStatus: number\n}\n\nexport type FetchSurfaceDecisionsFailureReason = 'request-failed' | 'invalid-json' | 'api-error' | 'http-error' | 'invalid-response'\n\nexport type FetchSurfaceDecisionsResult =\n | {\n ok: true\n data: SurfaceDecisionResponse\n }\n | {\n ok: false\n reason: FetchSurfaceDecisionsFailureReason\n error: unknown\n status?: number\n statusCode?: number\n }\n\nexport default async function fetchSurfaceDecisions(\n ctx: PipelineContext,\n { identity, url, clientMetadata, resource, userAgent, referer, cookies, originStatus }: FetchSurfaceDecisionsArgs,\n apiFetcher: Fetcher,\n): Promise<FetchSurfaceDecisionsResult> {\n const { config } = ctx\n const body = JSON.stringify({\n ...clientMetadata,\n surfaceSlug: config.surfaceSlug,\n identity,\n resource,\n http: {\n url,\n userAgent,\n referer,\n cookies,\n proxyOrigin: {\n status: originStatus,\n },\n },\n })\n\n const request = new Request(new URL('/api/v1/surface-decisions', config.mosHost), {\n method: 'POST',\n body,\n headers: withMosProxyHeaders({\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.mosSecretKey}`,\n }),\n })\n\n let response: Response\n try {\n response = await apiFetcher(request)\n } catch (error) {\n return { ok: false, reason: 'request-failed', error }\n }\n\n let data: unknown\n try {\n data = await response.json()\n } catch (error) {\n return { ok: false, reason: 'invalid-json', error, status: response.status }\n }\n\n if (isSurfaceDecisionError(data)) {\n return {\n ok: false,\n reason: 'api-error',\n error: new Error(data.message),\n status: response.status,\n statusCode: data.statusCode,\n }\n }\n\n if (!response.ok) {\n return {\n ok: false,\n reason: 'http-error',\n error: new Error(`Surface decisions API returned HTTP ${response.status}`),\n status: response.status,\n }\n }\n\n if (!isSurfaceDecisionResponse(data)) {\n return {\n ok: false,\n reason: 'invalid-response',\n error: new Error('Surface decisions API returned an invalid response shape'),\n status: response.status,\n }\n }\n\n return { ok: true, data }\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null\n\nconst isSurfaceDecisionError = (value: unknown): value is SurfaceDecisionError =>\n isRecord(value) && value.status === 'error' && typeof value.message === 'string' && typeof value.statusCode === 'number'\n\nconst isSurfaceDecisionResponse = (value: unknown): value is SurfaceDecisionResponse =>\n isRecord(value) &&\n value.status === 'success' &&\n isRecord(value.identity) &&\n isRecord(value.features) &&\n isRecord(value.customer) &&\n isRecord(value.surfaceBehavior) &&\n typeof value.componentsSkipped === 'boolean' &&\n isRecord(value.componentBehaviors)\n"]}
@@ -1,4 +1,5 @@
1
1
  import { defaultPersistIdentity, defaultResolveIdentity } from '../adapters/IdentityProvider';
2
+ import { selectSurfaceDecisionCookies } from '../surfaceDecisionCookies';
2
3
  import fetchSurfaceDecisions from './fetchSurfaceDecisions';
3
4
  import { parsePageMetadata } from './pageMetadata';
4
5
  export default async function getSurfaceDecisions(ctx, request, response, apiFetcher, rewriter, clientMetadataProvider, identityProvider, resourceProvider) {
@@ -29,6 +30,7 @@ export default async function getSurfaceDecisions(ctx, request, response, apiFet
29
30
  resource,
30
31
  userAgent: request.headers.get('User-Agent') ?? undefined,
31
32
  referer: request.headers.get('Referer') ?? undefined,
33
+ cookies: selectSurfaceDecisionCookies(request.headers.get('Cookie'), response, config.surfaceDecisionsCookiePatterns),
32
34
  originStatus: response.status,
33
35
  }, apiFetcher);
34
36
  if (!result.ok) {
@@ -1 +1 @@
1
- {"version":3,"file":"surfaceDecisions.js","sourceRoot":"","sources":["../../src/stages/surfaceDecisions.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ClientMetadataProvider } from '../adapters/ClientMetadataProvider'\nimport type { Fetcher } from '../adapters/Fetcher'\nimport type { HtmlRewriterAdapter } from '../adapters/HtmlRewriterAdapter'\nimport { defaultPersistIdentity, defaultResolveIdentity, type Identity, type IdentityProvider } from '../adapters/IdentityProvider'\nimport type { ResourceProvider } from '../adapters/ResourceProvider'\nimport type { PipelineContext } from '../context'\nimport type { SurfaceDecisionResponse } from '../types'\nimport fetchSurfaceDecisions from './fetchSurfaceDecisions'\nimport { parsePageMetadata } from './pageMetadata'\n\nexport default async function getSurfaceDecisions(\n ctx: PipelineContext,\n request: Request,\n response: Response,\n apiFetcher: Fetcher,\n rewriter: HtmlRewriterAdapter | null,\n clientMetadataProvider: ClientMetadataProvider | null,\n identityProvider: IdentityProvider | null,\n resourceProvider: ResourceProvider | null,\n): Promise<[Response, SurfaceDecisionResponse | null]> {\n const { config, logger } = ctx\n\n const resolve = identityProvider?.resolve ?? defaultResolveIdentity\n let identity: Identity\n try {\n identity = await resolve({ request, originResponse: response, config, logger })\n } catch (error) {\n logger.log({\n level: 'warn',\n code: 'identity-resolve-failed',\n message: 'Identity provider resolve threw; skipping surface decisions and returning origin response.',\n error,\n })\n return [response, null]\n }\n\n const [metadataStream, passThroughStream] = response.body?.tee() ?? [null, null]\n const pageMetadata = metadataStream && rewriter ? await parsePageMetadata(ctx, new Response(metadataStream, response), rewriter) : {}\n let modifiedResponse = passThroughStream ? new Response(passThroughStream, response) : response\n\n const clientMetadata = clientMetadataProvider?.build(request) ?? {}\n\n const resource = { id: new URL(request.url).pathname, meta: pageMetadata, ...resourceProvider?.build(request) }\n\n const result = await fetchSurfaceDecisions(\n ctx,\n {\n identity,\n url: request.url,\n clientMetadata,\n resource,\n userAgent: request.headers.get('User-Agent') ?? undefined,\n referer: request.headers.get('Referer') ?? undefined,\n originStatus: response.status,\n },\n apiFetcher,\n )\n\n if (!result.ok) {\n logger.log({\n level: 'warn',\n code: 'surface-decisions-api-failed',\n message: 'Surface decisions API failed; continuing with the origin response.',\n context: {\n reason: result.reason,\n status: result.status,\n statusCode: result.statusCode,\n },\n error: result.error,\n })\n return [modifiedResponse, null]\n }\n\n const surfaceDecisions = result.data\n\n const persist = identityProvider?.persist ?? defaultPersistIdentity\n try {\n modifiedResponse = await persist({\n resolved: identity,\n decisions: surfaceDecisions,\n response: modifiedResponse,\n request,\n config,\n logger,\n })\n } catch (error) {\n logger.log({\n level: 'warn',\n code: 'identity-persist-failed',\n message: 'Identity provider persist threw; keeping pre-persist response.',\n error,\n })\n }\n\n return [modifiedResponse, surfaceDecisions]\n}\n"]}
1
+ {"version":3,"file":"surfaceDecisions.js","sourceRoot":"","sources":["../../src/stages/surfaceDecisions.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ClientMetadataProvider } from '../adapters/ClientMetadataProvider'\nimport type { Fetcher } from '../adapters/Fetcher'\nimport type { HtmlRewriterAdapter } from '../adapters/HtmlRewriterAdapter'\nimport { defaultPersistIdentity, defaultResolveIdentity, type Identity, type IdentityProvider } from '../adapters/IdentityProvider'\nimport type { ResourceProvider } from '../adapters/ResourceProvider'\nimport type { PipelineContext } from '../context'\nimport { selectSurfaceDecisionCookies } from '../surfaceDecisionCookies'\nimport type { SurfaceDecisionResponse } from '../types'\nimport fetchSurfaceDecisions from './fetchSurfaceDecisions'\nimport { parsePageMetadata } from './pageMetadata'\n\nexport default async function getSurfaceDecisions(\n ctx: PipelineContext,\n request: Request,\n response: Response,\n apiFetcher: Fetcher,\n rewriter: HtmlRewriterAdapter | null,\n clientMetadataProvider: ClientMetadataProvider | null,\n identityProvider: IdentityProvider | null,\n resourceProvider: ResourceProvider | null,\n): Promise<[Response, SurfaceDecisionResponse | null]> {\n const { config, logger } = ctx\n\n const resolve = identityProvider?.resolve ?? defaultResolveIdentity\n let identity: Identity\n try {\n identity = await resolve({ request, originResponse: response, config, logger })\n } catch (error) {\n logger.log({\n level: 'warn',\n code: 'identity-resolve-failed',\n message: 'Identity provider resolve threw; skipping surface decisions and returning origin response.',\n error,\n })\n return [response, null]\n }\n\n const [metadataStream, passThroughStream] = response.body?.tee() ?? [null, null]\n const pageMetadata = metadataStream && rewriter ? await parsePageMetadata(ctx, new Response(metadataStream, response), rewriter) : {}\n let modifiedResponse = passThroughStream ? new Response(passThroughStream, response) : response\n\n const clientMetadata = clientMetadataProvider?.build(request) ?? {}\n\n const resource = { id: new URL(request.url).pathname, meta: pageMetadata, ...resourceProvider?.build(request) }\n\n const result = await fetchSurfaceDecisions(\n ctx,\n {\n identity,\n url: request.url,\n clientMetadata,\n resource,\n userAgent: request.headers.get('User-Agent') ?? undefined,\n referer: request.headers.get('Referer') ?? undefined,\n cookies: selectSurfaceDecisionCookies(request.headers.get('Cookie'), response, config.surfaceDecisionsCookiePatterns),\n originStatus: response.status,\n },\n apiFetcher,\n )\n\n if (!result.ok) {\n logger.log({\n level: 'warn',\n code: 'surface-decisions-api-failed',\n message: 'Surface decisions API failed; continuing with the origin response.',\n context: {\n reason: result.reason,\n status: result.status,\n statusCode: result.statusCode,\n },\n error: result.error,\n })\n return [modifiedResponse, null]\n }\n\n const surfaceDecisions = result.data\n\n const persist = identityProvider?.persist ?? defaultPersistIdentity\n try {\n modifiedResponse = await persist({\n resolved: identity,\n decisions: surfaceDecisions,\n response: modifiedResponse,\n request,\n config,\n logger,\n })\n } catch (error) {\n logger.log({\n level: 'warn',\n code: 'identity-persist-failed',\n message: 'Identity provider persist threw; keeping pre-persist response.',\n error,\n })\n }\n\n return [modifiedResponse, surfaceDecisions]\n}\n"]}
@@ -0,0 +1 @@
1
+ export declare function selectSurfaceDecisionCookies(cookieHeader: string | null, originResponse: Response | undefined, patterns: RegExp[]): Record<string, string> | undefined;
@@ -0,0 +1,24 @@
1
+ import { parse, parseSetCookie } from 'cookie';
2
+ export function selectSurfaceDecisionCookies(cookieHeader, originResponse, patterns) {
3
+ if (patterns.length === 0) {
4
+ return undefined;
5
+ }
6
+ const selected = {};
7
+ for (const [name, value] of Object.entries(parse(cookieHeader || ''))) {
8
+ if (value === undefined)
9
+ continue;
10
+ if (patterns.some((pattern) => pattern.test(name))) {
11
+ selected[name] = value;
12
+ }
13
+ }
14
+ const setCookies = originResponse?.headers.getSetCookie().map((header) => parseSetCookie(header)) ?? [];
15
+ for (const cookie of setCookies) {
16
+ if (!cookie.name || cookie.value === undefined)
17
+ continue;
18
+ if (patterns.some((pattern) => pattern.test(cookie.name))) {
19
+ selected[cookie.name] = cookie.value;
20
+ }
21
+ }
22
+ return Object.keys(selected).length > 0 ? selected : undefined;
23
+ }
24
+ //# sourceMappingURL=surfaceDecisionCookies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surfaceDecisionCookies.js","sourceRoot":"","sources":["../src/surfaceDecisionCookies.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAE9C,MAAM,UAAU,4BAA4B,CACxC,YAA2B,EAC3B,cAAoC,EACpC,QAAkB;IAElB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,SAAS,CAAA;IACpB,CAAC;IAED,MAAM,QAAQ,GAA2B,EAAE,CAAA;IAE3C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QACpE,IAAI,KAAK,KAAK,SAAS;YAAE,SAAQ;QACjC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACjD,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAA;QAC1B,CAAC;IACL,CAAC;IAED,MAAM,UAAU,GAAG,cAAc,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;IACvG,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;QAC9B,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS;YAAE,SAAQ;QACxD,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACxD,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,CAAA;QACxC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAA;AAClE,CAAC","sourcesContent":["import { parse, parseSetCookie } from 'cookie'\n\nexport function selectSurfaceDecisionCookies(\n cookieHeader: string | null,\n originResponse: Response | undefined,\n patterns: RegExp[],\n): Record<string, string> | undefined {\n if (patterns.length === 0) {\n return undefined\n }\n\n const selected: Record<string, string> = {}\n\n for (const [name, value] of Object.entries(parse(cookieHeader || ''))) {\n if (value === undefined) continue\n if (patterns.some((pattern) => pattern.test(name))) {\n selected[name] = value\n }\n }\n\n const setCookies = originResponse?.headers.getSetCookie().map((header) => parseSetCookie(header)) ?? []\n for (const cookie of setCookies) {\n if (!cookie.name || cookie.value === undefined) continue\n if (patterns.some((pattern) => pattern.test(cookie.name))) {\n selected[cookie.name] = cookie.value\n }\n }\n\n return Object.keys(selected).length > 0 ? selected : undefined\n}\n"]}
package/dist/types.d.ts CHANGED
@@ -116,6 +116,8 @@ export interface MOSConfigInput {
116
116
  injectScriptUrl?: string;
117
117
  /** Comma-separated regex patterns; matching pathnames skip surface decisions. */
118
118
  surfaceDecisionsIgnorePaths?: string;
119
+ /** Comma-separated regex patterns; matching request and origin Set-Cookie values are forwarded in the surface-decisions http payload. */
120
+ surfaceDecisionsCookies?: string;
119
121
  /** Optional headers added to or replacing client headers on every origin request. */
120
122
  originRequestHeaders?: Record<string, string>;
121
123
  /**
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export type PageMetadata = Record<string, string>\n\nexport interface FeatureMeterableProperty {\n type: 'meterable'\n hasAccess: boolean\n remainingUnits?: number\n}\n\nexport interface FeatureNumberProperty {\n type: 'number'\n value: number\n}\n\nexport interface Feature {\n featureSlug: string\n properties: Record<string, FeatureMeterableProperty | FeatureNumberProperty>\n sideEffects: unknown[]\n}\n\nexport interface SubSurfaceMetadataApi {\n cssSelector?: string | null\n}\n\nexport type SetHttpResponse = {\n headers?: Record<string, string>\n cookies?: string[]\n status: number\n statusText?: string\n body: string | null\n}\n\nexport type ModifyHttpResponse = {\n addHeaders?: { name: string; value: string }[]\n removeHeaders?: string[]\n addCookies?: string[]\n status?: number\n statusText?: string\n body?: string | null\n}\n\nexport type SurfaceBehaviorApi = {\n http?: ModifyHttpResponse | SetHttpResponse\n properties?: Record<string, unknown>\n} & Record<string, unknown>\n\nexport type WebComponentElement<T = Record<string, unknown>> = {\n schema: string\n props: T\n}\n\nexport type WebElement =\n | {\n type: 'html'\n content: string\n }\n | {\n type: 'text'\n content: string\n }\n | ({\n type: 'element'\n } & WebComponentElement)\n | ({\n type: 'custom'\n } & Record<string, unknown>)\n\nexport type WebComponentRangeReplacement = {\n fromMarker?: string\n toMarker?: string\n replaceWith?: WebElement[] | null\n}\n\nexport type WebContentSurfaceBehavior = {\n before?: WebElement[]\n prepend?: WebElement[]\n remove?: boolean\n replaceRange?: WebComponentRangeReplacement | null\n append?: WebElement[]\n after?: WebElement[]\n}\n\nexport type SubSurfaceBehaviorApi = {\n content?: WebContentSurfaceBehavior\n properties?: Record<string, unknown>\n metadata: SubSurfaceMetadataApi\n} & Record<string, unknown>\n\nexport interface SurfaceDecisionResponse {\n status: 'success'\n identity: {\n identifier: string\n isAuthenticated: boolean\n authType: string\n jwtClaims: Record<string, unknown>\n }\n features: Record<string, Feature>\n customer: {\n hasProducts: boolean\n }\n surfaceBehavior: SurfaceBehaviorApi\n componentsSkipped: boolean\n componentBehaviors: Record<string, SubSurfaceBehaviorApi>\n}\n\nexport interface SurfaceDecisionError {\n message: string\n status: 'error'\n statusCode: number\n}\n\nexport type MosAuthenticatedApiRoute = {\n matchPath: string\n method: 'POST' | 'PUT' | 'PATCH'\n mosPath: string\n}\n\nexport interface MOSConfigInput {\n /** Origin website base URL, e.g. `https://news.example.com`. */\n originUrl: string\n /** Surface slug identifying the MonetizationOS surface. */\n surfaceSlug: string\n /** MonetizationOS API base URL. */\n mosHost: string\n /** MonetizationOS secret key; expected format `sk_<env>_<suffix>` */\n mosSecretKey: string\n /** Path prefix routed to the MonetizationOS endpoint proxy. Default: `/mos-endpoints/`. */\n mosEndpointsPrefix?: string\n /** Cookie name storing the anonymous session id. */\n anonymousSessionCookieName: string\n /** Cookie name storing the authenticated user JWT. */\n authenticatedUserJwtCookieName: string\n /** Optional script URL injected into the `<head>` of HTML responses. */\n injectScriptUrl?: string\n /** Comma-separated regex patterns; matching pathnames skip surface decisions. */\n surfaceDecisionsIgnorePaths?: string\n /** Optional headers added to or replacing client headers on every origin request. */\n originRequestHeaders?: Record<string, string>\n /**\n * When true (default), JWT surface-decision requests include `createAnonymousIdentifierFallback: true`\n * so MonetizationOS can mint an anonymous identifier if JWT authentication fails.\n * Set to `false` to opt out.\n */\n createAnonymousIdentifierFallback?: boolean\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export type PageMetadata = Record<string, string>\n\nexport interface FeatureMeterableProperty {\n type: 'meterable'\n hasAccess: boolean\n remainingUnits?: number\n}\n\nexport interface FeatureNumberProperty {\n type: 'number'\n value: number\n}\n\nexport interface Feature {\n featureSlug: string\n properties: Record<string, FeatureMeterableProperty | FeatureNumberProperty>\n sideEffects: unknown[]\n}\n\nexport interface SubSurfaceMetadataApi {\n cssSelector?: string | null\n}\n\nexport type SetHttpResponse = {\n headers?: Record<string, string>\n cookies?: string[]\n status: number\n statusText?: string\n body: string | null\n}\n\nexport type ModifyHttpResponse = {\n addHeaders?: { name: string; value: string }[]\n removeHeaders?: string[]\n addCookies?: string[]\n status?: number\n statusText?: string\n body?: string | null\n}\n\nexport type SurfaceBehaviorApi = {\n http?: ModifyHttpResponse | SetHttpResponse\n properties?: Record<string, unknown>\n} & Record<string, unknown>\n\nexport type WebComponentElement<T = Record<string, unknown>> = {\n schema: string\n props: T\n}\n\nexport type WebElement =\n | {\n type: 'html'\n content: string\n }\n | {\n type: 'text'\n content: string\n }\n | ({\n type: 'element'\n } & WebComponentElement)\n | ({\n type: 'custom'\n } & Record<string, unknown>)\n\nexport type WebComponentRangeReplacement = {\n fromMarker?: string\n toMarker?: string\n replaceWith?: WebElement[] | null\n}\n\nexport type WebContentSurfaceBehavior = {\n before?: WebElement[]\n prepend?: WebElement[]\n remove?: boolean\n replaceRange?: WebComponentRangeReplacement | null\n append?: WebElement[]\n after?: WebElement[]\n}\n\nexport type SubSurfaceBehaviorApi = {\n content?: WebContentSurfaceBehavior\n properties?: Record<string, unknown>\n metadata: SubSurfaceMetadataApi\n} & Record<string, unknown>\n\nexport interface SurfaceDecisionResponse {\n status: 'success'\n identity: {\n identifier: string\n isAuthenticated: boolean\n authType: string\n jwtClaims: Record<string, unknown>\n }\n features: Record<string, Feature>\n customer: {\n hasProducts: boolean\n }\n surfaceBehavior: SurfaceBehaviorApi\n componentsSkipped: boolean\n componentBehaviors: Record<string, SubSurfaceBehaviorApi>\n}\n\nexport interface SurfaceDecisionError {\n message: string\n status: 'error'\n statusCode: number\n}\n\nexport type MosAuthenticatedApiRoute = {\n matchPath: string\n method: 'POST' | 'PUT' | 'PATCH'\n mosPath: string\n}\n\nexport interface MOSConfigInput {\n /** Origin website base URL, e.g. `https://news.example.com`. */\n originUrl: string\n /** Surface slug identifying the MonetizationOS surface. */\n surfaceSlug: string\n /** MonetizationOS API base URL. */\n mosHost: string\n /** MonetizationOS secret key; expected format `sk_<env>_<suffix>` */\n mosSecretKey: string\n /** Path prefix routed to the MonetizationOS endpoint proxy. Default: `/mos-endpoints/`. */\n mosEndpointsPrefix?: string\n /** Cookie name storing the anonymous session id. */\n anonymousSessionCookieName: string\n /** Cookie name storing the authenticated user JWT. */\n authenticatedUserJwtCookieName: string\n /** Optional script URL injected into the `<head>` of HTML responses. */\n injectScriptUrl?: string\n /** Comma-separated regex patterns; matching pathnames skip surface decisions. */\n surfaceDecisionsIgnorePaths?: string\n /** Comma-separated regex patterns; matching request and origin Set-Cookie values are forwarded in the surface-decisions http payload. */\n surfaceDecisionsCookies?: string\n /** Optional headers added to or replacing client headers on every origin request. */\n originRequestHeaders?: Record<string, string>\n /**\n * When true (default), JWT surface-decision requests include `createAnonymousIdentifierFallback: true`\n * so MonetizationOS can mint an anonymous identifier if JWT authentication fails.\n * Set to `false` to opt out.\n */\n createAnonymousIdentifierFallback?: boolean\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monetizationos/proxy",
3
- "version": "1.4.2",
3
+ "version": "1.4.3",
4
4
  "description": "Platform-agnostic MonetizationOS proxy core",
5
5
  "license": "MIT",
6
6
  "type": "module",