@appwarden/middleware 3.7.0 → 3.9.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-91.5%25-brightgreen)
7
+ ![Test Coverage](https://img.shields.io/badge/coverage-92.72%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
@@ -39,11 +39,13 @@ The path or route (for example, `/maintenance`) to redirect users to when the do
39
39
  This should be a working page on your site, such as a maintenance or status page, that
40
40
  explains why the website is temporarily unavailable.
41
41
 
42
- ### `contentSecurityPolicy`
42
+ ### `contentSecurityPolicy` (optional)
43
43
 
44
- Controls the [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) headers that Appwarden adds.
44
+ Controls the [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) headers that Appwarden adds. This configuration is optional—if not provided, no CSP header will be applied.
45
45
 
46
- - `mode` (optional) controls how the CSP is applied:
46
+ When provided, both `mode` and `directives` are required:
47
+
48
+ - `mode` controls how the CSP is applied:
47
49
  - `"disabled"` – no CSP header is sent.
48
50
  - `"report-only"` – sends the `Content-Security-Policy-Report-Only` header so violations are
49
51
  reported (for example in the browser console) but not blocked.
@@ -52,7 +54,7 @@ Controls the [Content Security Policy](https://developer.mozilla.org/en-US/docs/
52
54
  When developing or iterating on your CSP, we recommend starting with `"report-only"` so you can
53
55
  identify and fix violations before switching to `"enforced"`.
54
56
 
55
- - `directives` (optional) is an object whose keys are CSP directive names and whose values are
57
+ - `directives` is an object whose keys are CSP directive names and whose values are
56
58
  arrays of allowed sources. For example:
57
59
 
58
60
  ```ts
@@ -60,6 +62,7 @@ Controls the [Content Security Policy](https://developer.mozilla.org/en-US/docs/
60
62
  mode: "enforced",
61
63
  directives: {
62
64
  "script-src": ["'self'", "{{nonce}}"],
65
+ "style-src": ["'self'", "{{nonce}}"],
63
66
  },
64
67
  }
65
68
  ```
@@ -108,7 +111,7 @@ The **Universal Middleware** (`@appwarden/middleware/cloudflare`) is the recomme
108
111
  import { createAppwardenMiddleware } from "@appwarden/middleware/cloudflare"
109
112
 
110
113
  const appwardenHandler = createAppwardenMiddleware((cloudflare) => ({
111
- debug: cloudflare.env.DEBUG === "true",
114
+ debug: cloudflare.env.DEBUG,
112
115
  lockPageSlug: cloudflare.env.LOCK_PAGE_SLUG,
113
116
  appwardenApiToken: cloudflare.env.APPWARDEN_API_TOKEN,
114
117
  contentSecurityPolicy: {
@@ -142,12 +145,12 @@ import { createAppwardenMiddleware } from "@appwarden/middleware/cloudflare/astr
142
145
  const appwarden = createAppwardenMiddleware((cloudflare) => ({
143
146
  lockPageSlug: cloudflare.env.APPWARDEN_LOCK_PAGE_SLUG,
144
147
  appwardenApiToken: cloudflare.env.APPWARDEN_API_TOKEN,
145
- debug: cloudflare.env.APPWARDEN_DEBUG === "true",
148
+ debug: cloudflare.env.DEBUG,
146
149
  contentSecurityPolicy: {
147
- mode: "enforced",
150
+ // See Configuration > contentSecurityPolicy section for details
151
+ mode: "report-only",
148
152
  directives: {
149
- "script-src": ["'self'", "{{nonce}}"],
150
- "style-src": ["'self'", "{{nonce}}"],
153
+ "default-src": ["'self'"],
151
154
  },
152
155
  },
153
156
  }))
@@ -159,57 +162,59 @@ See the [Astro + Cloudflare guide](https://appwarden.io/docs/guides/astro-cloudf
159
162
 
160
163
  ##### React Router on Cloudflare
161
164
 
165
+ - Set `future.v8_middleware: true` in your `react-router.config.ts` file
166
+
162
167
  ```ts
163
168
  // app/root.tsx
169
+ import { env } from "cloudflare:workers"
164
170
  import { createAppwardenMiddleware } from "@appwarden/middleware/cloudflare/react-router"
165
- import type { CloudflareContext } from "@appwarden/middleware/cloudflare/react-router"
166
171
 
167
- export const unstable_middleware = [
168
- createAppwardenMiddleware((cloudflare: CloudflareContext) => ({
169
- lockPageSlug: cloudflare.env.APPWARDEN_LOCK_PAGE_SLUG,
170
- appwardenApiToken: cloudflare.env.APPWARDEN_API_TOKEN,
171
- debug: cloudflare.env.APPWARDEN_DEBUG === "true",
172
+ export const middleware = [
173
+ createAppwardenMiddleware(() => ({
174
+ lockPageSlug: env.APPWARDEN_LOCK_PAGE_SLUG,
175
+ appwardenApiToken: env.APPWARDEN_API_TOKEN,
176
+ // "debug" can be a string or boolean; the schema will normalize it
177
+ debug: env.DEBUG,
178
+ // "directives" can be a JSON string or an object; the schema will parse it
172
179
  contentSecurityPolicy: {
173
- mode: "enforced",
180
+ // See Configuration > contentSecurityPolicy section for details
181
+ mode: "report-only",
174
182
  directives: {
175
- "script-src": ["'self'", "{{nonce}}"],
176
- "style-src": ["'self'", "{{nonce}}"],
183
+ "default-src": ["'self'"],
177
184
  },
178
185
  },
179
186
  })),
180
187
  ]
181
188
  ```
182
189
 
183
- See the [React Router + Cloudflare guide](https://appwarden.io/docs/guides/react-router-cloudflare) for full usage and context setup.
190
+ See the [React Router + Cloudflare guide](https://appwarden.io/docs/guides/react-router-cloudflare) for more details.
184
191
 
185
192
  ##### TanStack Start on Cloudflare
186
193
 
187
194
  ```ts
188
195
  // start.ts
189
196
  import { createMiddleware } from "@tanstack/start"
190
- import { env, waitUntil } from "cloudflare:workers"
197
+ import { env } from "cloudflare:workers"
191
198
  import { createAppwardenMiddleware } from "@appwarden/middleware/cloudflare/tanstack-start"
192
199
 
193
- const appwardenMiddleware = createAppwardenMiddleware(({ env }) => ({
194
- lockPageSlug: env.APPWARDEN_LOCK_PAGE_SLUG,
195
- appwardenApiToken: env.APPWARDEN_API_TOKEN,
196
- debug: env.APPWARDEN_DEBUG, // Accepts string or boolean
197
- contentSecurityPolicy: {
198
- mode: "enforced",
199
- directives: {
200
- "script-src": ["'self'", "{{nonce}}"],
201
- "style-src": ["'self'", "{{nonce}}"],
200
+ const appwardenMiddleware = createMiddleware().server(
201
+ createAppwardenMiddleware(() => ({
202
+ lockPageSlug: env.APPWARDEN_LOCK_PAGE_SLUG,
203
+ appwardenApiToken: env.APPWARDEN_API_TOKEN,
204
+ debug: env.DEBUG, // Accepts string or boolean
205
+ contentSecurityPolicy: {
206
+ // See Configuration > contentSecurityPolicy section for details
207
+ mode: "report-only",
208
+ directives: {
209
+ "default-src": ["'self'"],
210
+ },
202
211
  },
203
- },
204
- }))
212
+ })),
213
+ )
205
214
 
206
- export default createMiddleware().server(async ({ next, request }) => {
207
- return await appwardenMiddleware({
208
- request,
209
- next,
210
- context: { env, waitUntil },
211
- })
212
- })
215
+ export const startInstance = createStart(() => ({
216
+ requestMiddleware: [appwardenMiddleware],
217
+ }))
213
218
  ```
214
219
 
215
220
  See the [TanStack Start + Cloudflare guide](https://appwarden.io/docs/guides/tanstack-start-cloudflare) for more details.
@@ -227,13 +232,13 @@ export const config = {
227
232
  export default createAppwardenMiddleware((cloudflare) => ({
228
233
  lockPageSlug: cloudflare.env.APPWARDEN_LOCK_PAGE_SLUG,
229
234
  appwardenApiToken: cloudflare.env.APPWARDEN_API_TOKEN,
230
- debug: cloudflare.env.APPWARDEN_DEBUG === "true",
235
+ debug: cloudflare.env.DEBUG,
231
236
  // Headers-only CSP (no HTML rewriting, no nonce support; do not use `{{nonce}}` here)
232
237
  contentSecurityPolicy: {
233
- mode: "report-only",
238
+ // See Configuration > contentSecurityPolicy section for details
239
+ mode: "enforced",
234
240
  directives: {
235
- "script-src": ["'self'"],
236
- "style-src": ["'self'", "'unsafe-inline'"],
241
+ "default-src": ["'self'"],
237
242
  },
238
243
  },
239
244
  }))
@@ -248,25 +253,22 @@ To use Appwarden as Vercel Edge Middleware, use the `@appwarden/middleware/verce
248
253
  ```ts
249
254
  // middleware.ts (Next.js app on Vercel)
250
255
  import { createAppwardenMiddleware } from "@appwarden/middleware/vercel"
251
- import type { VercelMiddlewareFunction } from "@appwarden/middleware/vercel"
252
-
253
- const appwardenMiddleware: VercelMiddlewareFunction = createAppwardenMiddleware(
254
- {
255
- // Edge Config or Upstash KV URL
256
- cacheUrl: process.env.APPWARDEN_CACHE_URL!,
257
- // Required when using Vercel Edge Config
258
- vercelApiToken: process.env.APPWARDEN_VERCEL_API_TOKEN!,
259
- appwardenApiToken: process.env.APPWARDEN_API_TOKEN!,
260
- lockPageSlug: "/maintenance",
261
- contentSecurityPolicy: {
262
- mode: "report-only",
263
- directives: {
264
- "script-src": ["'self'"],
265
- "style-src": ["'self'", "'unsafe-inline'"],
266
- },
256
+
257
+ const appwardenMiddleware = createAppwardenMiddleware({
258
+ // Edge Config or Upstash KV URL
259
+ cacheUrl: process.env.APPWARDEN_CACHE_URL!,
260
+ // Required when using Vercel Edge Config
261
+ vercelApiToken: process.env.APPWARDEN_VERCEL_API_TOKEN!,
262
+ appwardenApiToken: process.env.APPWARDEN_API_TOKEN!,
263
+ lockPageSlug: "/maintenance",
264
+ contentSecurityPolicy: {
265
+ // See Configuration > contentSecurityPolicy section for details
266
+ mode: "report-only",
267
+ directives: {
268
+ "default-src": ["'self'"],
267
269
  },
268
270
  },
269
- )
271
+ })
270
272
 
271
273
  export default appwardenMiddleware
272
274
  ```
@@ -295,7 +297,6 @@ Contributions are welcome! Please feel free to submit a Pull Request.
295
297
  - `fix: resolve issue with X`
296
298
  - `docs: update README`
297
299
  - `chore: update dependencies`
298
- - `refactor: improve code structure`
299
300
  - `test: add tests for feature X`
300
301
  4. Push to the branch (`git push origin feature/amazing-feature`)
301
302
  5. Open a Pull Request
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  MemoryCache,
3
3
  debug
4
- } from "./chunk-EPJ4ZVO6.js";
4
+ } from "./chunk-Z7FIMIZS.js";
5
5
  import {
6
6
  APPWARDEN_CACHE_KEY,
7
7
  APPWARDEN_TEST_ROUTE
8
- } from "./chunk-HCGLR3Z3.js";
8
+ } from "./chunk-UIIYORBW.js";
9
9
  import {
10
10
  deleteEdgeValue,
11
11
  getLockValue,
12
12
  store,
13
13
  syncEdgeValue
14
- } from "./chunk-GK6JL5NZ.js";
14
+ } from "./chunk-QGXPAVOA.js";
15
15
 
16
16
  // src/core/check-lock-status.ts
17
17
  var createContext = async (config) => {
@@ -116,8 +116,7 @@ var AppwardenApiTokenSchema = z.string().refine((val) => !!val, { message: "appw
116
116
  var LockValue = z.object({
117
117
  isLocked: z.number(),
118
118
  isLockedTest: z.number(),
119
- lastCheck: z.number(),
120
- code: z.string()
119
+ lastCheck: z.number()
121
120
  });
122
121
 
123
122
  // src/utils/errors.ts
@@ -160,8 +159,7 @@ var getLockValue = async (context) => {
160
159
  let cacheResponse, lockValue = {
161
160
  isLocked: 0,
162
161
  isLockedTest: 0,
163
- lastCheck: Date.now(),
164
- code: ""
162
+ lastCheck: Date.now()
165
163
  };
166
164
  switch (context.provider) {
167
165
  case "cloudflare-cache": {
@@ -259,7 +257,7 @@ var syncEdgeValue = async (context) => {
259
257
  const apiHostname = context.appwardenApiHostname ?? DEFAULT_API_HOSTNAME;
260
258
  context.debug(`GET ${apiHostname}`);
261
259
  try {
262
- const response = await fetch(new URL("/v1/status/check", apiHostname), {
260
+ const response = await fetch(new URL("/v1/appwarden/status", apiHostname), {
263
261
  method: "POST",
264
262
  headers: { "content-type": "application/json" },
265
263
  body: JSON.stringify({
@@ -57,10 +57,10 @@ var CSPModeSchema = z2.union([
57
57
  z2.literal("disabled"),
58
58
  z2.literal("report-only"),
59
59
  z2.literal("enforced")
60
- ]).optional().default("disabled");
60
+ ]);
61
61
  var UseCSPInputSchema = z2.object({
62
62
  mode: CSPModeSchema,
63
- directives: CSPDirectivesSchema.optional().refine(
63
+ directives: CSPDirectivesSchema.refine(
64
64
  (val) => {
65
65
  try {
66
66
  if (typeof val === "string") {
@@ -75,13 +75,7 @@ var UseCSPInputSchema = z2.object({
75
75
  ).transform(
76
76
  (val) => typeof val === "string" ? JSON.parse(val) : val
77
77
  )
78
- }).refine(
79
- (values) => (
80
- // validate that directives are provided when the mode is "report-only" or "enforced"
81
- ["report-only", "enforced"].includes(values.mode) ? !!values.directives : true
82
- ),
83
- { path: ["directives"], message: "DirectivesRequired" /* DirectivesRequired */ }
84
- );
78
+ });
85
79
 
86
80
  export {
87
81
  LOCKDOWN_TEST_EXPIRY_MS,
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  UseCSPInputSchema,
3
3
  isHTMLResponse
4
- } from "./chunk-HCGLR3Z3.js";
4
+ } from "./chunk-UIIYORBW.js";
5
5
  import {
6
6
  makeCSPHeader
7
- } from "./chunk-GK6JL5NZ.js";
7
+ } from "./chunk-QGXPAVOA.js";
8
8
 
9
9
  // src/middlewares/use-content-security-policy.ts
10
10
  var AppendAttribute = (attribute, nonce) => ({
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  LOCKDOWN_TEST_EXPIRY_MS
3
- } from "./chunk-HCGLR3Z3.js";
3
+ } from "./chunk-UIIYORBW.js";
4
4
  import {
5
5
  printMessage
6
- } from "./chunk-GK6JL5NZ.js";
6
+ } from "./chunk-QGXPAVOA.js";
7
7
 
8
8
  // src/utils/build-lock-page-url.ts
9
9
  function normalizeLockPageSlug(lockPageSlug) {