@arcjet/node 1.0.0-alpha.17 → 1.0.0-alpha.19

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.
Files changed (4) hide show
  1. package/index.d.ts +2 -2
  2. package/index.js +78 -74
  3. package/index.ts +108 -100
  4. package/package.json +12 -12
package/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ArcjetDecision, ArcjetOptions, Primitive, Product, ExtraProps } from "arcjet";
1
+ import type { ArcjetDecision, ArcjetOptions, Primitive, Product, ExtraProps, CharacteristicProps } from "arcjet";
2
2
  export * from "arcjet";
3
3
  type Simplify<T> = {
4
4
  [KeyType in keyof T]: T[KeyType];
@@ -57,4 +57,4 @@ export interface ArcjetNode<Props extends PlainObject> {
57
57
  *
58
58
  * @param options - Arcjet configuration options to apply to all requests.
59
59
  */
60
- export default function arcjet<const Rules extends (Primitive | Product)[]>(options: ArcjetOptions<Rules>): ArcjetNode<Simplify<ExtraProps<Rules>>>;
60
+ export default function arcjet<const Rules extends (Primitive | Product)[], const Characteristics extends readonly string[]>(options: ArcjetOptions<Rules, Characteristics>): ArcjetNode<Simplify<ExtraProps<Rules> & CharacteristicProps<Characteristics>>>;
package/index.js CHANGED
@@ -3,7 +3,7 @@ import core__default from 'arcjet';
3
3
  export * from 'arcjet';
4
4
  import findIP from '@arcjet/ip';
5
5
  import ArcjetHeaders from '@arcjet/headers';
6
- import { logLevel, baseUrl, isProduction, platform, isDevelopment } from '@arcjet/env';
6
+ import { logLevel, baseUrl, isDevelopment, platform } from '@arcjet/env';
7
7
  import { Logger } from '@arcjet/logger';
8
8
  import { createClient } from '@arcjet/protocol/client.js';
9
9
 
@@ -13,14 +13,14 @@ function createRemoteClient(options) {
13
13
  const url = options?.baseUrl ?? baseUrl(process.env);
14
14
  // The timeout for the Arcjet API in milliseconds. This is set to a low value
15
15
  // in production so calls fail open.
16
- const timeout = options?.timeout ?? (isProduction(process.env) ? 500 : 1000);
16
+ const timeout = options?.timeout ?? (isDevelopment(process.env) ? 1000 : 500);
17
17
  // Transport is the HTTP client that the client uses to make requests.
18
18
  const transport = createConnectTransport({
19
19
  baseUrl: url,
20
20
  httpVersion: "2",
21
21
  });
22
22
  const sdkStack = "NODEJS";
23
- const sdkVersion = "1.0.0-alpha.17";
23
+ const sdkVersion = "1.0.0-alpha.19";
24
24
  return createClient({
25
25
  transport,
26
26
  baseUrl: url,
@@ -39,77 +39,6 @@ function cookiesToString(cookies) {
39
39
  }
40
40
  return cookies;
41
41
  }
42
- function toArcjetRequest(request, props) {
43
- // We pull the cookies from the request before wrapping them in ArcjetHeaders
44
- const cookies = cookiesToString(request.headers?.cookie);
45
- // We construct an ArcjetHeaders to normalize over Headers
46
- const headers = new ArcjetHeaders(request.headers);
47
- let ip = findIP(request, headers, { platform: platform(process.env) });
48
- if (ip === "") {
49
- // If the `ip` is empty but we're in development mode, we default the IP
50
- // so the request doesn't fail.
51
- if (isDevelopment(process.env)) {
52
- // TODO: Log that the fingerprint is being overridden once the adapter
53
- // constructs the logger
54
- ip = "127.0.0.1";
55
- }
56
- }
57
- const method = request.method ?? "";
58
- const host = headers.get("host") ?? "";
59
- let path = "";
60
- let query = "";
61
- let protocol = "";
62
- if (typeof request.socket?.encrypted !== "undefined") {
63
- protocol = request.socket.encrypted ? "https:" : "http:";
64
- }
65
- else {
66
- protocol = "http:";
67
- }
68
- // Do some very simple validation, but also try/catch around URL parsing
69
- if (typeof request.url !== "undefined" && request.url !== "" && host !== "") {
70
- try {
71
- const url = new URL(request.url, `${protocol}//${host}`);
72
- path = url.pathname;
73
- query = url.search;
74
- protocol = url.protocol;
75
- }
76
- catch {
77
- // If the parsing above fails, just set the path as whatever url we
78
- // received.
79
- // TODO(#216): Add logging to arcjet-node
80
- path = request.url ?? "";
81
- }
82
- }
83
- else {
84
- path = request.url ?? "";
85
- }
86
- return {
87
- ...props,
88
- ip,
89
- method,
90
- protocol,
91
- host,
92
- path,
93
- headers,
94
- cookies,
95
- query,
96
- };
97
- }
98
- function withClient(aj) {
99
- return Object.freeze({
100
- withRule(rule) {
101
- const client = aj.withRule(rule);
102
- return withClient(client);
103
- },
104
- async protect(request, ...[props]) {
105
- // TODO(#220): The generic manipulations get really mad here, so we cast
106
- // Further investigation makes it seem like it has something to do with
107
- // the definition of `props` in the signature but it's hard to track down
108
- const req = toArcjetRequest(request, props ?? {});
109
- return aj.protect({}, req);
110
- },
111
- });
112
- }
113
42
  /**
114
43
  * Create a new {@link ArcjetNode} client. Always build your initial client
115
44
  * outside of a request handler so it persists across requests. If you need to
@@ -125,6 +54,81 @@ function arcjet(options) {
125
54
  : new Logger({
126
55
  level: logLevel(process.env),
127
56
  });
57
+ function toArcjetRequest(request, props) {
58
+ // We pull the cookies from the request before wrapping them in ArcjetHeaders
59
+ const cookies = cookiesToString(request.headers?.cookie);
60
+ // We construct an ArcjetHeaders to normalize over Headers
61
+ const headers = new ArcjetHeaders(request.headers);
62
+ let ip = findIP(request, headers, { platform: platform(process.env) });
63
+ if (ip === "") {
64
+ // If the `ip` is empty but we're in development mode, we default the IP
65
+ // so the request doesn't fail.
66
+ if (isDevelopment(process.env)) {
67
+ log.warn("Using 127.0.0.1 as IP address in development mode");
68
+ ip = "127.0.0.1";
69
+ }
70
+ else {
71
+ log.warn(`Client IP address is missing. If this is a dev environment set the ARCJET_ENV env var to "development"`);
72
+ }
73
+ }
74
+ const method = request.method ?? "";
75
+ const host = headers.get("host") ?? "";
76
+ let path = "";
77
+ let query = "";
78
+ let protocol = "";
79
+ if (typeof request.socket?.encrypted !== "undefined") {
80
+ protocol = request.socket.encrypted ? "https:" : "http:";
81
+ }
82
+ else {
83
+ protocol = "http:";
84
+ }
85
+ // Do some very simple validation, but also try/catch around URL parsing
86
+ if (typeof request.url !== "undefined" &&
87
+ request.url !== "" &&
88
+ host !== "") {
89
+ try {
90
+ const url = new URL(request.url, `${protocol}//${host}`);
91
+ path = url.pathname;
92
+ query = url.search;
93
+ protocol = url.protocol;
94
+ }
95
+ catch {
96
+ // If the parsing above fails, just set the path as whatever url we
97
+ // received.
98
+ path = request.url ?? "";
99
+ log.warn('Unable to parse URL. Using "%s" as `path`.', path);
100
+ }
101
+ }
102
+ else {
103
+ path = request.url ?? "";
104
+ }
105
+ return {
106
+ ...props,
107
+ ip,
108
+ method,
109
+ protocol,
110
+ host,
111
+ path,
112
+ headers,
113
+ cookies,
114
+ query,
115
+ };
116
+ }
117
+ function withClient(aj) {
118
+ return Object.freeze({
119
+ withRule(rule) {
120
+ const client = aj.withRule(rule);
121
+ return withClient(client);
122
+ },
123
+ async protect(request, ...[props]) {
124
+ // TODO(#220): The generic manipulations get really mad here, so we cast
125
+ // Further investigation makes it seem like it has something to do with
126
+ // the definition of `props` in the signature but it's hard to track down
127
+ const req = toArcjetRequest(request, props ?? {});
128
+ return aj.protect({}, req);
129
+ },
130
+ });
131
+ }
128
132
  const aj = core__default({ ...options, client, log });
129
133
  return withClient(aj);
130
134
  }
package/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createConnectTransport } from "@connectrpc/connect-node";
2
- import core, {
2
+ import core from "arcjet";
3
+ import type {
3
4
  ArcjetDecision,
4
5
  ArcjetOptions,
5
6
  Primitive,
@@ -7,16 +8,11 @@ import core, {
7
8
  ArcjetRequest,
8
9
  ExtraProps,
9
10
  Arcjet,
11
+ CharacteristicProps,
10
12
  } from "arcjet";
11
13
  import findIP from "@arcjet/ip";
12
14
  import ArcjetHeaders from "@arcjet/headers";
13
- import {
14
- baseUrl,
15
- isDevelopment,
16
- isProduction,
17
- logLevel,
18
- platform,
19
- } from "@arcjet/env";
15
+ import { baseUrl, isDevelopment, logLevel, platform } from "@arcjet/env";
20
16
  import { Logger } from "@arcjet/logger";
21
17
  import { createClient } from "@arcjet/protocol/client.js";
22
18
 
@@ -72,7 +68,7 @@ export function createRemoteClient(options?: RemoteClientOptions) {
72
68
 
73
69
  // The timeout for the Arcjet API in milliseconds. This is set to a low value
74
70
  // in production so calls fail open.
75
- const timeout = options?.timeout ?? (isProduction(process.env) ? 500 : 1000);
71
+ const timeout = options?.timeout ?? (isDevelopment(process.env) ? 1000 : 500);
76
72
 
77
73
  // Transport is the HTTP client that the client uses to make requests.
78
74
  const transport = createConnectTransport({
@@ -148,94 +144,6 @@ export interface ArcjetNode<Props extends PlainObject> {
148
144
  ): ArcjetNode<Simplify<Props & ExtraProps<Rule>>>;
149
145
  }
150
146
 
151
- function toArcjetRequest<Props extends PlainObject>(
152
- request: ArcjetNodeRequest,
153
- props: Props,
154
- ): ArcjetRequest<Props> {
155
- // We pull the cookies from the request before wrapping them in ArcjetHeaders
156
- const cookies = cookiesToString(request.headers?.cookie);
157
-
158
- // We construct an ArcjetHeaders to normalize over Headers
159
- const headers = new ArcjetHeaders(request.headers);
160
-
161
- let ip = findIP(request, headers, { platform: platform(process.env) });
162
- if (ip === "") {
163
- // If the `ip` is empty but we're in development mode, we default the IP
164
- // so the request doesn't fail.
165
- if (isDevelopment(process.env)) {
166
- // TODO: Log that the fingerprint is being overridden once the adapter
167
- // constructs the logger
168
- ip = "127.0.0.1";
169
- }
170
- }
171
- const method = request.method ?? "";
172
- const host = headers.get("host") ?? "";
173
- let path = "";
174
- let query = "";
175
- let protocol = "";
176
-
177
- if (typeof request.socket?.encrypted !== "undefined") {
178
- protocol = request.socket.encrypted ? "https:" : "http:";
179
- } else {
180
- protocol = "http:";
181
- }
182
-
183
- // Do some very simple validation, but also try/catch around URL parsing
184
- if (typeof request.url !== "undefined" && request.url !== "" && host !== "") {
185
- try {
186
- const url = new URL(request.url, `${protocol}//${host}`);
187
- path = url.pathname;
188
- query = url.search;
189
- protocol = url.protocol;
190
- } catch {
191
- // If the parsing above fails, just set the path as whatever url we
192
- // received.
193
- // TODO(#216): Add logging to arcjet-node
194
- path = request.url ?? "";
195
- }
196
- } else {
197
- path = request.url ?? "";
198
- }
199
-
200
- return {
201
- ...props,
202
- ip,
203
- method,
204
- protocol,
205
- host,
206
- path,
207
- headers,
208
- cookies,
209
- query,
210
- };
211
- }
212
-
213
- function withClient<const Rules extends (Primitive | Product)[]>(
214
- aj: Arcjet<ExtraProps<Rules>>,
215
- ): ArcjetNode<ExtraProps<Rules>> {
216
- return Object.freeze({
217
- withRule(rule: Primitive | Product) {
218
- const client = aj.withRule(rule);
219
- return withClient(client);
220
- },
221
- async protect(
222
- request: ArcjetNodeRequest,
223
- ...[props]: ExtraProps<Rules> extends WithoutCustomProps
224
- ? []
225
- : [ExtraProps<Rules>]
226
- ): Promise<ArcjetDecision> {
227
- // TODO(#220): The generic manipulations get really mad here, so we cast
228
- // Further investigation makes it seem like it has something to do with
229
- // the definition of `props` in the signature but it's hard to track down
230
- const req = toArcjetRequest(request, props ?? {}) as ArcjetRequest<
231
- ExtraProps<Rules>
232
- >;
233
-
234
- return aj.protect({}, req);
235
- },
236
- });
237
- }
238
-
239
147
  /**
240
148
  * Create a new {@link ArcjetNode} client. Always build your initial client
241
149
  * outside of a request handler so it persists across requests. If you need to
@@ -244,9 +152,14 @@ function withClient<const Rules extends (Primitive | Product)[]>(
244
152
  *
245
153
  * @param options - Arcjet configuration options to apply to all requests.
246
154
  */
247
- export default function arcjet<const Rules extends (Primitive | Product)[]>(
248
- options: ArcjetOptions<Rules>,
249
- ): ArcjetNode<Simplify<ExtraProps<Rules>>> {
155
+ export default function arcjet<
156
+ const Rules extends (Primitive | Product)[],
157
+ const Characteristics extends readonly string[],
158
+ >(
159
+ options: ArcjetOptions<Rules, Characteristics>,
160
+ ): ArcjetNode<
161
+ Simplify<ExtraProps<Rules> & CharacteristicProps<Characteristics>>
162
+ > {
250
163
  const client = options.client ?? createRemoteClient();
251
164
 
252
165
  const log = options.log
@@ -255,6 +168,101 @@ export default function arcjet<const Rules extends (Primitive | Product)[]>(
255
168
  level: logLevel(process.env),
256
169
  });
257
170
 
171
+ function toArcjetRequest<Props extends PlainObject>(
172
+ request: ArcjetNodeRequest,
173
+ props: Props,
174
+ ): ArcjetRequest<Props> {
175
+ // We pull the cookies from the request before wrapping them in ArcjetHeaders
176
+ const cookies = cookiesToString(request.headers?.cookie);
177
+
178
+ // We construct an ArcjetHeaders to normalize over Headers
179
+ const headers = new ArcjetHeaders(request.headers);
180
+
181
+ let ip = findIP(request, headers, { platform: platform(process.env) });
182
+ if (ip === "") {
183
+ // If the `ip` is empty but we're in development mode, we default the IP
184
+ // so the request doesn't fail.
185
+ if (isDevelopment(process.env)) {
186
+ log.warn("Using 127.0.0.1 as IP address in development mode");
187
+ ip = "127.0.0.1";
188
+ } else {
189
+ log.warn(
190
+ `Client IP address is missing. If this is a dev environment set the ARCJET_ENV env var to "development"`,
191
+ );
192
+ }
193
+ }
194
+ const method = request.method ?? "";
195
+ const host = headers.get("host") ?? "";
196
+ let path = "";
197
+ let query = "";
198
+ let protocol = "";
199
+
200
+ if (typeof request.socket?.encrypted !== "undefined") {
201
+ protocol = request.socket.encrypted ? "https:" : "http:";
202
+ } else {
203
+ protocol = "http:";
204
+ }
205
+
206
+ // Do some very simple validation, but also try/catch around URL parsing
207
+ if (
208
+ typeof request.url !== "undefined" &&
209
+ request.url !== "" &&
210
+ host !== ""
211
+ ) {
212
+ try {
213
+ const url = new URL(request.url, `${protocol}//${host}`);
214
+ path = url.pathname;
215
+ query = url.search;
216
+ protocol = url.protocol;
217
+ } catch {
218
+ // If the parsing above fails, just set the path as whatever url we
219
+ // received.
220
+ path = request.url ?? "";
221
+ log.warn('Unable to parse URL. Using "%s" as `path`.', path);
222
+ }
223
+ } else {
224
+ path = request.url ?? "";
225
+ }
226
+
227
+ return {
228
+ ...props,
229
+ ip,
230
+ method,
231
+ protocol,
232
+ host,
233
+ path,
234
+ headers,
235
+ cookies,
236
+ query,
237
+ };
238
+ }
239
+
240
+ function withClient<const Rules extends (Primitive | Product)[]>(
241
+ aj: Arcjet<ExtraProps<Rules>>,
242
+ ): ArcjetNode<ExtraProps<Rules>> {
243
+ return Object.freeze({
244
+ withRule(rule: Primitive | Product) {
245
+ const client = aj.withRule(rule);
246
+ return withClient(client);
247
+ },
248
+ async protect(
249
+ request: ArcjetNodeRequest,
250
+ ...[props]: ExtraProps<Rules> extends WithoutCustomProps
251
+ ? []
252
+ : [ExtraProps<Rules>]
253
+ ): Promise<ArcjetDecision> {
254
+ // TODO(#220): The generic manipulations get really mad here, so we cast
255
+ // Further investigation makes it seem like it has something to do with
256
+ // the definition of `props` in the signature but it's hard to track down
257
+ const req = toArcjetRequest(request, props ?? {}) as ArcjetRequest<
258
+ ExtraProps<Rules>
259
+ >;
260
+
261
+ return aj.protect({}, req);
262
+ },
263
+ });
264
+ }
265
+
258
266
  const aj = core({ ...options, client, log });
259
267
 
260
268
  return withClient(aj);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcjet/node",
3
- "version": "1.0.0-alpha.17",
3
+ "version": "1.0.0-alpha.19",
4
4
  "description": "Arcjet SDK for Node.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://arcjet.com",
@@ -40,23 +40,23 @@
40
40
  "test": "NODE_OPTIONS=--experimental-vm-modules jest --passWithNoTests"
41
41
  },
42
42
  "dependencies": {
43
- "@arcjet/env": "1.0.0-alpha.17",
44
- "@arcjet/headers": "1.0.0-alpha.17",
45
- "@arcjet/ip": "1.0.0-alpha.17",
46
- "@arcjet/logger": "1.0.0-alpha.17",
47
- "@arcjet/protocol": "1.0.0-alpha.17",
43
+ "@arcjet/env": "1.0.0-alpha.19",
44
+ "@arcjet/headers": "1.0.0-alpha.19",
45
+ "@arcjet/ip": "1.0.0-alpha.19",
46
+ "@arcjet/logger": "1.0.0-alpha.19",
47
+ "@arcjet/protocol": "1.0.0-alpha.19",
48
48
  "@connectrpc/connect-node": "1.4.0",
49
- "arcjet": "1.0.0-alpha.17"
49
+ "arcjet": "1.0.0-alpha.19"
50
50
  },
51
51
  "devDependencies": {
52
- "@arcjet/eslint-config": "1.0.0-alpha.17",
53
- "@arcjet/rollup-config": "1.0.0-alpha.17",
54
- "@arcjet/tsconfig": "1.0.0-alpha.17",
52
+ "@arcjet/eslint-config": "1.0.0-alpha.19",
53
+ "@arcjet/rollup-config": "1.0.0-alpha.19",
54
+ "@arcjet/tsconfig": "1.0.0-alpha.19",
55
55
  "@jest/globals": "29.7.0",
56
56
  "@types/node": "18.18.0",
57
- "@rollup/wasm-node": "4.18.0",
57
+ "@rollup/wasm-node": "4.18.1",
58
58
  "jest": "29.7.0",
59
- "typescript": "5.4.5"
59
+ "typescript": "5.5.3"
60
60
  },
61
61
  "publishConfig": {
62
62
  "access": "public",