@arcjet/astro 1.0.0-beta.9 → 1.1.0-rc

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/internal.js CHANGED
@@ -1,13 +1,15 @@
1
1
  import core__default from 'arcjet';
2
2
  export * from 'arcjet';
3
- import findIP, { parseProxy } from '@arcjet/ip';
4
- import ArcjetHeaders from '@arcjet/headers';
5
- import { baseUrl, isDevelopment, logLevel, platform } from '@arcjet/env';
3
+ import { readBodyWeb } from '@arcjet/body';
4
+ import findIp, { parseProxy } from '@arcjet/ip';
5
+ import { ArcjetHeaders } from '@arcjet/headers';
6
+ import { logLevel, isDevelopment, baseUrl, platform } from '@arcjet/env';
6
7
  import { Logger } from '@arcjet/logger';
7
8
  import { createClient } from '@arcjet/protocol/client.js';
8
9
  import { createTransport } from '@arcjet/transport';
9
- import { VERCEL, FLY_APP_NAME, ARCJET_LOG_LEVEL, ARCJET_KEY, ARCJET_ENV, ARCJET_BASE_URL } from 'astro:env/server';
10
+ import { VERCEL, RENDER, FLY_APP_NAME, FIREBASE_CONFIG, ARCJET_LOG_LEVEL, ARCJET_KEY, ARCJET_ENV, ARCJET_BASE_URL } from 'astro:env/server';
10
11
 
12
+ let warnedForAutomaticBodyRead = false;
11
13
  // We use a middleware to store the IP address on a `Request` with this symbol.
12
14
  // This is due to Astro inconsistently using `Symbol.for("astro.clientAddress")`
13
15
  // to store the client address and not exporting it from their module.
@@ -17,36 +19,28 @@ const env = {
17
19
  ARCJET_ENV,
18
20
  ARCJET_KEY,
19
21
  ARCJET_LOG_LEVEL,
22
+ FIREBASE_CONFIG,
20
23
  FLY_APP_NAME,
21
- VERCEL,
22
24
  // `MODE` is only set on `import.meta.env`.
23
25
  MODE: import.meta.env.MODE,
26
+ RENDER,
27
+ VERCEL,
24
28
  };
