@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/vercel.js CHANGED
@@ -2,30 +2,35 @@ import {
2
2
  isCacheUrl,
3
3
  isValidCacheUrl
4
4
  } from "./chunk-QEFORWCW.js";
5
+ import {
6
+ getErrors
7
+ } from "./chunk-NV7K5PRA.js";
8
+ import {
9
+ toNextResponse
10
+ } from "./chunk-HUWGPM4M.js";
5
11
  import {
6
12
  MemoryCache,
7
13
  TEMPORARY_REDIRECT_STATUS,
8
14
  buildLockPageUrl,
9
15
  debug,
16
+ handleHeartbeatRequest,
10
17
  isHTMLRequest,
11
- isOnLockPage
12
- } from "./chunk-AY4ZKZTF.js";
18
+ isHeartbeatRequest,
19
+ isOnLockPage,
20
+ makeCSPHeader,
21
+ printMessage,
22
+ sanitizeConfigErrors
23
+ } from "./chunk-HTSD4WPC.js";
13
24
  import {
14
25
  APPWARDEN_CACHE_KEY,
26
+ AppwardenApiHostnameSchema,
15
27
  CSPDirectivesSchema,
16
28
  CSPModeSchema,
29
+ HEARTBEAT_SERVICES,
30
+ LockValue,
17
31
  errors,
18
32
  globalErrors
19
- } from "./chunk-ZTVJBORU.js";
20
- import {
21
- getErrors,
22
- makeCSPHeader,
23
- printMessage
24
- } from "./chunk-R7TXTHSG.js";
25
- import {
26
- AppwardenApiHostnameSchema,
27
- LockValue
28
- } from "./chunk-WEM7GS4M.js";
33
+ } from "./chunk-Z7P4QVEY.js";
29
34
 
30
35
  // src/runners/appwarden-on-vercel.ts
31
36
  import { waitUntil } from "@vercel/functions";
@@ -250,6 +255,17 @@ function safeWaitUntil(promise) {
250
255
  }
