@mandujs/core 0.21.0 → 0.22.1
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/package.json +101 -69
- package/src/auth/__tests__/login.test.ts +419 -0
- package/src/auth/__tests__/password.test.ts +122 -0
- package/src/auth/__tests__/reset.test.ts +296 -0
- package/src/auth/__tests__/tokens.test.ts +274 -0
- package/src/auth/__tests__/verification.test.ts +274 -0
- package/src/auth/index.ts +76 -0
- package/src/auth/login.ts +225 -0
- package/src/auth/password.ts +120 -0
- package/src/auth/reset.ts +243 -0
- package/src/auth/tokens.ts +612 -0
- package/src/auth/verification.ts +253 -0
- package/src/bundler/__tests__/cli-bench-utils.test.ts +149 -0
- package/src/bundler/__tests__/cold-start.test.ts +504 -0
- package/src/bundler/__tests__/csp-nonce.test.ts +278 -0
- package/src/bundler/__tests__/dev-reliability.test.ts +619 -0
- package/src/bundler/__tests__/extended-watch.test.ts +710 -0
- package/src/bundler/__tests__/fast-refresh.test.ts +596 -0
- package/src/bundler/__tests__/hdr.test.ts +353 -0
- package/src/bundler/__tests__/hmr-client.test.ts +532 -0
- package/src/bundler/__tests__/manifest-schema.test.ts +266 -0
- package/src/bundler/__tests__/prod-smoke.test.ts +138 -0
- package/src/bundler/__tests__/slot-dispatch.test.ts +573 -0
- package/src/bundler/__tests__/url-cap-and-slot-regex.test.ts +286 -0
- package/src/bundler/__tests__/vendor-cache.test.ts +455 -0
- package/src/bundler/build.test.ts +8 -1
- package/src/bundler/build.ts +310 -18
- package/src/bundler/css.ts +326 -323
- package/src/bundler/dev.ts +1611 -59
- package/src/bundler/fast-refresh-plugin.ts +307 -0
- package/src/bundler/hmr-types.ts +252 -0
- package/src/bundler/manifest-schema.ts +301 -0
- package/src/bundler/safe-build.test.ts +128 -0
- package/src/bundler/safe-build.ts +77 -0
- package/src/bundler/scenario-matrix.ts +229 -0
- package/src/bundler/types.ts +11 -0
- package/src/bundler/vendor-cache-types.ts +130 -0
- package/src/bundler/vendor-cache.ts +526 -0
- package/src/client/router.ts +214 -56
- package/src/db/__tests__/db.test.ts +485 -0
- package/src/db/index.ts +513 -0
- package/src/db/migrations/__tests__/runner.test.ts +661 -0
- package/src/db/migrations/history-table.ts +345 -0
- package/src/db/migrations/lock.ts +269 -0
- package/src/db/migrations/runner.ts +633 -0
- package/src/desktop/__tests__/smoke.test.ts +100 -0
- package/src/desktop/__tests__/window.test.ts +172 -0
- package/src/desktop/__tests__/worker.test.ts +266 -0
- package/src/desktop/index.ts +43 -0
- package/src/desktop/types.ts +158 -0
- package/src/desktop/window.ts +492 -0
- package/src/desktop/worker.ts +180 -0
- package/src/email/__tests__/email.test.ts +355 -0
- package/src/email/index.ts +282 -0
- package/src/email/resend.ts +163 -0
- package/src/email/smtp.ts +64 -0
- package/src/filling/__tests__/session-sqlite.test.ts +454 -0
- package/src/filling/context.ts +72 -78
- package/src/filling/cookie-codec.ts +299 -0
- package/src/filling/deps.ts +25 -1
- package/src/filling/filling.ts +28 -3
- package/src/filling/session-sqlite.ts +617 -0
- package/src/filling/session.ts +265 -216
- package/src/guard/decision-memory.test.ts +52 -22
- package/src/id/__tests__/id.test.ts +120 -0
- package/src/id/index.ts +105 -0
- package/src/kitchen/index.ts +2 -2
- package/src/kitchen/kitchen-handler.ts +86 -0
- package/src/kitchen/stream/activity-sse.ts +2 -1
- package/src/middleware/csrf.ts +328 -0
- package/src/middleware/index.ts +40 -0
- package/src/middleware/oauth/__tests__/oauth.test.ts +574 -0
- package/src/middleware/oauth/index.ts +505 -0
- package/src/middleware/oauth/providers.ts +115 -0
- package/src/middleware/rate-limit/__tests__/rate-limit.test.ts +642 -0
- package/src/middleware/rate-limit/index.ts +522 -0
- package/src/middleware/rate-limit/sqlite-store.ts +382 -0
- package/src/middleware/secure/__tests__/secure.test.ts +360 -0
- package/src/middleware/secure/csp.ts +193 -0
- package/src/middleware/secure/index.ts +417 -0
- package/src/middleware/session.ts +174 -0
- package/src/observability/event-bus.ts +81 -79
- package/src/paths.ts +37 -0
- package/src/perf/hmr-markers.ts +215 -0
- package/src/perf/index.ts +104 -0
- package/src/resource/__tests__/generator.test.ts +603 -2
- package/src/resource/ddl/__tests__/diff.test.ts +639 -0
- package/src/resource/ddl/__tests__/emit.test.ts +799 -0
- package/src/resource/ddl/__tests__/snapshot.test.ts +499 -0
- package/src/resource/ddl/diff.ts +392 -0
- package/src/resource/ddl/emit.ts +548 -0
- package/src/resource/ddl/persistence-types.ts +218 -0
- package/src/resource/ddl/snapshot.ts +447 -0
- package/src/resource/ddl/type-map.ts +223 -0
- package/src/resource/ddl/types.ts +232 -0
- package/src/resource/generator-repo.ts +610 -0
- package/src/resource/generator-schema.ts +476 -0
- package/src/resource/generator.ts +117 -1
- package/src/resource/index.ts +17 -1
- package/src/resource/schema.ts +30 -0
- package/src/router/fs-scanner.ts +3 -0
- package/src/runtime/__tests__/error-boundary-redaction.test.ts +141 -0
- package/src/runtime/__tests__/hdr-client.test.ts +223 -0
- package/src/runtime/__tests__/http-errors.test.ts +117 -0
- package/src/runtime/__tests__/not-found.test.ts +152 -0
- package/src/runtime/boundary.tsx +21 -1
- package/src/runtime/fast-refresh-runtime.ts +322 -0
- package/src/runtime/fast-refresh-types.ts +128 -0
- package/src/runtime/hmr-client.ts +409 -0
- package/src/runtime/http-errors.ts +113 -0
- package/src/runtime/index.ts +6 -0
- package/src/runtime/logger.ts +678 -677
- package/src/runtime/not-found.ts +93 -0
- package/src/runtime/redirect.ts +133 -0
- package/src/runtime/server.ts +518 -20
- package/src/runtime/ssr.ts +340 -10
- package/src/runtime/streaming-ssr.ts +222 -19
- package/src/scheduler/__tests__/scheduler.test.ts +514 -0
- package/src/scheduler/index.ts +343 -0
- package/src/storage/s3/__tests__/s3.test.ts +479 -0
- package/src/storage/s3/index.ts +412 -0
- package/src/testing/index.ts +58 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 7.2 R1 Agent C (H1) — CSP nonce tests for Fast Refresh preamble.
|
|
3
|
+
*
|
|
4
|
+
* Three code paths covered:
|
|
5
|
+
*
|
|
6
|
+
* A — nonce resolver (env opt-out, explicit string, auto-generate, off)
|
|
7
|
+
* B — preamble tag emitter (nonce attribute injection, opt-out)
|
|
8
|
+
* C — response header (renderSSR / renderStreamingResponse emit the
|
|
9
|
+
* right `Content-Security-Policy` value when a nonce is in play)
|
|
10
|
+
*
|
|
11
|
+
* Dependencies: none that touch fs.watch / bun:build — tests use
|
|
12
|
+
* in-memory manifests only, so they run in any environment.
|
|
13
|
+
*
|
|
14
|
+
* References:
|
|
15
|
+
* docs/security/phase-7-1-audit.md §2 M-01
|
|
16
|
+
* docs/bun/phase-7-2-team-plan.md §3 Agent C H1
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { describe, expect, test, afterEach, beforeEach } from "bun:test";
|
|
20
|
+
import React from "react";
|
|
21
|
+
import type { BundleManifest } from "../types";
|
|
22
|
+
import {
|
|
23
|
+
renderSSR,
|
|
24
|
+
renderToHTML,
|
|
25
|
+
_testOnly_buildFastRefreshCspHeader,
|
|
26
|
+
_testOnly_generateCspNonce,
|
|
27
|
+
_testOnly_generateFastRefreshPreambleTag,
|
|
28
|
+
_testOnly_getAttachedCspNonce,
|
|
29
|
+
_testOnly_resolveFastRefreshCspNonce,
|
|
30
|
+
} from "../../runtime/ssr";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Minimal dev-mode manifest carrying the same Fast Refresh URLs the
|
|
34
|
+
* bundler emits. We deliberately keep this a literal so unit tests
|
|
35
|
+
* never invoke `Bun.build` — the plumbing under test is purely string
|
|
36
|
+
* manipulation + header emission.
|
|
37
|
+
*/
|
|
38
|
+
const DEV_MANIFEST: BundleManifest = {
|
|
39
|
+
version: 1,
|
|
40
|
+
buildTime: "2026-04-19T00:00:00.000Z",
|
|
41
|
+
env: "development",
|
|
42
|
+
bundles: {
|
|
43
|
+
page: {
|
|
44
|
+
js: "/.mandu/client/page.js",
|
|
45
|
+
dependencies: [],
|
|
46
|
+
priority: "visible",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
shared: {
|
|
50
|
+
runtime: "/.mandu/client/_runtime.js",
|
|
51
|
+
vendor: "/.mandu/client/_vendor-react.js",
|
|
52
|
+
fastRefresh: {
|
|
53
|
+
runtime: "/.mandu/client/_fast-refresh-runtime.js",
|
|
54
|
+
glue: "/.mandu/client/_vendor-react-refresh.js",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Preserve env across tests so MANDU_CSP_NONCE toggling doesn't leak.
|
|
60
|
+
let prevEnv: string | undefined;
|
|
61
|
+
beforeEach(() => {
|
|
62
|
+
prevEnv = process.env.MANDU_CSP_NONCE;
|
|
63
|
+
});
|
|
64
|
+
afterEach(() => {
|
|
65
|
+
if (prevEnv === undefined) {
|
|
66
|
+
delete process.env.MANDU_CSP_NONCE;
|
|
67
|
+
} else {
|
|
68
|
+
process.env.MANDU_CSP_NONCE = prevEnv;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
73
|
+
// Section A — resolveFastRefreshCspNonce
|
|
74
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
75
|
+
|
|
76
|
+
describe("csp-nonce — resolveFastRefreshCspNonce", () => {
|
|
77
|
+
test("[A1] generates a fresh 128-bit base64 nonce on each call (entropy > 120 unique of 200)", () => {
|
|
78
|
+
const seen = new Set<string>();
|
|
79
|
+
for (let i = 0; i < 200; i++) {
|
|
80
|
+
seen.add(_testOnly_generateCspNonce());
|
|
81
|
+
}
|
|
82
|
+
// 200 draws from 2^128 should produce 200 distinct values; accept
|
|
83
|
+
// ≥120 unique to survive any pathological RNG quirks.
|
|
84
|
+
expect(seen.size).toBeGreaterThan(120);
|
|
85
|
+
const sample = [...seen][0];
|
|
86
|
+
// 16 bytes → 24 base64 chars incl. `=` padding
|
|
87
|
+
expect(sample.length).toBeGreaterThanOrEqual(20);
|
|
88
|
+
expect(sample.length).toBeLessThanOrEqual(32);
|
|
89
|
+
// base64 alphabet only (no slashes / plus are allowed but not typical
|
|
90
|
+
// for nonces; we accept standard base64)
|
|
91
|
+
expect(/^[A-Za-z0-9+/=]+$/.test(sample)).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("[A2] opt-out via MANDU_CSP_NONCE=0 forces undefined even when cspNonce=true", () => {
|
|
95
|
+
process.env.MANDU_CSP_NONCE = "0";
|
|
96
|
+
expect(_testOnly_resolveFastRefreshCspNonce(true)).toBeUndefined();
|
|
97
|
+
expect(_testOnly_resolveFastRefreshCspNonce("fixed")).toBeUndefined();
|
|
98
|
+
expect(_testOnly_resolveFastRefreshCspNonce(false)).toBeUndefined();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("[A3] explicit string value is passed through verbatim", () => {
|
|
102
|
+
delete process.env.MANDU_CSP_NONCE;
|
|
103
|
+
expect(_testOnly_resolveFastRefreshCspNonce("abc123")).toBe("abc123");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("[A4] undefined / false produce undefined (legacy byte-identical path)", () => {
|
|
107
|
+
delete process.env.MANDU_CSP_NONCE;
|
|
108
|
+
expect(_testOnly_resolveFastRefreshCspNonce(undefined)).toBeUndefined();
|
|
109
|
+
expect(_testOnly_resolveFastRefreshCspNonce(false)).toBeUndefined();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
114
|
+
// Section B — generateFastRefreshPreambleTag
|
|
115
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
116
|
+
|
|
117
|
+
describe("csp-nonce — generateFastRefreshPreambleTag", () => {
|
|
118
|
+
test("[B1] injects nonce attribute onto <script> tag when nonce supplied", () => {
|
|
119
|
+
const out = _testOnly_generateFastRefreshPreambleTag(true, DEV_MANIFEST, "xyz456");
|
|
120
|
+
expect(out).toContain('<script nonce="xyz456">');
|
|
121
|
+
// The original preamble body must still be present (sanity)
|
|
122
|
+
expect(out).toContain("React Fast Refresh preamble");
|
|
123
|
+
// No bare `<script>` remains before the nonced one
|
|
124
|
+
const firstOpen = out.indexOf("<script");
|
|
125
|
+
expect(out.slice(firstOpen, firstOpen + 20)).toBe('<script nonce="xyz45');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("[B2] returns empty string in prod (isDev=false) regardless of nonce", () => {
|
|
129
|
+
expect(_testOnly_generateFastRefreshPreambleTag(false, DEV_MANIFEST, "x")).toBe("");
|
|
130
|
+
expect(_testOnly_generateFastRefreshPreambleTag(false, DEV_MANIFEST, undefined)).toBe("");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("[B3] returns empty string when manifest has no fastRefresh block (prod manifest)", () => {
|
|
134
|
+
const prodManifest: BundleManifest = {
|
|
135
|
+
...DEV_MANIFEST,
|
|
136
|
+
env: "production",
|
|
137
|
+
shared: { ...DEV_MANIFEST.shared, fastRefresh: undefined },
|
|
138
|
+
};
|
|
139
|
+
expect(_testOnly_generateFastRefreshPreambleTag(true, prodManifest, "x")).toBe("");
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("[B4] without nonce, emits bare <script> (backward compat)", () => {
|
|
143
|
+
const out = _testOnly_generateFastRefreshPreambleTag(true, DEV_MANIFEST, undefined);
|
|
144
|
+
expect(out).toContain("<script>");
|
|
145
|
+
expect(out).not.toContain("<script nonce");
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("[B5] nonce attribute value is HTML-escaped (defense in depth)", () => {
|
|
149
|
+
// A pathological nonce containing a `"` must not break out of the
|
|
150
|
+
// attribute. `escapeHtmlAttr` turns it into `"`.
|
|
151
|
+
const out = _testOnly_generateFastRefreshPreambleTag(true, DEV_MANIFEST, 'a"b');
|
|
152
|
+
expect(out).toContain("<script nonce=");
|
|
153
|
+
expect(out).not.toContain('nonce="a"b"'); // would break attribute
|
|
154
|
+
// The " entity is present somewhere in the opening tag
|
|
155
|
+
expect(out).toMatch(/<script nonce="a(?:"|")b"/);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
160
|
+
// Section C — buildFastRefreshCspHeader
|
|
161
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
162
|
+
|
|
163
|
+
describe("csp-nonce — buildFastRefreshCspHeader", () => {
|
|
164
|
+
test("[C1] header contains script-src with nonce and strict-dynamic", () => {
|
|
165
|
+
const h = _testOnly_buildFastRefreshCspHeader("abc123");
|
|
166
|
+
expect(h).toBe("script-src 'self' 'nonce-abc123' 'strict-dynamic'");
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("[C2] strips quotes / CR / LF from nonce (defense in depth)", () => {
|
|
170
|
+
const h = _testOnly_buildFastRefreshCspHeader('a"b\nc\rd');
|
|
171
|
+
// All unsafe chars removed — header is a single line
|
|
172
|
+
expect(h).not.toContain("\n");
|
|
173
|
+
expect(h).not.toContain("\r");
|
|
174
|
+
expect(h).not.toContain('"');
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
179
|
+
// Section D — renderSSR response header integration
|
|
180
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
181
|
+
|
|
182
|
+
describe("csp-nonce — renderSSR integration", () => {
|
|
183
|
+
test("[D1] when cspNonce=true, Response includes Content-Security-Policy header with the same nonce used in preamble", () => {
|
|
184
|
+
delete process.env.MANDU_CSP_NONCE;
|
|
185
|
+
const options = {
|
|
186
|
+
isDev: true,
|
|
187
|
+
routeId: "page",
|
|
188
|
+
serverData: {},
|
|
189
|
+
hydration: { strategy: "island" as const, priority: "visible" as const, preload: false },
|
|
190
|
+
bundleManifest: DEV_MANIFEST,
|
|
191
|
+
cspNonce: true,
|
|
192
|
+
};
|
|
193
|
+
const response = renderSSR(React.createElement("div", null, "hi"), options);
|
|
194
|
+
const header = response.headers.get("Content-Security-Policy");
|
|
195
|
+
expect(header).not.toBeNull();
|
|
196
|
+
expect(header).toMatch(/^script-src 'self' 'nonce-[A-Za-z0-9+/=]+' 'strict-dynamic'$/);
|
|
197
|
+
// The nonce in the header must match the one attached to options
|
|
198
|
+
const attachedNonce = _testOnly_getAttachedCspNonce(options);
|
|
199
|
+
expect(attachedNonce).not.toBeUndefined();
|
|
200
|
+
expect(header).toContain(`'nonce-${attachedNonce}'`);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("[D2] when cspNonce=false (default), NO CSP header is emitted — legacy byte-identical", () => {
|
|
204
|
+
const options = {
|
|
205
|
+
isDev: true,
|
|
206
|
+
routeId: "page",
|
|
207
|
+
serverData: {},
|
|
208
|
+
hydration: { strategy: "island" as const, priority: "visible" as const, preload: false },
|
|
209
|
+
bundleManifest: DEV_MANIFEST,
|
|
210
|
+
};
|
|
211
|
+
const response = renderSSR(React.createElement("div", null, "hi"), options);
|
|
212
|
+
expect(response.headers.get("Content-Security-Policy")).toBeNull();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("[D3] MANDU_CSP_NONCE=0 forces no header even with cspNonce=true", () => {
|
|
216
|
+
process.env.MANDU_CSP_NONCE = "0";
|
|
217
|
+
const options = {
|
|
218
|
+
isDev: true,
|
|
219
|
+
routeId: "page",
|
|
220
|
+
serverData: {},
|
|
221
|
+
hydration: { strategy: "island" as const, priority: "visible" as const, preload: false },
|
|
222
|
+
bundleManifest: DEV_MANIFEST,
|
|
223
|
+
cspNonce: true,
|
|
224
|
+
};
|
|
225
|
+
const response = renderSSR(React.createElement("div", null, "hi"), options);
|
|
226
|
+
expect(response.headers.get("Content-Security-Policy")).toBeNull();
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("[D4] when cspNonce is a caller-supplied string, that exact string ends up in header AND preamble", () => {
|
|
230
|
+
delete process.env.MANDU_CSP_NONCE;
|
|
231
|
+
const options = {
|
|
232
|
+
isDev: true,
|
|
233
|
+
routeId: "page",
|
|
234
|
+
serverData: {},
|
|
235
|
+
hydration: { strategy: "island" as const, priority: "visible" as const, preload: false },
|
|
236
|
+
bundleManifest: DEV_MANIFEST,
|
|
237
|
+
cspNonce: "fixed-nonce-value",
|
|
238
|
+
};
|
|
239
|
+
const response = renderSSR(React.createElement("div", null, "hi"), options);
|
|
240
|
+
const header = response.headers.get("Content-Security-Policy");
|
|
241
|
+
expect(header).toBe("script-src 'self' 'nonce-fixed-nonce-value' 'strict-dynamic'");
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("[D5] renderToHTML embeds nonce into the preamble script tag end-to-end", () => {
|
|
245
|
+
delete process.env.MANDU_CSP_NONCE;
|
|
246
|
+
const options = {
|
|
247
|
+
isDev: true,
|
|
248
|
+
routeId: "page",
|
|
249
|
+
serverData: {},
|
|
250
|
+
hydration: { strategy: "island" as const, priority: "visible" as const, preload: false },
|
|
251
|
+
bundleManifest: DEV_MANIFEST,
|
|
252
|
+
cspNonce: "e2e-token",
|
|
253
|
+
};
|
|
254
|
+
const html = renderToHTML(React.createElement("div", null, "hi"), options);
|
|
255
|
+
expect(html).toContain('<script nonce="e2e-token">');
|
|
256
|
+
// The preamble URLs must still be present (we didn't mangle the body)
|
|
257
|
+
expect(html).toContain("/.mandu/client/_vendor-react-refresh.js");
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("[D6] prod manifest (no shared.fastRefresh) produces no CSP header even with cspNonce=true", () => {
|
|
261
|
+
delete process.env.MANDU_CSP_NONCE;
|
|
262
|
+
const prodManifest: BundleManifest = {
|
|
263
|
+
...DEV_MANIFEST,
|
|
264
|
+
env: "production",
|
|
265
|
+
shared: { ...DEV_MANIFEST.shared, fastRefresh: undefined },
|
|
266
|
+
};
|
|
267
|
+
const options = {
|
|
268
|
+
isDev: false,
|
|
269
|
+
routeId: "page",
|
|
270
|
+
serverData: {},
|
|
271
|
+
hydration: { strategy: "island" as const, priority: "visible" as const, preload: false },
|
|
272
|
+
bundleManifest: prodManifest,
|
|
273
|
+
cspNonce: true,
|
|
274
|
+
};
|
|
275
|
+
const response = renderSSR(React.createElement("div", null, "hi"), options);
|
|
276
|
+
expect(response.headers.get("Content-Security-Policy")).toBeNull();
|
|
277
|
+
});
|
|
278
|
+
});
|