@appwarden/middleware 3.0.3 → 3.1.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @appwarden/middleware
2
2
 
3
- ![Test Coverage](https://img.shields.io/badge/coverage-95.96%25-brightgreen)
3
+ ![Test Coverage](https://img.shields.io/badge/coverage-95.44%25-brightgreen)
4
4
  [![npm version](https://img.shields.io/npm/v/@appwarden/middleware.svg)](https://www.npmjs.com/package/@appwarden/middleware)
5
5
  [![npm provenance](https://img.shields.io/badge/npm-provenance-green)](https://docs.npmjs.com/generating-provenance-statements)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
@@ -26,13 +26,17 @@ For detailed usage instructions, please refer to our [documentation](https://app
26
26
 
27
27
  ### On Cloudflare
28
28
 
29
- Cloudflare has two deployment options: [pages.dev](https://pages.dev) and [Workers static assets](https://developers.cloudflare.com/workers/static-assets/). We support both.
29
+ For all websites deployed on Cloudflare—including popular modern frameworks like Astro, Next.js, React Router, TanStack Start, and more—we recommend using the [build-cloudflare-action](https://github.com/appwarden/build-cloudflare-action). This action builds a worker that runs on the root of your domain (e.g., `your.app.io/*` for an `your.app.io` website), providing seamless integration with Appwarden without modifying your application code.
30
30
 
31
- #### On pages.dev
31
+ Please see the [build-cloudflare-action documentation](https://github.com/appwarden/build-cloudflare-action) for more information.
32
32
 
33
- - [All websites on pages.dev](https://appwarden.io/docs/guides/cloudflare-integration)
33
+ #### Recommended: build-cloudflare-action
34
34
 
35
- #### On Workers static assets
35
+ - [build-cloudflare-action](https://github.com/appwarden/build-cloudflare-action) Seamless integration with Appwarden for Cloudflare-deployed websites.
36
+
37
+ #### Framework adapters (alternative)
38
+
39
+ If you cannot use the `build-cloudflare-action`, you can use framework-specific adapters instead:
36
40
 
37
41
  - [Astro](https://appwarden.io/docs/guides/astro-cloudflare)
38
42
  - [React Router](https://appwarden.io/docs/guides/react-router-cloudflare)
@@ -2,6 +2,27 @@ import {
2
2
  LOCKDOWN_TEST_EXPIRY_MS
3
3
  } from "./chunk-7UTT3M2S.js";
4
4
 
5
+ // src/utils/build-lock-page-url.ts
6
+ function normalizeLockPageSlug(lockPageSlug) {
7
+ return lockPageSlug.startsWith("/") ? lockPageSlug : `/${lockPageSlug}`;
8
+ }
9
+ function buildLockPageUrl(lockPageSlug, requestUrl) {
10
+ const normalizedSlug = normalizeLockPageSlug(lockPageSlug);
11
+ return new URL(normalizedSlug, requestUrl);
12
+ }
13
+ function normalizeTrailingSlash(path) {
14
+ if (path === "/") return path;
15
+ return path.endsWith("/") ? path.slice(0, -1) : path;
16
+ }
17
+ function isOnLockPage(lockPageSlug, requestUrl) {
18
+ const normalizedSlug = normalizeTrailingSlash(
19
+ normalizeLockPageSlug(lockPageSlug)
20
+ );
21
+ const url = typeof requestUrl === "string" ? new URL(requestUrl) : requestUrl;
22
+ const normalizedPathname = normalizeTrailingSlash(url.pathname);
23
+ return normalizedPathname === normalizedSlug;
24
+ }
25
+
5
26
  // src/utils/memory-cache.ts
6
27
  var MemoryCache = class {
7
28
  cache = /* @__PURE__ */ new Map();
@@ -102,6 +123,8 @@ var LockValue = z.object({
102
123
  });
103
124
 
104
125
  export {
126
+ buildLockPageUrl,
127
+ isOnLockPage,
105
128
  getErrors,
106
129
  MemoryCache,
107
130
  BooleanSchema,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  LockValue,
3
3
  MemoryCache
4
- } from "./chunk-B5IE7V77.js";
4
+ } from "./chunk-OPZQCRC4.js";
5
5
  import {
6
6
  APPWARDEN_CACHE_KEY,
7
7
  APPWARDEN_TEST_ROUTE,
@@ -0,0 +1,27 @@
1
+ import {
2
+ getErrors
3
+ } from "./chunk-OPZQCRC4.js";
4
+ import {
5
+ printMessage
6
+ } from "./chunk-7UTT3M2S.js";
7
+
8
+ // src/utils/validate-config.ts
9
+ function validateConfig(config, schema) {
10
+ const result = schema.safeParse(config);
11
+ const hasErrors = !result.success;
12
+ if (hasErrors) {
13
+ const mappedErrors = getErrors(result.error);
14
+ if (mappedErrors.length > 0) {
15
+ for (const error of mappedErrors) {
16
+ console.error(printMessage(error));
17
+ }
18
+ } else {
19
+ console.error(printMessage(result.error.message));
20
+ }
21
+ }
22
+ return hasErrors;
23
+ }
24
+
25
+ export {
26
+ validateConfig
27
+ };
@@ -3,17 +3,17 @@ import {
3
3
  createRedirect
4
4
  } from "../chunk-6M7BE3AW.js";
5
5
  import {
6
- buildLockPageUrl,
7
- isOnLockPage,
8
6
  validateConfig
9
- } from "../chunk-4JGYMZTR.js";
7
+ } from "../chunk-RDASXYYU.js";
10
8
  import {
11
9
  checkLockStatus
12
- } from "../chunk-5DEXVBY6.js";
10
+ } from "../chunk-RB2LXM55.js";
13
11
  import {
14
12
  AppwardenApiTokenSchema,
15
- BooleanSchema
16
- } from "../chunk-B5IE7V77.js";
13
+ BooleanSchema,
14
+ buildLockPageUrl,
15
+ isOnLockPage
16
+ } from "../chunk-OPZQCRC4.js";
17
17
  import {
18
18
  isHTMLRequest,
19
19
  printMessage
@@ -2,17 +2,17 @@ import {
2
2
  TEMPORARY_REDIRECT_STATUS
3
3
  } from "../chunk-6M7BE3AW.js";
4
4
  import {
5
- buildLockPageUrl,
6
- isOnLockPage,
7
5
  validateConfig
8
- } from "../chunk-4JGYMZTR.js";
6
+ } from "../chunk-RDASXYYU.js";
9
7
  import {
10
8
  checkLockStatus
11
- } from "../chunk-5DEXVBY6.js";
9
+ } from "../chunk-RB2LXM55.js";
12
10
  import {
13
11
  AppwardenApiTokenSchema,
14
- BooleanSchema
15
- } from "../chunk-B5IE7V77.js";
12
+ BooleanSchema,
13
+ buildLockPageUrl,
14
+ isOnLockPage
15
+ } from "../chunk-OPZQCRC4.js";
16
16
  import {
17
17
  isHTMLRequest,
18
18
  printMessage
@@ -6,6 +6,11 @@ interface CloudflareContext {
6
6
  env: CloudflareEnv;
7
7
  ctx: ExecutionContext;
8
8
  }
9
+ /**
10
+ * Symbol used to store Cloudflare context in RouterContextProvider.
11
+ * This is used when middleware is enabled with the v8_middleware future flag.
12
+ */
13
+ declare const cloudflareContextSymbol: unique symbol;
9
14
  /**
10
15
  * Configuration for the Appwarden middleware.
11
16
  */
@@ -27,13 +32,15 @@ type ReactRouterConfigFn = (cloudflare: CloudflareContext) => ReactRouterAppward
27
32
  /**
28
33
  * React Router middleware function signature.
29
34
  * This matches the unstable_middleware export type in React Router v7.
35
+ *
36
+ * Supports both old and new context APIs:
37
+ * - Old API: context is a plain object with `cloudflare` property
38
+ * - New API (v8_middleware): context is a RouterContextProvider instance
30
39
  */
31
40
  interface ReactRouterMiddlewareArgs {
32
41
  request: Request;
33
42
  params: Record<string, string | undefined>;
34
- context: {
35
- cloudflare: CloudflareContext;
36
- };
43
+ context: any;
37
44
  }
38
45
  type ReactRouterMiddlewareFunction = (args: ReactRouterMiddlewareArgs, next: () => Promise<unknown>) => Promise<unknown>;
39
46
  /**
@@ -42,6 +49,10 @@ type ReactRouterMiddlewareFunction = (args: ReactRouterMiddlewareArgs, next: ()
42
49
  * This middleware checks if the site is locked and redirects to the lock page if so.
43
50
  * It should be exported from your root route (root.tsx) to protect all routes.
44
51
  *
52
+ * Supports both old and new React Router context APIs:
53
+ * - Old API: Pass context as plain object with `cloudflare` property
54
+ * - New API (v8_middleware): Use RouterContextProvider with cloudflareContextSymbol
55
+ *
45
56
  * @example
46
57
  * ```typescript
47
58
  * // app/root.tsx
@@ -60,4 +71,4 @@ type ReactRouterMiddlewareFunction = (args: ReactRouterMiddlewareArgs, next: ()
60
71
  */
61
72
  declare function createAppwardenMiddleware(configFn: ReactRouterConfigFn): ReactRouterMiddlewareFunction;
62
73
 
63
- export { type CloudflareContext, type ReactRouterAppwardenConfig, type ReactRouterConfigFn, type ReactRouterMiddlewareArgs, type ReactRouterMiddlewareFunction, createAppwardenMiddleware };
74
+ export { type CloudflareContext, type ReactRouterAppwardenConfig, type ReactRouterConfigFn, type ReactRouterMiddlewareArgs, type ReactRouterMiddlewareFunction, cloudflareContextSymbol, createAppwardenMiddleware };
@@ -2,17 +2,17 @@ import {
2
2
  createRedirect
3
3
  } from "../chunk-6M7BE3AW.js";
4
4
  import {
5
- buildLockPageUrl,
6
- isOnLockPage,
7
5
  validateConfig
8
- } from "../chunk-4JGYMZTR.js";
6
+ } from "../chunk-RDASXYYU.js";
9
7
  import {
10
8
  checkLockStatus
11
- } from "../chunk-5DEXVBY6.js";
9
+ } from "../chunk-RB2LXM55.js";
12
10
  import {
13
11
  AppwardenApiTokenSchema,
14
- BooleanSchema
15
- } from "../chunk-B5IE7V77.js";
12
+ BooleanSchema,
13
+ buildLockPageUrl,
14
+ isOnLockPage
15
+ } from "../chunk-OPZQCRC4.js";
16
16
  import {
17
17
  isHTMLRequest,
18
18
  printMessage
@@ -32,15 +32,33 @@ var ReactRouterCloudflareConfigSchema = z.object({
32
32
  });
33
33
 
34
34
  // src/adapters/react-router-cloudflare.ts
35
+ var cloudflareContextSymbol = /* @__PURE__ */ Symbol.for(
36
+ "@appwarden/middleware:cloudflare"
37
+ );
38
+ function getCloudflareContext(context) {
39
+ if (context?.cloudflare) {
40
+ return context.cloudflare;
41
+ }
42
+ if (context?.get && typeof context.get === "function") {
43
+ try {
44
+ const cloudflare = context.get(cloudflareContextSymbol);
45
+ if (cloudflare) {
46
+ return cloudflare;
47
+ }
48
+ } catch {
49
+ }
50
+ }
51
+ return null;
52
+ }
35
53
  function createAppwardenMiddleware(configFn) {
36
54
  return async (args, next) => {
37
55
  const { request, context } = args;
38
56
  try {
39
- const cloudflare = context.cloudflare;
57
+ const cloudflare = getCloudflareContext(context);
40
58
  if (!cloudflare) {
41
59
  console.error(
42
60
  printMessage(
43
- "Cloudflare context not found. Make sure you're running on Cloudflare Workers."
61
+ "Cloudflare context not found. Make sure you're running on Cloudflare Workers and have set up the context correctly."
44
62
  )
45
63
  );
46
64
  return next();
@@ -83,5 +101,6 @@ function createAppwardenMiddleware(configFn) {
83
101
  };
84
102
  }
85
103
  export {
104
+ cloudflareContextSymbol,
86
105
  createAppwardenMiddleware
87
106
  };
@@ -2,17 +2,17 @@ import {
2
2
  createRedirect
3
3
  } from "../chunk-6M7BE3AW.js";
4
4
  import {
5
- buildLockPageUrl,
6
- isOnLockPage,
7
5
  validateConfig
8
- } from "../chunk-4JGYMZTR.js";
6
+ } from "../chunk-RDASXYYU.js";
9
7
  import {
10
8
  checkLockStatus
11
- } from "../chunk-5DEXVBY6.js";
9
+ } from "../chunk-RB2LXM55.js";
12
10
  import {
13
11
  AppwardenApiTokenSchema,
14
- BooleanSchema
15
- } from "../chunk-B5IE7V77.js";
12
+ BooleanSchema,
13
+ buildLockPageUrl,
14
+ isOnLockPage
15
+ } from "../chunk-OPZQCRC4.js";
16
16
  import {
17
17
  isHTMLRequest,
18
18
  printMessage
package/cloudflare.js CHANGED
@@ -5,15 +5,16 @@ import {
5
5
  checkLockStatus,
6
6
  getLockValue,
7
7
  store
8
- } from "./chunk-5DEXVBY6.js";
8
+ } from "./chunk-RB2LXM55.js";
9
9
  import {
10
10
  AppwardenApiTokenSchema,
11
11
  BooleanSchema,
12
- getErrors
13
- } from "./chunk-B5IE7V77.js";
12
+ getErrors,
13
+ isOnLockPage
14
+ } from "./chunk-OPZQCRC4.js";
14
15
  import {
15
16
  APPWARDEN_CACHE_KEY,
16
- isHTMLResponse,
17
+ isHTMLRequest,
17
18
  printMessage
18
19
  } from "./chunk-7UTT3M2S.js";
19
20
 
@@ -124,42 +125,48 @@ var handleResetCache = async (keyName, provider, edgeCache, request) => {
124
125
 
125
126
  // src/middlewares/use-appwarden.ts
126
127
  var useAppwarden = (input) => async (context, next) => {
127
- await next();
128
- const { request, response } = context;
128
+ const { request } = context;
129
+ let shouldCallNext = true;
129
130
  try {
130
131
  const requestUrl = new URL(request.url);
131
- const provider = "cloudflare-cache";
132
- const keyName = APPWARDEN_CACHE_KEY;
133
- const edgeCache = store.json(
134
- {
135
- serviceOrigin: requestUrl.origin,
136
- cache: await caches.open("appwarden:lock")
137
- },
138
- keyName
139
- );
140
132
  if (isResetCacheRequest(request)) {
133
+ const provider = "cloudflare-cache";
134
+ const keyName = APPWARDEN_CACHE_KEY;
135
+ const edgeCache = store.json(
136
+ {
137
+ serviceOrigin: requestUrl.origin,
138
+ cache: await caches.open("appwarden:lock")
139
+ },
140
+ keyName
141
+ );
141
142
  await handleResetCache(keyName, provider, edgeCache, request);
142
143
  return;
143
144
  }
144
- if (isHTMLResponse(response)) {
145
- const lockPageSlug = input.multidomainConfig?.[requestUrl.hostname]?.lockPageSlug ?? input.lockPageSlug;
146
- if (!lockPageSlug) {
147
- return;
148
- }
149
- const result = await checkLockStatus({
150
- request,
151
- appwardenApiToken: input.appwardenApiToken,
152
- appwardenApiHostname: input.appwardenApiHostname,
153
- debug: input.debug,
145
+ if (!isHTMLRequest(request)) {
146
+ return;
147
+ }
148
+ const lockPageSlug = input.multidomainConfig?.[requestUrl.hostname]?.lockPageSlug ?? input.lockPageSlug;
149
+ if (!lockPageSlug) {
150
+ return;
151
+ }
152
+ if (isOnLockPage(lockPageSlug, request.url)) {
153
+ return;
154
+ }
155
+ const result = await checkLockStatus({
156
+ request,
157
+ appwardenApiToken: input.appwardenApiToken,
158
+ appwardenApiHostname: input.appwardenApiHostname,
159
+ debug: input.debug,
160
+ lockPageSlug,
161
+ waitUntil: (fn) => context.waitUntil(fn)
162
+ });
163
+ if (result.isLocked) {
164
+ context.response = await renderLockPage({
154
165
  lockPageSlug,
155
- waitUntil: (fn) => context.waitUntil(fn)
166
+ requestUrl
156
167
  });
157
- if (result.isLocked) {
158
- context.response = await renderLockPage({
159
- lockPageSlug,
160
- requestUrl
161
- });
162
- }
168
+ shouldCallNext = false;
169
+ return;
163
170
  }
164
171
  } catch (e) {
165
172
  const message = "Appwarden encountered an unknown error. Please contact Appwarden support at https://appwarden.io/join-community.";
@@ -168,6 +175,10 @@ var useAppwarden = (input) => async (context, next) => {
168
175
  e instanceof Error ? `${message} - ${e.message}` : message
169
176
  )
170
177
  );
178
+ } finally {
179
+ if (shouldCallNext) {
180
+ await next();
181
+ }
171
182
  }
172
183
  };
173
184
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appwarden/middleware",
3
- "version": "3.0.3",
3
+ "version": "3.1.1",
4
4
  "description": "Instantly shut off access your app deployed on Cloudflare or Vercel",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/vercel.js CHANGED
@@ -3,13 +3,13 @@ import {
3
3
  isValidCacheUrl
4
4
  } from "./chunk-QEFORWCW.js";
5
5
  import {
6
- isOnLockPage,
7
6
  validateConfig
8
- } from "./chunk-4JGYMZTR.js";
7
+ } from "./chunk-RDASXYYU.js";
9
8
  import {
10
9
  LockValue,
11
- MemoryCache
12
- } from "./chunk-B5IE7V77.js";
10
+ MemoryCache,
11
+ isOnLockPage
12
+ } from "./chunk-OPZQCRC4.js";
13
13
  import {
14
14
  APPWARDEN_CACHE_KEY,
15
15
  debug,
package/chunk-4JGYMZTR.js DELETED
@@ -1,50 +0,0 @@
1
- import {
2
- getErrors
3
- } from "./chunk-B5IE7V77.js";
4
- import {
5
- printMessage
6
- } from "./chunk-7UTT3M2S.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/validate-config.ts
30
- function validateConfig(config, schema) {
31
- const result = schema.safeParse(config);
32
- const hasErrors = !result.success;
33
- if (hasErrors) {
34
- const mappedErrors = getErrors(result.error);
35
- if (mappedErrors.length > 0) {
36
- for (const error of mappedErrors) {
37
- console.error(printMessage(error));
38
- }
39
- } else {
40
- console.error(printMessage(result.error.message));
41
- }
42
- }
43
- return hasErrors;
44
- }
45
-
46
- export {
47
- buildLockPageUrl,
48
- isOnLockPage,
49
- validateConfig
50
- };