@appwarden/middleware 3.11.5 → 3.12.0

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
@@ -4,7 +4,7 @@
4
4
  [![GitHub](https://img.shields.io/badge/GitHub-appwarden%2Fmiddleware-181717?logo=github&logoColor=white)](https://github.com/appwarden/middleware)
5
5
  [![npm version](https://img.shields.io/npm/v/@appwarden/middleware.svg)](https://www.npmjs.com/package/@appwarden/middleware)
6
6
  [![npm provenance](https://img.shields.io/badge/npm-provenance-green)](https://docs.npmjs.com/generating-provenance-statements)
7
- ![Test Coverage](https://img.shields.io/badge/coverage-93.08%25-brightgreen)
7
+ ![Test Coverage](https://img.shields.io/badge/coverage-95.28%25-brightgreen)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
9
9
 
10
10
  ## Core Features
@@ -1,11 +1,9 @@
1
- import {
2
- UseCSPInputSchema
3
- } from "./chunk-ZTVJBORU.js";
4
1
  import {
5
2
  AppwardenApiHostnameSchema,
6
3
  AppwardenApiTokenSchema,
7
- BooleanSchema
8
- } from "./chunk-WEM7GS4M.js";
4
+ BooleanSchema,
5
+ UseCSPInputSchema
6
+ } from "./chunk-Z7P4QVEY.js";
9
7
 
10
8
  // src/schemas/use-appwarden.ts
11
9
  import { z } from "zod";
@@ -1,6 +1,13 @@
1
1
  import {
2
+ MemoryCache,
3
+ debug,
4
+ printMessage
5
+ } from "./chunk-HTSD4WPC.js";
6
+ import {
7
+ APPWARDEN_CACHE_KEY,
8
+ APPWARDEN_TEST_ROUTE,
2
9
  LockValue
3
- } from "./chunk-WEM7GS4M.js";
10
+ } from "./chunk-Z7P4QVEY.js";
4
11
 
5
12
  // src/utils/cloudflare/cloudflare-cache.ts
6
13
  var store = {
@@ -38,51 +45,6 @@ var updateCacheValue = async (context, cacheKey, value, ttl) => {
38
45
  };
39
46
  var clearCache = (context, cacheKey) => context.cache.delete(cacheKey);
40
47
 
41
- // src/utils/cloudflare/csp-keywords.ts
42
- var CSP_KEYWORDS = [
43
- "self",
44
- "none",
45
- "unsafe-inline",
46
- "unsafe-eval",
47
- "unsafe-hashes",
48
- "strict-dynamic",
49
- "report-sample",
50
- "unsafe-allow-redirects",
51
- "wasm-unsafe-eval",
52
- "trusted-types-eval",
53
- "report-sha256",
54
- "report-sha384",
55
- "report-sha512",
56
- "unsafe-webtransport-hashes"
57
- ];
58
- var CSP_KEYWORDS_SET = new Set(CSP_KEYWORDS);
59
- var isCSPKeyword = (value) => {
60
- return CSP_KEYWORDS_SET.has(value.toLowerCase());
61
- };
62
- var isQuoted = (value) => {
63
- return value.startsWith("'") && value.endsWith("'");
64
- };
65
- var autoQuoteCSPKeyword = (value) => {
66
- const trimmed = value.trim();
67
- if (isQuoted(trimmed)) {
68
- return trimmed;
69
- }
70
- if (isCSPKeyword(trimmed)) {
71
- return `'${trimmed}'`;
72
- }
73
- return trimmed;
74
- };
75
- var autoQuoteCSPDirectiveValue = (value) => {
76
- return value.trim().split(/\s+/).filter(Boolean).map(autoQuoteCSPKeyword).join(" ");
77
- };
78
- var autoQuoteCSPDirectiveArray = (values) => {
79
- return values.map(autoQuoteCSPKeyword);
80
- };
81
-
82
- // src/utils/print-message.ts
83
- var addSlashes = (str) => str.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$").replace(/"/g, '\\"').replace(/'/g, "\\'").replace(/\u0000/g, "\\0").replace(/<\/script>/gi, "<\\/script>");
84
- var printMessage = (message) => `[@appwarden/middleware] ${addSlashes(message)}`;
85
-
86
48
  // src/utils/cloudflare/delete-edge-value.ts
87
49
  var deleteEdgeValue = async (context) => {
88
50
  try {
@@ -105,39 +67,6 @@ var deleteEdgeValue = async (context) => {
105
67
  }
106
68
  };
107
69
 
108
- // src/utils/errors.ts
109
- var errorsMap = {
110
- mode: '`CSP_MODE` must be one of "disabled", "report-only", or "enforced"',
111
- directives: {
112
- ["DirectivesRequired" /* DirectivesRequired */]: '`CSP_DIRECTIVES` must be provided when `CSP_MODE` is "report-only" or "enforced"',
113
- ["DirectivesBadParse" /* DirectivesBadParse */]: "Failed to parse `CSP_DIRECTIVES`. Is it a valid JSON string?"
114
- },
115
- appwardenApiToken: "Please provide a valid `appwardenApiToken`. Learn more at https://appwarden.com/docs/guides/api-token-management."
116
- };
117
- var getErrors = (error) => {
118
- const matches = [];
119
- const errors = [...Object.entries(error.flatten().fieldErrors)];
120
- for (const issue of error.issues) {
121
- errors.push(
122
- ...Object.entries(
123
- "returnTypeError" in issue ? issue.returnTypeError.flatten().fieldErrors : {}
124
- )
125
- );
126
- }
127
- for (const [field, maybeSchemaErrorKey] of errors) {
128
- let match = errorsMap[field];
129
- if (match) {
130
- if (match instanceof Object) {
131
- if (maybeSchemaErrorKey) {
132
- match = match[maybeSchemaErrorKey[0]];
133
- }
134
- }
135
- matches.push(match);
136
- }
137
- }
138
- return matches;
139
- };
140
-
141
70
  // src/utils/cloudflare/get-lock-value.ts
142
71
  var getLockValue = async (context) => {
143
72
  try {
@@ -181,56 +110,6 @@ var getLockValue = async (context) => {
181
110
  }
182
111
  };
183
112
 
184
- // src/utils/cloudflare/insert-errors-logs.ts
185
- var insertErrorLogs = async (context, error) => {
186
- const errors = getErrors(error);
187
- for (const err of errors) {
188
- console.log(printMessage(err));
189
- }
190
- return new HTMLRewriter().on("body", {
191
- element: (elem) => {
192
- elem.append(
193
- `<script>
194
- ${errors.map((err) => `console.error(\`${printMessage(err)}\`)`).join("\n")}
195
- </script>`,
196
- { html: true }
197
- );
198
- }
199
- }).transform(await fetch(context.request.clone()));
200
- };
201
-
202
- // src/utils/cloudflare/make-csp-header.ts
203
- var addNonce = (value, cspNonce) => value.replace("{{nonce}}", `'nonce-${cspNonce}'`);
204
- var makeCSPHeader = (cspNonce, directives, mode) => {
205
- const namesSeen = /* @__PURE__ */ new Set(), result = [];
206
- Object.entries(directives ?? {}).forEach(([originalName, value]) => {
207
- const name = originalName.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
208
- if (namesSeen.has(name)) {
209
- throw new Error(`${originalName} is specified more than once`);
210
- }
211
- namesSeen.add(name);
212
- let directiveValue;
213
- if (Array.isArray(value)) {
214
- directiveValue = autoQuoteCSPDirectiveArray(value).join(" ");
215
- } else if (value === true) {
216
- directiveValue = "";
217
- } else if (typeof value === "string") {
218
- directiveValue = autoQuoteCSPDirectiveValue(value);
219
- } else {
220
- return;
221
- }
222
- if (directiveValue) {
223
- result.push(`${name} ${addNonce(directiveValue, cspNonce)}`);
224
- } else {
225
- result.push(name);
226
- }
227
- });
228
- return [
229
- mode === "enforced" ? "Content-Security-Policy" : "Content-Security-Policy-Report-Only",
230
- result.join("; ")
231
- ];
232
- };
233
-
234
113
  // src/utils/cloudflare/sync-edge-value.ts
235
114
  var APIError = class extends Error {
236
115
  constructor(message) {
@@ -287,19 +166,72 @@ var syncEdgeValue = async (context) => {
287
166
  }
288
167
  };
289
168
 
169
+ // src/core/check-lock-status.ts
170
+ var createContext = async (config) => {
171
+ const requestUrl = new URL(config.request.url);
172
+ const keyName = APPWARDEN_CACHE_KEY;
173
+ const provider = "cloudflare-cache";
174
+ const debugFn = debug(config.debug ?? false);
175
+ const edgeCache = store.json(
176
+ {
177
+ serviceOrigin: requestUrl.origin,
178
+ cache: await caches.open("appwarden:lock"),
179
+ debug: debugFn
180
+ },
181
+ keyName
182
+ );
183
+ return {
184
+ keyName,
185
+ request: config.request,
186
+ edgeCache,
187
+ requestUrl,
188
+ provider,
189
+ debug: debugFn,
190
+ lockPageSlug: config.lockPageSlug,
191
+ appwardenApiToken: config.appwardenApiToken,
192
+ appwardenApiHostname: config.appwardenApiHostname,
193
+ waitUntil: config.waitUntil
194
+ };
195
+ };
196
+ var resolveLockStatus = async (context) => {
197
+ const { lockValue, shouldDeleteEdgeValue } = await getLockValue(context);
198
+ if (shouldDeleteEdgeValue) {
199
+ context.debug("Deleting corrupted cache value");
200
+ await deleteEdgeValue(context);
201
+ }
202
+ const isTestRoute = context.requestUrl.pathname === APPWARDEN_TEST_ROUTE;
203
+ const isTestLock = isTestRoute && !MemoryCache.isTestExpired(lockValue) && !!lockValue;
204
+ return {
205
+ isLocked: !!lockValue?.isLocked || isTestLock,
206
+ isTestLock,
207
+ lockValue,
208
+ wasDeleted: shouldDeleteEdgeValue ?? false
209
+ };
210
+ };
211
+ var checkLockStatus = async (config) => {
212
+ const context = await createContext(config);
213
+ let { isLocked, isTestLock, lockValue, wasDeleted } = await resolveLockStatus(context);
214
+ const isExpired = MemoryCache.isExpired(lockValue);
215
+ if (!isExpired && !wasDeleted && lockValue) {
216
+ context.debug("Lock value resolved from cache");
217
+ }
218
+ if (isExpired || wasDeleted) {
219
+ if (!lockValue || wasDeleted || lockValue.isLocked) {
220
+ context.debug(
221
+ "No fresh cached lock status available - syncing with API synchronously"
222
+ );
223
+ await syncEdgeValue(context);
224
+ ({ isLocked, isTestLock } = await resolveLockStatus(context));
225
+ } else {
226
+ context.debug(
227
+ "Cached lock status expired but last known state unlocked - syncing with API in background"
228
+ );
229
+ config.waitUntil(syncEdgeValue(context));
230
+ }
231
+ }
232
+ return { isLocked, isTestLock };
233
+ };
234
+
290
235
  export {
291
- printMessage,
292
- getErrors,
293
- store,
294
- CSP_KEYWORDS,
295
- isCSPKeyword,
296
- isQuoted,
297
- autoQuoteCSPKeyword,
298
- autoQuoteCSPDirectiveValue,
299
- autoQuoteCSPDirectiveArray,
300
- deleteEdgeValue,
301
- getLockValue,
302
- insertErrorLogs,
303
- makeCSPHeader,
304
- syncEdgeValue
236
+ checkLockStatus
305
237
  };
@@ -0,0 +1,11 @@
1
+ // src/utils/get-now.ts
2
+ var getNowMs = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
3
+ var getElapsedMs = (startTime) => Math.round(getNowMs() - startTime);
4
+ var logElapsed = (debug, startTime) => {
5
+ debug(`Middleware executed in ${getElapsedMs(startTime)}ms`);
6
+ };
7
+
8
+ export {
9
+ getNowMs,
10
+ logElapsed
11
+ };
@@ -0,0 +1,378 @@
1
+ import {
2
+ APPWARDEN_HEARTBEAT_ROUTE,
3
+ HEARTBEAT_CONFIG_ERROR_MAX_CODE_LENGTH,
4
+ HEARTBEAT_CONFIG_ERROR_MAX_COUNT,
5
+ HEARTBEAT_CONFIG_ERROR_MAX_MESSAGE_LENGTH,
6
+ HEARTBEAT_CONFIG_ERROR_MAX_PATH_DEPTH,
7
+ HEARTBEAT_CONFIG_ERROR_MAX_PATH_SEGMENT_LENGTH,
8
+ HEARTBEAT_CONTRACT_VERSION,
9
+ LOCKDOWN_TEST_EXPIRY_MS
10
+ } from "./chunk-Z7P4QVEY.js";
11
+
12
+ // src/utils/build-lock-page-url.ts
13
+ function normalizeLockPageSlug(lockPageSlug) {
14
+ return lockPageSlug.startsWith("/") ? lockPageSlug : `/${lockPageSlug}`;
15
+ }
16
+ function buildLockPageUrl(lockPageSlug, requestUrl) {
17
+ const normalizedSlug = normalizeLockPageSlug(lockPageSlug);
18
+ return new URL(normalizedSlug, requestUrl);
19
+ }
20
+ function normalizeTrailingSlash(path) {
21
+ if (path === "/") return path;
22
+ return path.endsWith("/") ? path.slice(0, -1) : path;
23
+ }
24
+ function isOnLockPage(lockPageSlug, requestUrl) {
25
+ const normalizedSlug = normalizeTrailingSlash(
26
+ normalizeLockPageSlug(lockPageSlug)
27
+ );
28
+ const url = typeof requestUrl === "string" ? new URL(requestUrl) : requestUrl;
29
+ const normalizedPathname = normalizeTrailingSlash(url.pathname);
30
+ return normalizedPathname === normalizedSlug;
31
+ }
32
+
33
+ // src/utils/create-redirect.ts
34
+ var TEMPORARY_REDIRECT_STATUS = 302;
35
+ var createRedirect = (url) => {
36
+ return new Response(null, {
37
+ status: TEMPORARY_REDIRECT_STATUS,
38
+ headers: {
39
+ Location: url.toString()
40
+ }
41
+ });
42
+ };
43
+
44
+ // src/utils/print-message.ts
45
+ var addSlashes = (str) => str.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$").replace(/"/g, '\\"').replace(/'/g, "\\'").replace(/\u0000/g, "\\0").replace(/<\/script>/gi, "<\\/script>");
46
+ var printMessage = (message) => `[@appwarden/middleware] ${addSlashes(message)}`;
47
+
48
+ // src/utils/debug.ts
49
+ var debug = (isDebug) => (...msg) => {
50
+ if (!isDebug) return;
51
+ const parts = msg.map((m) => {
52
+ let content;
53
+ if (m instanceof Error) {
54
+ content = m.stack ?? m.message;
55
+ } else if (typeof m === "object" && m !== null) {
56
+ try {
57
+ content = JSON.stringify(m);
58
+ } catch {
59
+ try {
60
+ content = String(m);
61
+ } catch {
62
+ content = "[Unserializable value]";
63
+ }
64
+ }
65
+ } else {
66
+ content = String(m);
67
+ }
68
+ return content;
69
+ });
70
+ const message = parts.join(" ");
71
+ console.log(printMessage(message));
72
+ };
73
+
74
+ // src/utils/memory-cache.ts
75
+ var MemoryCache = class {
76
+ cache = /* @__PURE__ */ new Map();
77
+ maxSize;
78
+ constructor(options) {
79
+ this.maxSize = options.maxSize;
80
+ }
81
+ get(key) {
82
+ let item;
83
+ if (this.cache.has(key)) {
84
+ item = this.cache.get(key);
85
+ this.cache.delete(key);
86
+ if (item !== void 0) {
87
+ this.cache.set(key, item);
88
+ }
89
+ }
90
+ return item;
91
+ }
92
+ put(key, value) {
93
+ if (this.cache.has(key)) {
94
+ this.cache.delete(key);
95
+ } else if (this.cache.size >= this.maxSize) {
96
+ const firstKey = this.cache.keys().next().value;
97
+ if (firstKey !== void 0) {
98
+ this.cache.delete(firstKey);
99
+ }
100
+ }
101
+ this.cache.set(key, value);
102
+ }
103
+ getValues() {
104
+ return this.cache;
105
+ }
106
+ // the default value will be expired here
107
+ static isExpired = (lockValue) => {
108
+ if (!lockValue) {
109
+ return true;
110
+ }
111
+ return Date.now() > lockValue.lastCheck + 3e4;
112
+ };
113
+ static isTestExpired = (lockValue) => {
114
+ if (!lockValue) {
115
+ return true;
116
+ }
117
+ return Date.now() > lockValue.isLockedTest + LOCKDOWN_TEST_EXPIRY_MS;
118
+ };
119
+ };
120
+
121
+ // src/version.ts
122
+ var MIDDLEWARE_VERSION = "3.11.6";
123
+
124
+ // src/utils/heartbeat.ts
125
+ function createSanitizedMessage(code, path) {
126
+ const fieldName = path.length > 0 ? path[path.length - 1] : "field";
127
+ switch (code) {
128
+ case "invalid_type":
129
+ return `Invalid type for ${fieldName}`;
130
+ case "invalid_literal":
131
+ return `Invalid value for ${fieldName}`;
132
+ case "unrecognized_keys":
133
+ return `Unrecognized keys in ${fieldName}`;
134
+ case "invalid_union":
135
+ return `Invalid union value for ${fieldName}`;
136
+ case "invalid_enum_value":
137
+ return `Invalid enum value for ${fieldName}`;
138
+ case "invalid_arguments":
139
+ return `Invalid arguments for ${fieldName}`;
140
+ case "invalid_return_type":
141
+ return `Invalid return type for ${fieldName}`;
142
+ case "invalid_date":
143
+ return `Invalid date for ${fieldName}`;
144
+ case "invalid_string":
145
+ return `Invalid string format for ${fieldName}`;
146
+ case "too_small":
147
+ return `Value too small for ${fieldName}`;
148
+ case "too_big":
149
+ return `Value too large for ${fieldName}`;
150
+ case "invalid_intersection_types":
151
+ return `Invalid intersection types for ${fieldName}`;
152
+ case "not_multiple_of":
153
+ return `Value not a multiple of required value for ${fieldName}`;
154
+ case "not_finite":
155
+ return `Value must be finite for ${fieldName}`;
156
+ case "custom":
157
+ return `Validation failed for ${fieldName}`;
158
+ default:
159
+ return `Validation error for ${fieldName}`;
160
+ }
161
+ }
162
+ function sanitizePath(path) {
163
+ const truncatedPath = path.slice(0, HEARTBEAT_CONFIG_ERROR_MAX_PATH_DEPTH);
164
+ return truncatedPath.map((segment) => {
165
+ if (typeof segment === "string" && segment.length > HEARTBEAT_CONFIG_ERROR_MAX_PATH_SEGMENT_LENGTH) {
166
+ return segment.substring(
167
+ 0,
168
+ HEARTBEAT_CONFIG_ERROR_MAX_PATH_SEGMENT_LENGTH - 3
169
+ ) + "...";
170
+ }
171
+ return segment;
172
+ });
173
+ }
174
+ function truncateMessage(message) {
175
+ if (message.length <= HEARTBEAT_CONFIG_ERROR_MAX_MESSAGE_LENGTH) {
176
+ return message;
177
+ }
178
+ return message.substring(0, HEARTBEAT_CONFIG_ERROR_MAX_MESSAGE_LENGTH - 3) + "...";
179
+ }
180
+ function createHeartbeatConfigError(path, code, message) {
181
+ return {
182
+ path: sanitizePath(path),
183
+ code: code.substring(0, HEARTBEAT_CONFIG_ERROR_MAX_CODE_LENGTH),
184
+ message: truncateMessage(message)
185
+ };
186
+ }
187
+ function normalizeHeartbeatConfigErrors(configErrors) {
188
+ return configErrors.slice(0, HEARTBEAT_CONFIG_ERROR_MAX_COUNT).map(
189
+ (configError) => createHeartbeatConfigError(
190
+ configError.path,
191
+ configError.code,
192
+ configError.message
193
+ )
194
+ );
195
+ }
196
+ function sanitizeConfigErrors(error) {
197
+ if (!error) {
198
+ return [];
199
+ }
200
+ const errors = [];
201
+ const issues = error.issues.slice(0, HEARTBEAT_CONFIG_ERROR_MAX_COUNT);
202
+ for (const issue of issues) {
203
+ const sanitizedPath = sanitizePath(issue.path);
204
+ const message = truncateMessage(
205
+ createSanitizedMessage(issue.code, sanitizedPath)
206
+ );
207
+ errors.push({
208
+ path: sanitizedPath,
209
+ code: issue.code,
210
+ message
211
+ });
212
+ }
213
+ return errors;
214
+ }
215
+ function createHeartbeatResponseBody(service, configErrors = []) {
216
+ return {
217
+ app: "appwarden",
218
+ kind: "heartbeat",
219
+ status: "ok",
220
+ contractVersion: HEARTBEAT_CONTRACT_VERSION,
221
+ service,
222
+ version: MIDDLEWARE_VERSION,
223
+ configErrors: normalizeHeartbeatConfigErrors(configErrors)
224
+ };
225
+ }
226
+ function createHeartbeatResponse(service, configErrors = []) {
227
+ const body = createHeartbeatResponseBody(service, configErrors);
228
+ return new Response(JSON.stringify(body), {
229
+ status: 200,
230
+ headers: {
231
+ "content-type": "application/json",
232
+ "cache-control": "no-store",
233
+ "x-appwarden-heartbeat": "1",
234
+ "x-appwarden-contract-version": String(HEARTBEAT_CONTRACT_VERSION),
235
+ "x-appwarden-service": service,
236
+ "x-appwarden-version": MIDDLEWARE_VERSION
237
+ }
238
+ });
239
+ }
240
+ function isHeartbeatRoute(url) {
241
+ return url.pathname === APPWARDEN_HEARTBEAT_ROUTE;
242
+ }
243
+ function isHeartbeatRequest(request, url) {
244
+ return request.method === "GET" && isHeartbeatRoute(url);
245
+ }
246
+ function handleHeartbeatRequest(request, service, configErrors = []) {
247
+ return createHeartbeatResponse(service, configErrors);
248
+ }
249
+
250
+ // src/utils/request-checks.ts
251
+ function isHTMLResponse(response) {
252
+ return response.headers.get("Content-Type")?.includes("text/html") ?? false;
253
+ }
254
+ function isHTMLRequest(request) {
255
+ const accept = request.headers.get("accept");
256
+ if (!accept) {
257
+ return false;
258
+ }
259
+ const normalizedAccept = accept.toLowerCase();
260
+ const isWildcardOnlyAccept = (value) => {
261
+ const mediaRanges2 = value.split(",");
262
+ let hasNonEmptyRange = false;
263
+ for (const range of mediaRanges2) {
264
+ const [typeSubtype] = range.split(";");
265
+ const trimmed = typeSubtype.trim();
266
+ if (!trimmed) {
267
+ continue;
268
+ }
269
+ hasNonEmptyRange = true;
270
+ if (trimmed !== "*/*" && trimmed !== "*") {
271
+ return false;
272
+ }
273
+ }
274
+ return hasNonEmptyRange;
275
+ };
276
+ if (isWildcardOnlyAccept(normalizedAccept)) {
277
+ return false;
278
+ }
279
+ const mediaRanges = normalizedAccept.split(",");
280
+ for (const range of mediaRanges) {
281
+ const [typeSubtype] = range.split(";");
282
+ const token = typeSubtype.trim();
283
+ if (token === "text/html") {
284
+ return true;
285
+ }
286
+ }
287
+ return false;
288
+ }
289
+
290
+ // src/utils/cloudflare/csp-keywords.ts
291
+ var CSP_KEYWORDS = [
292
+ "self",
293
+ "none",
294
+ "unsafe-inline",
295
+ "unsafe-eval",
296
+ "unsafe-hashes",
297
+ "strict-dynamic",
298
+ "report-sample",
299
+ "unsafe-allow-redirects",
300
+ "wasm-unsafe-eval",
301
+ "trusted-types-eval",
302
+ "report-sha256",
303
+ "report-sha384",
304
+ "report-sha512",
305
+ "unsafe-webtransport-hashes"
306
+ ];
307
+ var CSP_KEYWORDS_SET = new Set(CSP_KEYWORDS);
308
+ var isCSPKeyword = (value) => {
309
+ return CSP_KEYWORDS_SET.has(value.toLowerCase());
310
+ };
311
+ var isQuoted = (value) => {
312
+ return value.startsWith("'") && value.endsWith("'");
313
+ };
314
+ var autoQuoteCSPKeyword = (value) => {
315
+ const trimmed = value.trim();
316
+ if (isQuoted(trimmed)) {
317
+ return trimmed;
318
+ }
319
+ if (isCSPKeyword(trimmed)) {
320
+ return `'${trimmed}'`;
321
+ }
322
+ return trimmed;
323
+ };
324
+ var autoQuoteCSPDirectiveValue = (value) => {
325
+ return value.trim().split(/\s+/).filter(Boolean).map(autoQuoteCSPKeyword).join(" ");
326
+ };
327
+ var autoQuoteCSPDirectiveArray = (values) => {
328
+ return values.map(autoQuoteCSPKeyword);
329
+ };
330
+
331
+ // src/utils/cloudflare/make-csp-header.ts
332
+ var addNonce = (value, cspNonce) => value.replace("{{nonce}}", `'nonce-${cspNonce}'`);
333
+ var makeCSPHeader = (cspNonce, directives, mode) => {
334
+ const namesSeen = /* @__PURE__ */ new Set(), result = [];
335
+ Object.entries(directives ?? {}).forEach(([originalName, value]) => {
336
+ const name = originalName.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
337
+ if (namesSeen.has(name)) {
338
+ throw new Error(`${originalName} is specified more than once`);
339
+ }
340
+ namesSeen.add(name);
341
+ let directiveValue;
342
+ if (Array.isArray(value)) {
343
+ directiveValue = autoQuoteCSPDirectiveArray(value).join(" ");
344
+ } else if (value === true) {
345
+ directiveValue = "";
346
+ } else if (typeof value === "string") {
347
+ directiveValue = autoQuoteCSPDirectiveValue(value);
348
+ } else {
349
+ return;
350
+ }
351
+ if (directiveValue) {
352
+ result.push(`${name} ${addNonce(directiveValue, cspNonce)}`);
353
+ } else {
354
+ result.push(name);
355
+ }
356
+ });
357
+ return [
358
+ mode === "enforced" ? "Content-Security-Policy" : "Content-Security-Policy-Report-Only",
359
+ result.join("; ")
360
+ ];
361
+ };
362
+
363
+ export {
364
+ buildLockPageUrl,
365
+ isOnLockPage,
366
+ TEMPORARY_REDIRECT_STATUS,
367
+ createRedirect,
368
+ printMessage,
369
+ debug,
370
+ MemoryCache,
371
+ createHeartbeatConfigError,
372
+ sanitizeConfigErrors,
373
+ isHeartbeatRequest,
374
+ handleHeartbeatRequest,
375
+ isHTMLResponse,
376
+ isHTMLRequest,
377
+ makeCSPHeader
378
+ };
@@ -0,0 +1,9 @@
1
+ // src/utils/to-next-response.ts
2
+ import { NextResponse } from "next/server";
3
+ var toNextResponse = (response) => {
4
+ return new NextResponse(response.body, response);
5
+ };
6
+
7
+ export {
8
+ toNextResponse
9
+ };
@@ -1,12 +1,10 @@
1
1
  import {
2
- isHTMLResponse
3
- } from "./chunk-AY4ZKZTF.js";
2
+ isHTMLResponse,
3
+ makeCSPHeader
4
+ } from "./chunk-HTSD4WPC.js";
4
5
  import {
5
6
  UseCSPInputSchema
6
- } from "./chunk-ZTVJBORU.js";
7
- import {
8
- makeCSPHeader
9
- } from "./chunk-R7TXTHSG.js";
7
+ } from "./chunk-Z7P4QVEY.js";
10
8
 
11
9
  // src/middlewares/use-content-security-policy.ts
12
10
  var AppendAttribute = (attribute, nonce) => ({