@agent-native/core 0.84.65 → 0.84.67

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.
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
26
26
  * Body: { update: string (base64), requestSource?: string }
27
27
  */
28
28
  export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
29
- error: string;
30
29
  ok?: undefined;
30
+ error: string;
31
31
  } | {
32
32
  error?: undefined;
33
33
  ok: boolean;
@@ -41,8 +41,8 @@ export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import
41
41
  * Body: { text: string, fieldName?: string, requestSource?: string }
42
42
  */
43
43
  export declare const postCollabText: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
44
- text?: undefined;
45
44
  ok?: undefined;
45
+ text?: undefined;
46
46
  error: string;
47
47
  } | {
48
48
  error?: undefined;
@@ -3,18 +3,18 @@ declare const _default: import("../../action.js").ActionDefinition<{
3
3
  url?: string;
4
4
  filename?: string;
5
5
  }, {
6
+ id?: undefined;
6
7
  error: string;
7
8
  configured?: undefined;
8
9
  connectPath?: undefined;
9
10
  url?: undefined;
10
- id?: undefined;
11
11
  provider?: undefined;
12
12
  } | {
13
+ id?: undefined;
13
14
  error: string;
14
15
  configured: boolean;
15
16
  connectPath: string;
16
17
  url?: undefined;
17
- id?: undefined;
18
18
  provider?: undefined;
19
19
  } | {
20
20
  error?: undefined;
@@ -13,13 +13,13 @@
13
13
  export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
14
14
  count: number;
15
15
  updated?: undefined;
16
- ok?: undefined;
17
16
  error?: undefined;
17
+ ok?: undefined;
18
18
  } | {
19
19
  count?: undefined;
20
20
  updated: number;
21
- ok?: undefined;
22
21
  error?: undefined;
22
+ ok?: undefined;
23
23
  } | {
24
24
  count?: undefined;
25
25
  updated?: undefined;
@@ -41,28 +41,28 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
41
41
  thumbsUpRate: number;
42
42
  avgEvalScore: number;
43
43
  } | {
44
- ok?: undefined;
45
44
  error?: undefined;
46
45
  summary: import("./types.js").TraceSummary;
47
46
  spans: import("./types.js").TraceSpan[];
48
47
  id?: undefined;
49
- } | {
50
48
  ok?: undefined;
49
+ } | {
51
50
  error?: undefined;
52
51
  summary?: undefined;
53
52
  spans?: undefined;
54
53
  id: string;
55
- } | {
56
54
  ok?: undefined;
55
+ } | {
57
56
  summary?: undefined;
58
57
  spans?: undefined;
59
- error: any;
60
58
  id?: undefined;
59
+ error: any;
60
+ ok?: undefined;
61
61
  } | {
62
62
  error?: undefined;
63
63
  summary?: undefined;
64
64
  spans?: undefined;
65
- ok: boolean;
66
65
  id?: undefined;
66
+ ok: boolean;
67
67
  }>>;
68
68
  //# sourceMappingURL=routes.d.ts.map
@@ -15,7 +15,7 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
15
15
  error: string;
16
16
  ok?: undefined;
17
17
  } | {
18
- ok: boolean;
19
18
  error?: undefined;
19
+ ok: boolean;
20
20
  }>>;
21
21
  //# sourceMappingURL=routes.d.ts.map
@@ -49,8 +49,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
49
49
  }>;
50
50
  /** DELETE /_agent-native/resources/:id — delete a resource */