251
256
  function createAppwardenMiddleware(config) {
252
257
  return async (request) => {
258
+ const requestUrl = new URL(request.url);
259
+ if (isHeartbeatRequest(request, requestUrl)) {
260
+ const validationResult = AppwardenConfigSchema.safeParse(config);
261
+ const configErrors = validationResult.success ? [] : sanitizeConfigErrors(validationResult.error);
262
+ const response = handleHeartbeatRequest(
263
+ request,
264
+ HEARTBEAT_SERVICES.VERCEL,
265
+ configErrors
266
+ );
267
+ return toNextResponse(response);
268
+ }
253
269
  if (validateConfig(config, AppwardenConfigSchema)) {
254
270
  return NextResponse.next();
255
271
  }
@@ -267,8 +283,15 @@ function createAppwardenMiddleware(config) {
267
283
  }
268
284
  return response;
269
285
  };
286
+ const createMutableRedirectResponse = (location) => {
287
+ return new Response(null, {
288
+ status: TEMPORARY_REDIRECT_STATUS,
289
+ headers: {
290
+ Location: location
291
+ }
292
+ });
293
+ };
270
294
  try {
271
- const requestUrl = new URL(request.url);
272
295
  const isHTML = isHTMLRequest(request);
273
296
  debugFn(
274
297
  `Appwarden middleware invoked for ${requestUrl.pathname}`,
@@ -319,9 +342,8 @@ function createAppwardenMiddleware(config) {
319
342
  parsedConfig.lockPageSlug,
320
343
  request.url
321
344
  );
322
- const redirectResponse = Response.redirect(
323
- lockPageUrl.toString(),
324
- TEMPORARY_REDIRECT_STATUS
345
+ const redirectResponse = createMutableRedirectResponse(
346
+ lockPageUrl.toString()
325
347
  );
326
348
  return applyCspHeaders(redirectResponse);
327
349
  }
package/chunk-AY4ZKZTF.js DELETED
@@ -1,162 +0,0 @@
1
- import {
2
- LOCKDOWN_TEST_EXPIRY_MS
3
- } from "./chunk-ZTVJBORU.js";
4
- import {
5
- printMessage
6
- } from "./chunk-R7TXTHSG.js";
7
-
8
- // src/utils/build-lock-page-url.ts
9
- function normalizeLockPageSlug(lockPageSlug) {
10
- return lockPageSlug.startsWith("/") ? lockPageSlug : `/${lockPageSlug}`;
11
- }
12
- function buildLockPageUrl(lockPageSlug, requestUrl) {
13
- const normalizedSlug = normalizeLockPageSlug(lockPageSlug);
14
- return new URL(normalizedSlug, requestUrl);
15
- }
16
- function normalizeTrailingSlash(path) {
17
- if (path === "/") return path;
18
- return path.endsWith("/") ? path.slice(0, -1) : path;
19
- }
20
- function isOnLockPage(lockPageSlug, requestUrl) {
21
- const normalizedSlug = normalizeTrailingSlash(
22
- normalizeLockPageSlug(lockPageSlug)
23
- );
24
- const url = typeof requestUrl === "string" ? new URL(requestUrl) : requestUrl;
25
- const normalizedPathname = normalizeTrailingSlash(url.pathname);
26
- return normalizedPathname === normalizedSlug;
27
- }
28
-
29
- // src/utils/create-redirect.ts
30
- var TEMPORARY_REDIRECT_STATUS = 302;
31
- var createRedirect = (url) => {
32
- return new Response(null, {
33
- status: TEMPORARY_REDIRECT_STATUS,
34
- headers: {
35
- Location: url.toString()
36
- }
37
- });
38
- };
39
-
40
- // src/utils/debug.ts
41
- var debug = (isDebug) => (...msg) => {
42
- if (!isDebug) return;
43
- const parts = msg.map((m) => {
44
- let content;
45
- if (m instanceof Error) {
46
- content = m.stack ?? m.message;
47
- } else if (typeof m === "object" && m !== null) {
48
- try {
49
- content = JSON.stringify(m);
50
- } catch {
51
- try {
52
- content = String(m);
53
- } catch {
54
- content = "[Unserializable value]";
55
- }
56
- }
57
- } else {
58
- content = String(m);
59
- }
60
- return content;
61
- });
62
- const message = parts.join(" ");
63
- console.log(printMessage(message));
64
- };
65
-
66
- // src/utils/memory-cache.ts
67
- var MemoryCache = class {
68
- cache = /* @__PURE__ */ new Map();
69
- maxSize;
70
- constructor(options) {
71
- this.maxSize = options.maxSize;
72
- }
73
- get(key) {
74
- let item;
75
- if (this.cache.has(key)) {
76
- item = this.cache.get(key);
77
- this.cache.delete(key);
78
- if (item !== void 0) {
79
- this.cache.set(key, item);
80
- }
81
- }
82
- return item;
83
- }
84
- put(key, value) {
85
- if (this.cache.has(key)) {
86
- this.cache.delete(key);
87
- } else if (this.cache.size >= this.maxSize) {
88
- const firstKey = this.cache.keys().next().value;
89
- if (firstKey !== void 0) {
90
- this.cache.delete(firstKey);
91
- }
92
- }
93
- this.cache.set(key, value);
94
- }
95
- getValues() {
96
- return this.cache;
97
- }
98
- // the default value will be expired here
99
- static isExpired = (lockValue) => {
100
- if (!lockValue) {
101
- return true;
102
- }
103
- return Date.now() > lockValue.lastCheck + 3e4;
104
- };
105
- static isTestExpired = (lockValue) => {
106
- if (!lockValue) {
107
- return true;
108
- }
109
- return Date.now() > lockValue.isLockedTest + LOCKDOWN_TEST_EXPIRY_MS;
110
- };
111
- };
112
-
113
- // src/utils/request-checks.ts
114
- function isHTMLResponse(response) {
115
- return response.headers.get("Content-Type")?.includes("text/html") ?? false;
116
- }
117
- function isHTMLRequest(request) {
118
- const accept = request.headers.get("accept");
119
- if (!accept) {
120
- return false;
121
- }
122
- const normalizedAccept = accept.toLowerCase();
123
- const isWildcardOnlyAccept = (value) => {
124
- const mediaRanges2 = value.split(",");
125
- let hasNonEmptyRange = false;
126
- for (const range of mediaRanges2) {
127
- const [typeSubtype] = range.split(";");
128
- const trimmed = typeSubtype.trim();
129
- if (!trimmed) {
130
- continue;
131
- }
132
- hasNonEmptyRange = true;
133
- if (trimmed !== "*/*" && trimmed !== "*") {
134
- return false;
135
- }
136
- }
137
- return hasNonEmptyRange;
138
- };
139
- if (isWildcardOnlyAccept(normalizedAccept)) {
140
- return false;
141
- }
142
- const mediaRanges = normalizedAccept.split(",");
143
- for (const range of mediaRanges) {
144
- const [typeSubtype] = range.split(";");
145
- const token = typeSubtype.trim();
146
- if (token === "text/html") {
147
- return true;
148
- }
149
- }
150
- return false;
151
- }
152
-
153
- export {
154
- buildLockPageUrl,
155
- isOnLockPage,
156
- TEMPORARY_REDIRECT_STATUS,
157
- createRedirect,
158
- debug,
159
- MemoryCache,
160
- isHTMLResponse,
161
- isHTMLRequest
162
- };
package/chunk-QC2ZUZWY.js DELETED
@@ -1,84 +0,0 @@
1
- import {
2
- MemoryCache,
3
- debug
4
- } from "./chunk-AY4ZKZTF.js";
5
- import {
6
- APPWARDEN_CACHE_KEY,
7
- APPWARDEN_TEST_ROUTE
8
- } from "./chunk-ZTVJBORU.js";
9
- import {
10
- deleteEdgeValue,
11
- getLockValue,
12
- store,
13
- syncEdgeValue
14
- } from "./chunk-R7TXTHSG.js";
15
-
16
- // src/core/check-lock-status.ts
17
- var createContext = async (config) => {
18
- const requestUrl = new URL(config.request.url);
19
- const keyName = APPWARDEN_CACHE_KEY;
20
- const provider = "cloudflare-cache";
21
- const debugFn = debug(config.debug ?? false);
22
- const edgeCache = store.json(
23
- {
24
- serviceOrigin: requestUrl.origin,
25
- cache: await caches.open("appwarden:lock"),
26
- debug: debugFn
27
- },
28
- keyName
29
- );
30
- return {
31
- keyName,
32
- request: config.request,
33
- edgeCache,
34
- requestUrl,
35
- provider,
36
- debug: debugFn,
37
- lockPageSlug: config.lockPageSlug,
38
- appwardenApiToken: config.appwardenApiToken,
39
- appwardenApiHostname: config.appwardenApiHostname,
40
- waitUntil: config.waitUntil
41
- };
42
- };
43
- var resolveLockStatus = async (context) => {
44
- const { lockValue, shouldDeleteEdgeValue } = await getLockValue(context);
45
- if (shouldDeleteEdgeValue) {
46
- context.debug("Deleting corrupted cache value");
47
- await deleteEdgeValue(context);
48
- }
49
- const isTestRoute = context.requestUrl.pathname === APPWARDEN_TEST_ROUTE;
50
- const isTestLock = isTestRoute && !MemoryCache.isTestExpired(lockValue) && !!lockValue;
51
- return {
52
- isLocked: !!lockValue?.isLocked || isTestLock,
53
- isTestLock,
54
- lockValue,
55
- wasDeleted: shouldDeleteEdgeValue ?? false
56
- };
57
- };
58
- var checkLockStatus = async (config) => {
59
- const context = await createContext(config);
60
- let { isLocked, isTestLock, lockValue, wasDeleted } = await resolveLockStatus(context);
61
- const isExpired = MemoryCache.isExpired(lockValue);
62
- if (!isExpired && !wasDeleted && lockValue) {
63
- context.debug("Lock value resolved from cache");
64
- }
65
- if (isExpired || wasDeleted) {
66
- if (!lockValue || wasDeleted || lockValue.isLocked) {
67
- context.debug(
68
- "No fresh cached lock status available - syncing with API synchronously"
69
- );
70
- await syncEdgeValue(context);
71
- ({ isLocked, isTestLock } = await resolveLockStatus(context));
72
- } else {
73
- context.debug(
74
- "Cached lock status expired but last known state unlocked - syncing with API in background"
75
- );
76
- config.waitUntil(syncEdgeValue(context));
77
- }
78
- }
79
- return { isLocked, isTestLock };
80
- };
81
-
82
- export {
83
- checkLockStatus
84
- };
package/chunk-WEM7GS4M.js DELETED
@@ -1,29 +0,0 @@
1
- // src/schemas/helpers.ts
2
- import { z } from "zod";
3
- var BoolOrStringSchema = z.union([z.string(), z.boolean()]).optional();
4
- var BooleanSchema = BoolOrStringSchema.transform((val) => {
5
- if (val === "true" || val === true) {
6
- return true;
7
- } else if (val === "false" || val === false) {
8
- return false;
9
- }
10
- throw new Error("Invalid value");
11
- });
12
- var AppwardenApiTokenSchema = z.string().refine((val) => !!val, { message: "appwardenApiToken is required" });
13
- var AppwardenApiHostnameSchema = z.string().url({
14
- message: "Invalid `appwardenApiHostname`. Please provide an absolute URL (e.g. https://api.appwarden.io)."
15
- }).refine((value) => value.startsWith("https://"), {
16
- message: "`appwardenApiHostname` must use the https:// scheme (e.g. https://api.appwarden.io)."
17
- });
18
- var LockValue = z.object({
19
- isLocked: z.number(),
20
- isLockedTest: z.number(),
21
- lastCheck: z.number()
22
- });
23
-
24
- export {
25
- BooleanSchema,
26
- AppwardenApiTokenSchema,
27
- AppwardenApiHostnameSchema,
28
- LockValue
29
- };
package/chunk-X7WZVYQS.js DELETED
@@ -1,6 +0,0 @@
1
- // src/utils/get-now.ts
2
- var getNowMs = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
3
-
4
- export {
5
- getNowMs
6
- };
@@ -1,29 +0,0 @@
1
- import {
2
- CSP_KEYWORDS,
3
- autoQuoteCSPDirectiveArray,
4
- autoQuoteCSPDirectiveValue,
5
- autoQuoteCSPKeyword,
6
- deleteEdgeValue,
7
- getLockValue,
8
- insertErrorLogs,
9
- isCSPKeyword,
10
- isQuoted,
11
- makeCSPHeader,
12
- store,
13
- syncEdgeValue
14
- } from "./chunk-R7TXTHSG.js";
15
- import "./chunk-WEM7GS4M.js";
16
- export {
17
- CSP_KEYWORDS,
18
- autoQuoteCSPDirectiveArray,
19
- autoQuoteCSPDirectiveValue,
20
- autoQuoteCSPKeyword,
21
- deleteEdgeValue,
22
- getLockValue,
23
- insertErrorLogs,
24
- isCSPKeyword,
25
- isQuoted,
26
- makeCSPHeader,
27
- store,
28
- syncEdgeValue
29
- };