25
- // TODO: Deduplicate with other packages
26
- function errorMessage(err) {
27
- if (err) {
28
- if (typeof err === "string") {
29
- return err;
30
- }
31
- if (typeof err === "object" &&
32
- "message" in err &&
33
- typeof err.message === "string") {
34
- return err.message;
35
- }
36
- }
37
- return "Unknown problem";
38
- }
29
+ /**
30
+ * Create a remote client.
31
+ *
32
+ * @param options
33
+ * Configuration (optional).
34
+ * @returns
35
+ * Client.
36
+ */
39
37
  function createRemoteClient(options) {
40
- // The base URL for the Arcjet API. Will default to the standard production
41
- // API unless environment variable `ARCJET_BASE_URL` is set.
42
38
  const url = options?.baseUrl ?? baseUrl(env);
43
- // The timeout for the Arcjet API in milliseconds. This is set to a low value
44
- // in production so calls fail open.
45
39
  const timeout = options?.timeout ?? (isDevelopment(env) ? 1000 : 500);
46
40
  // Transport is the HTTP client that the client uses to make requests.
47
41
  const transport = createTransport(url);
48
42
  const sdkStack = "ASTRO";
49
- const sdkVersion = "1.0.0-beta.9";
43
+ const sdkVersion = "1.1.0-rc";
50
44
  return createClient({
51
45
  transport,
52
46
  baseUrl: url,
@@ -56,12 +50,16 @@ function createRemoteClient(options) {
56
50
  });
57
51
  }
58
52
  /**
59
- * Create a new {@link ArcjetAstro} client. Always build your initial client
60
- * outside of a request handler so it persists across requests. If you need to
61
- * augment a client inside a handler, call the `withRule()` function on the base
62
- * client.
53
+ * Create a new Astro integration of Arcjet.
63
54
  *
64
- * @param options - Arcjet configuration options to apply to all requests.
55
+ * @template Rules
56
+ * List of rules.
57
+ * @template Characteristics
58
+ * Characteristics to track a user by.
59
+ * @param options
60
+ * Configuration.
61
+ * @returns
62
+ * Astro integration of Arcjet.
65
63
  */
66
64
  function createArcjetClient(options) {
67
65
  const client = options.client ?? createRemoteClient();
@@ -81,14 +79,15 @@ function createArcjetClient(options) {
81
79
  if (!clientAddress) {
82
80
  throw new Error("`protect()` cannot be used in prerendered pages");
83
81
  }
84
- const cookies = request.headers.get("cookie") ?? undefined;
82
+ const cookies = request.headers.get("cookie") ?? "";
85
83
  // We construct an ArcjetHeaders to normalize over Headers
86
84
  const headers = new ArcjetHeaders(request.headers);
87
85
  const url = new URL(request.url);
88
- let ip = findIP({
89
- ip: clientAddress,
90
- headers,
91
- }, { platform: platform(env), proxies });
86
+ const xArcjetIp = isDevelopment(env)
87
+ ? headers.get("x-arcjet-ip")
88
+ : undefined;
89
+ let ip = xArcjetIp ||
90
+ findIp({ ip: clientAddress, headers }, { platform: platform(env), proxies });
92
91
  if (ip === "") {
93
92
  // If the `ip` is empty but we're in development mode, we default the IP
94
93
  // so the request doesn't fail.
@@ -112,31 +111,35 @@ function createArcjetClient(options) {
112
111
  };
113
112
  }
114
113
  function withClient(aj) {
115
- return Object.freeze({
114
+ const client = {
116
115
  withRule(rule) {
117
116
  const client = aj.withRule(rule);
118
117
  return withClient(client);
119
118
  },
120
- async protect(request, ...[props]) {
121
- // TODO(#220): The generic manipulations get really mad here, so we cast
122
- // Further investigation makes it seem like it has something to do with
123
- // the definition of `props` in the signature but it's hard to track down
124
- const req = toArcjetRequest(request, props ?? {});
119
+ async protect(request, props) {
120
+ // Cast of `{}` because here we switch from `undefined` to `Properties`.
121
+ const req = toArcjetRequest(request, props || {});
125
122
  const getBody = async () => {
126
- try {
127
- const clonedRequest = request.clone();
128
- // Awaited to throw if it rejects and we'll just return undefined
129
- const body = await clonedRequest.text();
130
- return body;
123
+ const clonedRequest = request.clone();
124
+ let expectedLength;
125
+ const expectedLengthString = request.headers.get("content-length");
126
+ if (typeof expectedLengthString === "string") {
127
+ expectedLength = parseInt(expectedLengthString, 10);
131
128
  }
132
- catch (e) {
133
- log.error("failed to get request body: %s", errorMessage(e));
134
- return;
129
+ // HEAD and GET requests do not have a body.
130
+ if (!clonedRequest.body) {
131
+ throw new Error("Cannot read body: body is missing");
135
132
  }
133
+ if (!warnedForAutomaticBodyRead) {
134
+ warnedForAutomaticBodyRead = true;
135
+ log.warn("Automatically reading the request body is deprecated; please pass an explicit `sensitiveInfoValue` field. See <https://docs.arcjet.com/upgrading/sdk-migration>.");
136
+ }
137
+ return readBodyWeb(clonedRequest.body, { expectedLength });
136
138
  };
137
139
  return aj.protect({ getBody }, req);
138
140
  },
139
- });
141
+ };
142
+ return Object.freeze(client);
140
143
  }
141
144
  const aj = core__default({ ...options, client, log });
142
145
  return withClient(aj);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcjet/astro",
3
- "version": "1.0.0-beta.9",
3
+ "version": "1.1.0-rc",
4
4
  "description": "Arcjet helps developers protect their Astro sites in just a few lines of code. Bot detection. Rate limiting. Email validation. Attack protection. Data redaction. A developer-first approach to security.",
5
5
  "keywords": [
6
6
  "analyze",
@@ -46,29 +46,30 @@
46
46
  "scripts": {
47
47
  "build": "rollup --config rollup.config.js",
48
48
  "lint": "eslint .",
49
- "prepublishOnly": "npm run build",
50
- "test": "npm run build && npm run lint"
49
+ "test-api": "node --test -- test/*.test.js",
50
+ "test-coverage": "node --experimental-test-coverage --test -- test/*.test.js",
51
+ "test": "npm run build && npm run lint && npm run test-coverage"
51
52
  },
52
53
  "dependencies": {
53
- "@arcjet/env": "1.0.0-beta.9",
54
- "@arcjet/headers": "1.0.0-beta.9",
55
- "@arcjet/ip": "1.0.0-beta.9",
56
- "@arcjet/logger": "1.0.0-beta.9",
57
- "@arcjet/protocol": "1.0.0-beta.9",
58
- "@arcjet/transport": "1.0.0-beta.9",
59
- "arcjet": "1.0.0-beta.9"
54
+ "@arcjet/body": "1.1.0-rc",
55
+ "@arcjet/env": "1.1.0-rc",
56
+ "@arcjet/headers": "1.1.0-rc",
57
+ "@arcjet/ip": "1.1.0-rc",
58
+ "@arcjet/logger": "1.1.0-rc",
59
+ "@arcjet/protocol": "1.1.0-rc",
60
+ "@arcjet/transport": "1.1.0-rc",
61
+ "arcjet": "1.1.0-rc"
60
62
  },
61
63
  "peerDependencies": {
62
64
  "astro": "^5.9.3"
63
65
  },
64
66
  "devDependencies": {
65
- "@arcjet/eslint-config": "1.0.0-beta.9",
66
- "@arcjet/rollup-config": "1.0.0-beta.9",
67
- "@arcjet/tsconfig": "1.0.0-beta.9",
68
- "@rollup/wasm-node": "4.44.2",
69
- "astro": "5.11.0",
70
- "eslint": "9.30.1",
71
- "typescript": "5.8.3"
67
+ "@arcjet/eslint-config": "1.1.0-rc",
68
+ "@arcjet/rollup-config": "1.1.0-rc",
69
+ "@rollup/wasm-node": "4.57.0",
70
+ "astro": "5.16.15",
71
+ "eslint": "9.39.2",
72
+ "typescript": "5.9.3"
72
73
  },
73
74
  "publishConfig": {
74
75
  "access": "public",