51
51
  export declare function handleDeleteResource(event: any): Promise<{
52
- error: string;
53
52
  ok?: undefined;
53
+ error: string;
54
54
  } | {
55
55
  error?: undefined;
56
56
  ok: boolean;
@@ -20,27 +20,9 @@
20
20
  * return new Response(wrapWithAnalytics(body), { ... });
21
21
  * ```
22
22
  */
23
- /**
24
- * Script hosts the injected GA loader pulls executable code from. Google Tag
25
- * Manager serves `gtag/js`, and GA4 can lazy-load additional collectors from
26
- * `www.google-analytics.com`. These must be listed in the document `script-src`
27
- * so the CSP reflects the code the framework itself injects (see
28
- * `applyDocumentCsp` in `ssr-handler.ts`).
29
- */
30
- export declare const GA_CSP_SCRIPT_HOSTS: readonly ["https://www.googletagmanager.com", "https://www.google-analytics.com"];
31
- /**
32
- * Network/image hosts used by the GA4 loader when it sends page-view and event
33
- * beacons. These are separate from `script-src`: a stricter deployment CSP with
34
- * `connect-src 'self'` or `img-src 'self'` can load gtag.js but still drop all
35
- * analytics events unless these hosts are present too.
36
- */
37
- export declare const GA_CSP_CONNECT_HOSTS: readonly ["https://www.google-analytics.com", "https://analytics.google.com", "https://stats.g.doubleclick.net", "https://region1.google-analytics.com"];
38
- export declare const GA_CSP_IMG_HOSTS: readonly ["https://www.google-analytics.com", "https://www.googletagmanager.com", "https://stats.g.doubleclick.net"];
39
23
  /**
40
24
  * The exact JS body (no surrounding `<script>` tags) of the inline gtag config
41
- * block injected next to the gtag.js loader. Returned so the SSR handler can
42
- * hash it for the `script-src` CSP directive — the hash must be computed from
43
- * the identical string that `getGaScript()` embeds, so both call this helper.
25
+ * block injected next to the gtag.js loader.
44
26
  * Returns `null` when GA is not configured.
45
27
  */
46
28
  export declare function getGaInlineConfigScriptBody(): string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/server/analytics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAuBH;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,YAC9B,kCAAkC,EAClC,kCAAkC,CAC1B,CAAC;AAEX;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,YAC/B,kCAAkC,EAClC,8BAA8B,EAC9B,iCAAiC,EACjC,sCAAsC,CAC9B,CAAC;AAEX,eAAO,MAAM,gBAAgB,YAC3B,kCAAkC,EAClC,kCAAkC,EAClC,iCAAiC,CACzB,CAAC;AAEX;;;;;;GAMG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,GAAG,IAAI,CAc3D;AAaD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,cAAc,GAAG,cAAc,CA4BtE"}
1
+ {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/server/analytics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAuBH;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,GAAG,IAAI,CAc3D;AAaD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,cAAc,GAAG,cAAc,CA4BtE"}
@@ -34,39 +34,9 @@ function getGaMeasurementId() {
34
34
  normalizeMeasurementId(process.env.AGENT_NATIVE_BUILD_GA_MEASUREMENT_ID) ||
35
35
  normalizeMeasurementId(getViteBakedGaMeasurementId()));
36
36
  }
37
- /**
38
- * Script hosts the injected GA loader pulls executable code from. Google Tag
39
- * Manager serves `gtag/js`, and GA4 can lazy-load additional collectors from
40
- * `www.google-analytics.com`. These must be listed in the document `script-src`
41
- * so the CSP reflects the code the framework itself injects (see
42
- * `applyDocumentCsp` in `ssr-handler.ts`).
43
- */
44
- export const GA_CSP_SCRIPT_HOSTS = [
45
- "https://www.googletagmanager.com",
46
- "https://www.google-analytics.com",
47
- ];
48
- /**
49
- * Network/image hosts used by the GA4 loader when it sends page-view and event
50
- * beacons. These are separate from `script-src`: a stricter deployment CSP with
51
- * `connect-src 'self'` or `img-src 'self'` can load gtag.js but still drop all
52
- * analytics events unless these hosts are present too.
53
- */
54
- export const GA_CSP_CONNECT_HOSTS = [
55
- "https://www.google-analytics.com",
56
- "https://analytics.google.com",
57
- "https://stats.g.doubleclick.net",
58
- "https://region1.google-analytics.com",
59
- ];
60
- export const GA_CSP_IMG_HOSTS = [
61
- "https://www.google-analytics.com",
62
- "https://www.googletagmanager.com",
63
- "https://stats.g.doubleclick.net",
64
- ];
65
37
  /**
66
38
  * The exact JS body (no surrounding `<script>` tags) of the inline gtag config
67
- * block injected next to the gtag.js loader. Returned so the SSR handler can
68
- * hash it for the `script-src` CSP directive — the hash must be computed from
69
- * the identical string that `getGaScript()` embeds, so both call this helper.
39
+ * block injected next to the gtag.js loader.
70
40
  * Returns `null` when GA is not configured.
71
41
  */
72
42
  export function getGaInlineConfigScriptBody() {
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/server/analytics.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Opt-in analytics injection for SSR streams.\n * Supported environment variables:\n * - `GA_MEASUREMENT_ID` — Google Analytics 4 measurement ID\n *\n * Netlify configuration-file env vars are build-time only for serverless\n * functions, so the Vite/Nitro build paths also bake this public value into\n * SSR bundles.\n *\n * Amplitude and Sentry are initialized client-side via their npm packages\n * (see `packages/core/src/client/analytics.ts`). Only GA requires script\n * tag injection because the gtag.js loader must be a `<script src>`.\n *\n * When set, the corresponding script tags are injected before `</head>`.\n * When not set, the stream passes through untouched (zero overhead).\n *\n * Usage in entry.server.tsx:\n * ```ts\n * import { wrapWithAnalytics } from \"@agent-native/core/server\";\n * return new Response(wrapWithAnalytics(body), { ... });\n * ```\n */\n\ndeclare const __AGENT_NATIVE_BUILD_GA_MEASUREMENT_ID__: string | undefined;\n\nfunction normalizeMeasurementId(value: string | undefined): string | null {\n const trimmed = value?.trim();\n return trimmed ? trimmed : null;\n}\n\nfunction getViteBakedGaMeasurementId(): string | undefined {\n return typeof __AGENT_NATIVE_BUILD_GA_MEASUREMENT_ID__ === \"string\"\n ? __AGENT_NATIVE_BUILD_GA_MEASUREMENT_ID__\n : undefined;\n}\n\nfunction getGaMeasurementId(): string | null {\n return (\n normalizeMeasurementId(process.env.GA_MEASUREMENT_ID) ||\n normalizeMeasurementId(process.env.AGENT_NATIVE_BUILD_GA_MEASUREMENT_ID) ||\n normalizeMeasurementId(getViteBakedGaMeasurementId())\n );\n}\n\n/**\n * Script hosts the injected GA loader pulls executable code from. Google Tag\n * Manager serves `gtag/js`, and GA4 can lazy-load additional collectors from\n * `www.google-analytics.com`. These must be listed in the document `script-src`\n * so the CSP reflects the code the framework itself injects (see\n * `applyDocumentCsp` in `ssr-handler.ts`).\n */\nexport const GA_CSP_SCRIPT_HOSTS = [\n \"https://www.googletagmanager.com\",\n \"https://www.google-analytics.com\",\n] as const;\n\n/**\n * Network/image hosts used by the GA4 loader when it sends page-view and event\n * beacons. These are separate from `script-src`: a stricter deployment CSP with\n * `connect-src 'self'` or `img-src 'self'` can load gtag.js but still drop all\n * analytics events unless these hosts are present too.\n */\nexport const GA_CSP_CONNECT_HOSTS = [\n \"https://www.google-analytics.com\",\n \"https://analytics.google.com\",\n \"https://stats.g.doubleclick.net\",\n \"https://region1.google-analytics.com\",\n] as const;\n\nexport const GA_CSP_IMG_HOSTS = [\n \"https://www.google-analytics.com\",\n \"https://www.googletagmanager.com\",\n \"https://stats.g.doubleclick.net\",\n] as const;\n\n/**\n * The exact JS body (no surrounding `<script>` tags) of the inline gtag config\n * block injected next to the gtag.js loader. Returned so the SSR handler can\n * hash it for the `script-src` CSP directive — the hash must be computed from\n * the identical string that `getGaScript()` embeds, so both call this helper.\n * Returns `null` when GA is not configured.\n */\nexport function getGaInlineConfigScriptBody(): string | null {\n const id = getGaMeasurementId();\n if (!id) return null;\n const jsId = JSON.stringify(id);\n return (\n `window.dataLayer=window.dataLayer||[];` +\n `function gtag(){dataLayer.push(arguments);}` +\n `gtag('js',new Date());` +\n `gtag('config',${jsId});` +\n `if(typeof sessionStorage!=='undefined'&&sessionStorage.getItem('__an_signin')){` +\n `sessionStorage.removeItem('__an_signin');` +\n `gtag('event','sign_in');` +\n `}`\n );\n}\n\nfunction getGaScript(): string | null {\n const id = getGaMeasurementId();\n if (!id) return null;\n const srcId = encodeURIComponent(id);\n const inlineBody = getGaInlineConfigScriptBody();\n return (\n `<script async src=\"https://www.googletagmanager.com/gtag/js?id=${srcId}\"></script>` +\n `<script>${inlineBody}</script>`\n );\n}\n\nexport function wrapWithAnalytics(body: ReadableStream): ReadableStream {\n const scripts = [getGaScript()].filter(Boolean).join(\"\");\n if (!scripts) return body;\n\n const decoder = new TextDecoder();\n const encoder = new TextEncoder();\n let injected = false;\n\n return body.pipeThrough(\n new TransformStream({\n transform(chunk, controller) {\n if (injected) {\n controller.enqueue(chunk);\n return;\n }\n const text = decoder.decode(chunk, { stream: true });\n const headCloseIdx = text.indexOf(\"</head>\");\n if (headCloseIdx !== -1) {\n const modified =\n text.slice(0, headCloseIdx) + scripts + text.slice(headCloseIdx);\n controller.enqueue(encoder.encode(modified));\n injected = true;\n } else {\n controller.enqueue(chunk);\n }\n },\n }),\n );\n}\n"]}
1
+ {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/server/analytics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,SAAS,sBAAsB,CAAC,KAAyB;IACvD,MAAM,OAAO,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;IAC9B,OAAO,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AAClC,CAAC;AAED,SAAS,2BAA2B;IAClC,OAAO,OAAO,wCAAwC,KAAK,QAAQ;QACjE,CAAC,CAAC,wCAAwC;QAC1C,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED,SAAS,kBAAkB;IACzB,OAAO,CACL,sBAAsB,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;QACrD,sBAAsB,CAAC,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC;QACxE,sBAAsB,CAAC,2BAA2B,EAAE,CAAC,CACtD,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,2BAA2B;IACzC,MAAM,EAAE,GAAG,kBAAkB,EAAE,CAAC;IAChC,IAAI,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IACrB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IAChC,OAAO,CACL,wCAAwC;QACxC,6CAA6C;QAC7C,wBAAwB;QACxB,iBAAiB,IAAI,IAAI;QACzB,iFAAiF;QACjF,2CAA2C;QAC3C,0BAA0B;QAC1B,GAAG,CACJ,CAAC;AACJ,CAAC;AAED,SAAS,WAAW;IAClB,MAAM,EAAE,GAAG,kBAAkB,EAAE,CAAC;IAChC,IAAI,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IACrB,MAAM,KAAK,GAAG,kBAAkB,CAAC,EAAE,CAAC,CAAC;IACrC,MAAM,UAAU,GAAG,2BAA2B,EAAE,CAAC;IACjD,OAAO,CACL,kEAAkE,KAAK,aAAa;QACpF,WAAW,UAAU,WAAW,CACjC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAoB;IACpD,MAAM,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzD,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE1B,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,OAAO,IAAI,CAAC,WAAW,CACrB,IAAI,eAAe,CAAC;QAClB,SAAS,CAAC,KAAK,EAAE,UAAU;YACzB,IAAI,QAAQ,EAAE,CAAC;gBACb,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC1B,OAAO;YACT,CAAC;YACD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YACrD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAC7C,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;gBACxB,MAAM,QAAQ,GACZ,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;gBACnE,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAC7C,QAAQ,GAAG,IAAI,CAAC;YAClB,CAAC;iBAAM,CAAC;gBACN,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;KACF,CAAC,CACH,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Opt-in analytics injection for SSR streams.\n * Supported environment variables:\n * - `GA_MEASUREMENT_ID` — Google Analytics 4 measurement ID\n *\n * Netlify configuration-file env vars are build-time only for serverless\n * functions, so the Vite/Nitro build paths also bake this public value into\n * SSR bundles.\n *\n * Amplitude and Sentry are initialized client-side via their npm packages\n * (see `packages/core/src/client/analytics.ts`). Only GA requires script\n * tag injection because the gtag.js loader must be a `<script src>`.\n *\n * When set, the corresponding script tags are injected before `</head>`.\n * When not set, the stream passes through untouched (zero overhead).\n *\n * Usage in entry.server.tsx:\n * ```ts\n * import { wrapWithAnalytics } from \"@agent-native/core/server\";\n * return new Response(wrapWithAnalytics(body), { ... });\n * ```\n */\n\ndeclare const __AGENT_NATIVE_BUILD_GA_MEASUREMENT_ID__: string | undefined;\n\nfunction normalizeMeasurementId(value: string | undefined): string | null {\n const trimmed = value?.trim();\n return trimmed ? trimmed : null;\n}\n\nfunction getViteBakedGaMeasurementId(): string | undefined {\n return typeof __AGENT_NATIVE_BUILD_GA_MEASUREMENT_ID__ === \"string\"\n ? __AGENT_NATIVE_BUILD_GA_MEASUREMENT_ID__\n : undefined;\n}\n\nfunction getGaMeasurementId(): string | null {\n return (\n normalizeMeasurementId(process.env.GA_MEASUREMENT_ID) ||\n normalizeMeasurementId(process.env.AGENT_NATIVE_BUILD_GA_MEASUREMENT_ID) ||\n normalizeMeasurementId(getViteBakedGaMeasurementId())\n );\n}\n\n/**\n * The exact JS body (no surrounding `<script>` tags) of the inline gtag config\n * block injected next to the gtag.js loader.\n * Returns `null` when GA is not configured.\n */\nexport function getGaInlineConfigScriptBody(): string | null {\n const id = getGaMeasurementId();\n if (!id) return null;\n const jsId = JSON.stringify(id);\n return (\n `window.dataLayer=window.dataLayer||[];` +\n `function gtag(){dataLayer.push(arguments);}` +\n `gtag('js',new Date());` +\n `gtag('config',${jsId});` +\n `if(typeof sessionStorage!=='undefined'&&sessionStorage.getItem('__an_signin')){` +\n `sessionStorage.removeItem('__an_signin');` +\n `gtag('event','sign_in');` +\n `}`\n );\n}\n\nfunction getGaScript(): string | null {\n const id = getGaMeasurementId();\n if (!id) return null;\n const srcId = encodeURIComponent(id);\n const inlineBody = getGaInlineConfigScriptBody();\n return (\n `<script async src=\"https://www.googletagmanager.com/gtag/js?id=${srcId}\"></script>` +\n `<script>${inlineBody}</script>`\n );\n}\n\nexport function wrapWithAnalytics(body: ReadableStream): ReadableStream {\n const scripts = [getGaScript()].filter(Boolean).join(\"\");\n if (!scripts) return body;\n\n const decoder = new TextDecoder();\n const encoder = new TextEncoder();\n let injected = false;\n\n return body.pipeThrough(\n new TransformStream({\n transform(chunk, controller) {\n if (injected) {\n controller.enqueue(chunk);\n return;\n }\n const text = decoder.decode(chunk, { stream: true });\n const headCloseIdx = text.indexOf(\"</head>\");\n if (headCloseIdx !== -1) {\n const modified =\n text.slice(0, headCloseIdx) + scripts + text.slice(headCloseIdx);\n controller.enqueue(encoder.encode(modified));\n injected = true;\n } else {\n controller.enqueue(chunk);\n }\n },\n }),\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-handler.d.ts","sourceRoot":"","sources":["../../src/server/ssr-handler.ts"],"names":[],"mappings":"AA8CA,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AAooBpC;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,2FAgE5E"}
1
+ {"version":3,"file":"ssr-handler.d.ts","sourceRoot":"","sources":["../../src/server/ssr-handler.ts"],"names":[],"mappings":"AAuCA,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AAoSpC;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,2FAgE5E"}
@@ -19,10 +19,8 @@ import { defineEventHandler } from "h3";
19
19
  import { createRequestHandler } from "react-router";
20
20
  import { DEFAULT_SSR_CACHE_HEADERS, DEFAULT_SPECULATION_RULES_PATH, } from "../shared/cache-control.js";
21
21
  import { AGENT_NATIVE_SOCIAL_IMAGE_ALT, AGENT_NATIVE_SOCIAL_IMAGE_HEIGHT, AGENT_NATIVE_SOCIAL_IMAGE_PATH, AGENT_NATIVE_SOCIAL_IMAGE_TYPE, AGENT_NATIVE_SOCIAL_IMAGE_WIDTH, withAgentNativeSocialImageCacheBuster, } from "../shared/social-meta.js";
22
- import { GA_CSP_CONNECT_HOSTS, GA_CSP_IMG_HOSTS, GA_CSP_SCRIPT_HOSTS, getGaInlineConfigScriptBody, } from "./analytics.js";
23
22
  import { getAppBasePathFromViteEnv, stripAppBasePath as canonicalStripAppBasePath, } from "./app-base-path.js";
24
23
  import { runWithRequestContext } from "./request-context.js";
25
- import { computeInlineScriptHash } from "./security-headers.js";
26
24
  import { getSentryClientConfigScript } from "./sentry-config.js";
27
25
  export { DEFAULT_SSR_CACHE_HEADERS, DEFAULT_SPECULATION_RULES_HEADER, DEFAULT_SSR_CACHE_CONTROL, } from "../shared/cache-control.js";
28
26
  function getAppBasePath() {
@@ -192,275 +190,17 @@ function applyDefaultSpeculationRulesHeader(headers, status, basePath) {
192
190
  headers.set("speculation-rules", `"${rulesPath}"`);
193
191
  }
194
192
  /**
195
- * Extract the plain JS body from a `<script ...>body</script>` string.
196
- * Returns `null` if the input is falsy or has no recognisable `</script>` end.
197
- * Used to compute the sha256 hash of framework-injected inline scripts so the
198
- * hash can be listed in app-owned `script-src` CSP directives.
199
- */
200
- function extractScriptBody(scriptTag) {
201
- if (!scriptTag)
202
- return null;
203
- const start = scriptTag.indexOf(">") + 1;
204
- const end = scriptTag.lastIndexOf("</script>");
205
- if (start <= 0 || end < start)
206
- return null;
207
- return scriptTag.slice(start, end);
208
- }
209
- const CSP_DIRECTIVES_WITH_VALUE_TOKENS = new Set([
210
- "base-uri",
211
- "block-all-mixed-content",
212
- "child-src",
213
- "connect-src",
214
- "default-src",
215
- "fenced-frame-src",
216
- "font-src",
217
- "form-action",
218
- "frame-ancestors",
219
- "frame-src",
220
- "img-src",
221
- "manifest-src",
222
- "media-src",
223
- "navigate-to",
224
- "object-src",
225
- "plugin-types",
226
- "prefetch-src",
227
- "referrer",
228
- "reflected-xss",
229
- "require-sri-for",
230
- "require-trusted-types-for",
231
- "report-to",
232
- "report-uri",
233
- "sandbox",
234
- "script-src",
235
- "script-src-attr",
236
- "script-src-elem",
237
- "style-src",
238
- "style-src-attr",
239
- "style-src-elem",
240
- "trusted-types",
241
- "upgrade-insecure-requests",
242
- "webrtc",
243
- "worker-src",
244
- ]);
245
- function hasCommaJoinedCspPolicies(policy) {
246
- let commaIndex = policy.indexOf(",");
247
- while (commaIndex !== -1) {
248
- const afterComma = policy.slice(commaIndex + 1);
249
- const directive = /^\s+([a-z][a-z0-9-]*)(?=\s|;|$)/i.exec(afterComma)?.[1];
250
- if (directive &&
251
- CSP_DIRECTIVES_WITH_VALUE_TOKENS.has(directive.toLowerCase())) {
252
- return true;
253
- }
254
- commaIndex = policy.indexOf(",", commaIndex + 1);
255
- }
256
- return false;
257
- }
258
- function parseCsp(policy) {
259
- return policy
260
- .split(";")
261
- .map((part) => part.trim())
262
- .filter(Boolean)
263
- .map((part) => {
264
- const [name = "", ...tokens] = part.split(/\s+/);
265
- return { name: name.toLowerCase(), tokens };
266
- })
267
- .filter((directive) => directive.name);
268
- }
269
- function serializeCsp(directives) {
270
- return directives
271
- .map((directive) => [directive.name, ...directive.tokens].filter(Boolean).join(" "))
272
- .join("; ");
273
- }
274
- function appendCspTokens(tokens, additions) {
275
- if (!additions.length)
276
- return tokens;
277
- const next = tokens.filter((token) => token !== "'none'");
278
- const seen = new Set(next);
279
- for (const token of additions) {
280
- if (!token || seen.has(token))
281
- continue;
282
- next.push(token);
283
- seen.add(token);
284
- }
285
- return next;
286
- }
287
- function findCspDirective(directives, name) {
288
- return directives.find((directive) => directive.name === name);
289
- }
290
- function appendToExistingOrDefaultCspDirective(directives, name, additions) {
291
- if (!additions.length)
292
- return;
293
- const existing = findCspDirective(directives, name);
294
- if (existing) {
295
- existing.tokens = appendCspTokens(existing.tokens, additions);
296
- return;
297
- }
298
- const defaultSrc = findCspDirective(directives, "default-src");
299
- if (!defaultSrc)
300
- return;
301
- directives.push({
302
- name,
303
- tokens: appendCspTokens([...defaultSrc.tokens], additions),
304
- });
305
- }
306
- function appendToExistingCspDirective(directives, name, additions) {
307
- const existing = findCspDirective(directives, name);
308
- if (!existing)
309
- return;
310
- existing.tokens = appendCspTokens(existing.tokens, additions);
311
- }
312
- function ensureCspDirective(directives, name, tokens) {
313
- if (findCspDirective(directives, name))
314
- return;
315
- directives.push({ name, tokens: [...tokens] });
316
- }
317
- function hasStrictNonceScriptPolicy(tokens) {
318
- return tokens.some((token) => token === "'strict-dynamic'" || token.startsWith("'nonce-"));
319
- }
320
- function appendToScriptCspDirective(directives, name, additions) {
321
- const existing = findCspDirective(directives, name);
322
- if (existing) {
323
- if (hasStrictNonceScriptPolicy(existing.tokens))
324
- return false;
325
- existing.tokens = appendCspTokens(existing.tokens, additions);
326
- return true;
327
- }
328
- const defaultSrc = findCspDirective(directives, "default-src");
329
- if (!defaultSrc || hasStrictNonceScriptPolicy(defaultSrc.tokens)) {
330
- return false;
331
- }
332
- directives.push({
333
- name,
334
- tokens: appendCspTokens([...defaultSrc.tokens], additions),
335
- });
336
- return true;
337
- }
338
- function appendToEffectiveScriptElementCspDirective(directives, additions) {
339
- const scriptSrcElem = findCspDirective(directives, "script-src-elem");
340
- if (scriptSrcElem) {
341
- if (hasStrictNonceScriptPolicy(scriptSrcElem.tokens))
342
- return false;
343
- scriptSrcElem.tokens = appendCspTokens(scriptSrcElem.tokens, additions);
344
- return true;
345
- }
346
- return appendToScriptCspDirective(directives, "script-src", additions);
347
- }
348
- function augmentExistingEnforcedCspForFrameworkScripts(policy, options) {
349
- // Multiple CSP headers are surfaced by Headers.get() as one comma-joined
350
- // string. CSP is not a comma-list header, so serializing a parsed combined
351
- // value would turn two policies into one invalid policy. Leave those headers
352
- // app-owned; a comma inside a source/report URL is still safe to parse.
353
- if (hasCommaJoinedCspPolicies(policy))
354
- return policy;
355
- const directives = parseCsp(policy);
356
- if (!directives.length)
357
- return policy;
358
- if (options.gaEnabled) {
359
- const addedScriptElement = appendToEffectiveScriptElementCspDirective(directives, options.gaScriptSrcTokens);
360
- if (addedScriptElement) {
361
- appendToExistingOrDefaultCspDirective(directives, "connect-src", GA_CSP_CONNECT_HOSTS);
362
- appendToExistingOrDefaultCspDirective(directives, "img-src", GA_CSP_IMG_HOSTS);
363
- }
364
- }
365
- ensureCspDirective(directives, "object-src", ["'none'"]);
366
- ensureCspDirective(directives, "base-uri", ["'self'"]);
367
- return serializeCsp(directives);
368
- }
369
- function augmentExistingReportOnlyCspForFrameworkScripts(policy, options) {
370
- if (hasCommaJoinedCspPolicies(policy))
371
- return policy;
372
- const directives = parseCsp(policy);
373
- if (!directives.length)
374
- return policy;
375
- appendToExistingOrDefaultCspDirective(directives, "script-src", options.scriptSrcTokens);
376
- appendToExistingCspDirective(directives, "script-src-elem", options.scriptSrcTokens);
377
- if (options.gaEnabled) {
378
- appendToExistingOrDefaultCspDirective(directives, "connect-src", GA_CSP_CONNECT_HOSTS);
379
- appendToExistingOrDefaultCspDirective(directives, "img-src", GA_CSP_IMG_HOSTS);
380
- }
381
- return serializeCsp(directives);
382
- }
383
- /**
384
- * Apply a Content-Security-Policy header to HTML document responses.
385
- *
386
- * Two directives are always enforced in production:
387
- *
388
- * - `object-src 'none'` — disables Flash / Java / PDF plugin execution,
389
- * which are a reliable code-execution vector even in modern browsers.
390
- * - `base-uri 'self'` — prevents a `<base href="...">` injection from
391
- * hijacking all relative URLs in the document (a common attack target when
392
- * user-controlled content reaches the HTML).
393
- *
394
- * A third directive, `script-src`, is emitted via `Content-Security-Policy-
395
- * Report-Only` rather than enforced when the app has no existing document CSP.
396
- * The framework injects inline scripts for analytics, Sentry, and template
397
- * setup, and hosted apps need Google Tag Manager to load without noisy CSP
398
- * diagnostics. The report-only policy is intentionally permissive for scripts:
399
- * it includes `'unsafe-inline'` plus the known GA/GTM loader hosts.
400
- *
401
- * If an app or host already sends an enforced CSP with `script-src`,
402
- * `script-src-elem`, `connect-src`, `img-src`, or `default-src`, we merge only
403
- * GA-specific allowances into existing host/hash policies. Strict nonce or
404
- * `strict-dynamic` script policies stay app-owned because blindly appending
405
- * hashes or hosts would widen the policy without reliably loading our injected
406
- * scripts.
407
- *
408
- * Templates additionally render a theme-init inline script whose exact content
409
- * varies by template (default theme param, custom docs variant, etc.) and which
410
- * is rendered by React Router, not this handler, so its hash is not available
411
- * here. Shipping script-src as Report-Only surfaces the remaining violations
412
- * without breaking template customisations; teams can graduate to enforcement
413
- * once their hashes are enumerated.
193
+ * Strip document-level CSP from app HTML responses.
414
194
  *
415
- * Skipped in development (`NODE_ENV !== 'production'`) so HMR eval and Vite
416
- * dev-server injects are never blocked. Set `AGENT_NATIVE_DISABLE_DOC_CSP=1`
417
- * to opt out in production for a template with exotic needs.
195
+ * Hosted templates inject framework bootstrap scripts, analytics, Sentry config,
196
+ * and app-owned inline scripts whose exact bytes vary by build/template. Any
197
+ * shared CSP header, even Report-Only, can block or noisily report Google Tag
198
+ * Manager and those bootstraps. Extension iframes and webviews keep their own
199
+ * route-specific sandboxes; normal app documents deliberately do not emit CSP.
418
200
  */
419
- function applyDocumentCsp(headers, sentryScript) {
420
- if (process.env.NODE_ENV !== "production")
421
- return;
422
- if (process.env.AGENT_NATIVE_DISABLE_DOC_CSP === "1")
423
- return;
424
- // script-src as Report-Only: keep this deliberately loose so the framework's
425
- // injected analytics and template bootstrap scripts do not look blocked in
426
- // browser diagnostics.
427
- const sentryBody = extractScriptBody(sentryScript);
428
- const sentryHash = sentryBody ? computeInlineScriptHash(sentryBody) : null;
429
- const gaInlineBody = getGaInlineConfigScriptBody();
430
- const gaHash = gaInlineBody ? computeInlineScriptHash(gaInlineBody) : null;
431
- const gaHosts = gaInlineBody ? [...GA_CSP_SCRIPT_HOSTS] : [];
432
- const gaScriptSrcTokens = [
433
- "'unsafe-inline'",
434
- ...(gaHash ? [gaHash] : []),
435
- ...gaHosts,
436
- ];
437
- const scriptSrcTokens = [
438
- "'self'",
439
- "'unsafe-inline'",
440
- ...(sentryHash ? [sentryHash] : []),
441
- ...(gaHash ? [gaHash] : []),
442
- ...gaHosts,
443
- ];
444
- const cspAugmentOptions = {
445
- scriptSrcTokens,
446
- gaScriptSrcTokens,
447
- gaEnabled: Boolean(gaInlineBody),
448
- };
449
- const existing = headers.get("content-security-policy") ?? "";
450
- if (!existing) {
451
- headers.set("content-security-policy", "object-src 'none'; base-uri 'self'");
452
- }
453
- else {
454
- headers.set("content-security-policy", augmentExistingEnforcedCspForFrameworkScripts(existing, cspAugmentOptions));
455
- }
456
- const scriptSrc = `script-src ${scriptSrcTokens.join(" ")}`;
457
- const existingRo = headers.get("content-security-policy-report-only") ?? "";
458
- if (!existingRo) {
459
- headers.set("content-security-policy-report-only", scriptSrc);
460
- }
461
- else {
462
- headers.set("content-security-policy-report-only", augmentExistingReportOnlyCspForFrameworkScripts(existingRo, cspAugmentOptions));
463
- }
201
+ function removeDocumentCsp(headers) {
202
+ headers.delete("content-security-policy");
203
+ headers.delete("content-security-policy-report-only");
464
204
  }
465
205
  function isFrameworkOrAssetPath(pathname) {
466
206
  return (pathname.startsWith("/.well-known/") ||
@@ -487,7 +227,15 @@ async function rewriteMountedResponse(response, basePath, pathname, requestUrl)
487
227
  headers.set("location", prefixMountedPath(location, basePath));
488
228
  }
489
229
  const contentType = headers.get("content-type") ?? "";
490
- if (!contentType.toLowerCase().includes("text/html") || !response.body) {
230
+ if (!contentType.toLowerCase().includes("text/html")) {
231
+ return new Response(response.body, {
232
+ status: response.status,
233
+ statusText: response.statusText,
234
+ headers,
235
+ });
236
+ }
237
+ removeDocumentCsp(headers);
238
+ if (!response.body) {
491
239
  return new Response(response.body, {
492
240
  status: response.status,
493
241
  statusText: response.statusText,
@@ -496,7 +244,6 @@ async function rewriteMountedResponse(response, basePath, pathname, requestUrl)
496
244
  }
497
245
  const html = await response.text();
498
246
  headers.delete("content-length");
499
- applyDocumentCsp(headers, sentryClientConfigScript);
500
247
  return new Response(injectHeadScript(injectDefaultSocialImageMeta(prefixMountedHtml(html, basePath), defaultSocialImageUrl(requestUrl, basePath)), sentryClientConfigScript), {
501
248
  status: response.status,
502
249
  statusText: response.statusText,