@arcjet/analyze 1.0.0-beta.9 → 1.0.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.
Files changed (4) hide show
  1. package/README.md +48 -24
  2. package/index.d.ts +75 -9
  3. package/index.js +93 -14
  4. package/package.json +12 -13
package/README.md CHANGED
@@ -25,43 +25,67 @@ This is the [Arcjet][arcjet] local analysis engine.
25
25
  - [npm package (`@arcjet/analyze`)](https://www.npmjs.com/package/@arcjet/analyze)
26
26
  - [GitHub source code (`analyze/` in `arcjet/arcjet-js`)](https://github.com/arcjet/arcjet-js/tree/main/analyze)
27
27
 
28
- ## Installation
28
+ ## What is this?
29
29
 
30
- ```shell
31
- npm install -S @arcjet/analyze
32
- ```
30
+ This package provides functionality to analyze requests.
31
+ The work is done in WebAssembly but is called here from JavaScript.
32
+ The functionality is wrapped up into rules in our core package
33
+ ([`arcjet`][github-arcjet-arcjet]),
34
+ in turn exposed from our [SDKs][github-arcjet-sdks] (such as `@arcjet/next`).
33
35
 
34
- ## Example
36
+ The WebAssembly files are in
37
+ [`@arcjet/analyze-wasm`][github-arcjet-analyze-wasm].
38
+ They are separate because we need to change the import structure for each
39
+ runtime that we support in the bindings.
40
+ Separate packages lets us not duplicate code while providing a combined
41
+ higher-level API for calling our core functionality.
35
42
 
36
- <!--
37
- TODO(@wooorm-arcjet): I think this example is out of date?
38
- Either remove it if we don’t want people to use this.
39
- Or, change the API to allow simpler use?
40
- -->
43
+ ## When should I use this?
41
44
 
42
- ```ts
43
- import { generateFingerprint, isValidEmail } from "@arcjet/analyze";
45
+ This is an internal Arcjet package not designed for public use.
46
+ See our [_Get started_ guide][arcjet-get-started] for how to use Arcjet in your
47
+ application.
48
+
49
+ ## Install
44
50
 
45
- const fingerprint = generateFingerprint("127.0.0.1");
46
- console.log("fingerprint: ", fingerprint);
51
+ This package is ESM only.
52
+ Install with npm in Node.js:
47
53
 
48
- const valid = isValidEmail("hello@example.com");
49
- console.log("is email valid?", valid);
54
+ ```sh
55
+ npm install @arcjet/analyze
50
56
  ```
51
57
 
52
- ## Implementation
58
+ ## Use
53
59
 
54
- This package uses the Wasm bindings provided by `@arcjet/analyze-wasm` to
55
- call various functions that are exported by our wasm bindings.
60
+ ```js
61
+ import { generateFingerprint, isValidEmail } from "@arcjet/analyze";
56
62
 
57
- We chose to put this logic in a separate package because we need to change the
58
- import structure for each runtime that we support in the wasm bindings. Moving
59
- this to a separate package allows us not to have to duplicate code while providing
60
- a combined higher-level api for calling our core functionality in Wasm.
63
+ const fingerprint = await generateFingerprint(
64
+ { characteristics: [] },
65
+ { ip: "127.0.0.1" },
66
+ );
67
+ console.log(fingerprint);
68
+ // => "fp::2::0d219da6100b99f95cf639b77e088c6df3c096aa5fd61dec5287c5cf94d5e545"
69
+
70
+ const result = await isValidEmail({}, "hello@example.com", {
71
+ tag: "allow-email-validation-config",
72
+ val: {
73
+ allowDomainLiteral: false,
74
+ allow: [],
75
+ requireTopLevelDomain: true,
76
+ },
77
+ });
78
+ console.log(result);
79
+ // => { blocked: [], validity: "valid" }
80
+ ```
61
81
 
62
82
  ## License
63
83
 
64
- Licensed under the [Apache License, Version 2.0][apache-license].
84
+ [Apache License, Version 2.0][apache-license] © [Arcjet Labs, Inc.][arcjet]
65
85
 
66
86
  [arcjet]: https://arcjet.com
87
+ [arcjet-get-started]: https://docs.arcjet.com/get-started
67
88
  [apache-license]: http://www.apache.org/licenses/LICENSE-2.0
89
+ [github-arcjet-analyze-wasm]: https://github.com/arcjet/arcjet-js/tree/main/analyze-wasm
90
+ [github-arcjet-arcjet]: https://github.com/arcjet/arcjet-js/tree/main/arcjet
91
+ [github-arcjet-sdks]: https://github.com/arcjet/arcjet-js#sdks
package/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { BotConfig, BotResult, DetectedSensitiveInfoEntity, DetectSensitiveInfoFunction, EmailValidationConfig, EmailValidationResult, SensitiveInfoEntities, SensitiveInfoEntity, SensitiveInfoResult } from "@arcjet/analyze-wasm";
1
+ import type { BotConfig, BotResult, DetectedSensitiveInfoEntity, DetectSensitiveInfoFunction, EmailValidationConfig, EmailValidationResult, FilterResult, SensitiveInfoEntities, SensitiveInfoEntity, SensitiveInfoResult } from "@arcjet/analyze-wasm";
2
2
  import type { ArcjetLogger } from "@arcjet/protocol";
3
3
  interface AnalyzeContext {
4
4
  log: ArcjetLogger;
@@ -15,15 +15,81 @@ type AnalyzeRequest = {
15
15
  query?: string;
16
16
  extra?: Record<string, string>;
17
17
  };
18
- export { type EmailValidationConfig, type BotConfig, type SensitiveInfoEntity, type DetectedSensitiveInfoEntity, };
18
+ export { type EmailValidationConfig, type BotConfig, type FilterResult, type SensitiveInfoEntity, type DetectedSensitiveInfoEntity, };
19
19
  /**
20
- * Generate a fingerprint for the client. This is used to identify the client
21
- * across multiple requests.
22
- * @param context - The Arcjet Analyze context.
23
- * @param request - The request to fingerprint.
24
- * @returns A SHA-256 string fingerprint.
20
+ * Generate a fingerprint.
21
+ *
22
+ * Fingerprints can be used to identify the client across multiple requests.
23
+ *
24
+ * This considers different things on the `request` based on the passed
25
+ * `context.characteristics`.
26
+ *
27
+ * See [*Fingerprints* on
28
+ * `docs.arcjet.com`](https://docs.arcjet.com/fingerprints/) for more info.
29
+ *
30
+ * @param context
31
+ * Context.
32
+ * @param request
33
+ * Request.
34
+ * @returns
35
+ * Promise for a SHA-256 fingerprint.
25
36
  */
26
37
  export declare function generateFingerprint(context: AnalyzeContext, request: AnalyzeRequest): Promise<string>;
27
- export declare function isValidEmail(context: AnalyzeContext, candidate: string, options: EmailValidationConfig): Promise<EmailValidationResult>;
38
+ /**
39
+ * Check whether an email is valid.
40
+ *
41
+ * @param context
42
+ * Context.
43
+ * @param value
44
+ * Value.
45
+ * @param options
46
+ * Configuration.
47
+ * @returns
48
+ * Promise for a result.
49
+ */
50
+ export declare function isValidEmail(context: AnalyzeContext, value: string, options: EmailValidationConfig): Promise<EmailValidationResult>;
51
+ /**
52
+ * Detect whether a request is by a bot.
53
+ *
54
+ * @param context
55
+ * Context.
56
+ * @param request
57
+ * Request.
58
+ * @param options
59
+ * Configuration.
60
+ * @returns
61
+ * Promise for a result.
62
+ */
28
63
  export declare function detectBot(context: AnalyzeContext, request: AnalyzeRequest, options: BotConfig): Promise<BotResult>;
29
- export declare function detectSensitiveInfo(context: AnalyzeContext, candidate: string, entities: SensitiveInfoEntities, contextWindowSize: number, detect?: DetectSensitiveInfoFunction): Promise<SensitiveInfoResult>;
64
+ /**
65
+ * Detect sensitive info in a value.
66
+ *
67
+ * @param context
68
+ * Context.
69
+ * @param value
70
+ * Value.
71
+ * @param entities
72
+ * Strategy to use for detecting sensitive info;
73
+ * either by denying everything and allowing certain tags or by allowing
74
+ * everything and denying certain tags.
75
+ * @param contextWindowSize
76
+ * Number of tokens to pass to `detect`.
77
+ * @param detect
78
+ * Function to detect sensitive info (optional).
79
+ * @returns
80
+ * Promise for a result.
81
+ */
82
+ export declare function detectSensitiveInfo(context: AnalyzeContext, value: string, entities: SensitiveInfoEntities, contextWindowSize: number, detect?: DetectSensitiveInfoFunction): Promise<SensitiveInfoResult>;
83
+ /**
84
+ * Check if a filter matches a request.
85
+ *
86
+ * @param context
87
+ * Arcjet context.
88
+ * @param request
89
+ * Request.
90
+ * @param expressions
91
+ * Filter expressions.
92
+ * @returns
93
+ * Promise to whether the filter matches the request.
94
+ */
95
+ export declare function matchFilters(context: AnalyzeContext, request: AnalyzeRequest, expressions: ReadonlyArray<string>, allowIfMatch: boolean): Promise<FilterResult>;
package/index.js CHANGED
@@ -38,6 +38,11 @@ function createCoreImports(detect) {
38
38
  return "unknown";
39
39
  },
40
40
  },
41
+ "arcjet:js-req/filter-overrides": {
42
+ ipLookup() {
43
+ return undefined;
44
+ },
45
+ },
41
46
  // TODO(@wooorm-arcjet): figure out a test case for this with the default `detect`.
42
47
  "arcjet:js-req/sensitive-information-identifier": {
43
48
  detect,
@@ -50,13 +55,23 @@ function createCoreImports(detect) {
50
55
  },
51
56
  };
52
57
  }
53
- // TODO(@wooorm-arcjet): document what is used to fingerprint.
54
58
  /**
55
- * Generate a fingerprint for the client. This is used to identify the client
56
- * across multiple requests.
57
- * @param context - The Arcjet Analyze context.
58
- * @param request - The request to fingerprint.
59
- * @returns A SHA-256 string fingerprint.
59
+ * Generate a fingerprint.
60
+ *
61
+ * Fingerprints can be used to identify the client across multiple requests.
62
+ *
63
+ * This considers different things on the `request` based on the passed
64
+ * `context.characteristics`.
65
+ *
66
+ * See [*Fingerprints* on
67
+ * `docs.arcjet.com`](https://docs.arcjet.com/fingerprints/) for more info.
68
+ *
69
+ * @param context
70
+ * Context.
71
+ * @param request
72
+ * Request.
73
+ * @returns
74
+ * Promise for a SHA-256 fingerprint.
60
75
  */
61
76
  async function generateFingerprint(context, request) {
62
77
  const { log } = context;
@@ -70,20 +85,42 @@ async function generateFingerprint(context, request) {
70
85
  log.debug("WebAssembly is not supported in this runtime");
71
86
  return "";
72
87
  }
73
- // TODO(@wooorm-arcjet): docs.
74
- async function isValidEmail(context, candidate, options) {
88
+ /**
89
+ * Check whether an email is valid.
90
+ *
91
+ * @param context
92
+ * Context.
93
+ * @param value
94
+ * Value.
95
+ * @param options
96
+ * Configuration.
97
+ * @returns
98
+ * Promise for a result.
99
+ */
100
+ async function isValidEmail(context, value, options) {
75
101
  const { log } = context;
76
102
  const coreImports = createCoreImports();
77
103
  const analyze = await initializeWasm(coreImports);
78
104
  if (typeof analyze !== "undefined") {
79
- return analyze.isValidEmail(candidate, options);
105
+ return analyze.isValidEmail(value, options);
80
106
  // Ignore the `else` branch as we test in places that have WebAssembly.
81
107
  /* node:coverage ignore next 4 */
82
108
  }
83
109
  log.debug("WebAssembly is not supported in this runtime");
84
110
  return { blocked: [], validity: "valid" };
85
111
  }
86
- // TODO(@wooorm-arcjet): docs.
112
+ /**
113
+ * Detect whether a request is by a bot.
114
+ *
115
+ * @param context
116
+ * Context.
117
+ * @param request
118
+ * Request.
119
+ * @param options
120
+ * Configuration.
121
+ * @returns
122
+ * Promise for a result.
123
+ */
87
124
  async function detectBot(context, request, options) {
88
125
  const { log } = context;
89
126
  const coreImports = createCoreImports();
@@ -96,14 +133,31 @@ async function detectBot(context, request, options) {
96
133
  log.debug("WebAssembly is not supported in this runtime");
97
134
  return { allowed: [], denied: [], spoofed: false, verified: false };
98
135
  }
99
- // TODO(@wooorm-arcjet): docs.
100
- async function detectSensitiveInfo(context, candidate, entities, contextWindowSize, detect) {
136
+ /**
137
+ * Detect sensitive info in a value.
138
+ *
139
+ * @param context
140
+ * Context.
141
+ * @param value
142
+ * Value.
143
+ * @param entities
144
+ * Strategy to use for detecting sensitive info;
145
+ * either by denying everything and allowing certain tags or by allowing
146
+ * everything and denying certain tags.
147
+ * @param contextWindowSize
148
+ * Number of tokens to pass to `detect`.
149
+ * @param detect
150
+ * Function to detect sensitive info (optional).
151
+ * @returns
152
+ * Promise for a result.
153
+ */
154
+ async function detectSensitiveInfo(context, value, entities, contextWindowSize, detect) {
101
155
  const { log } = context;
102
156
  const coreImports = createCoreImports(detect);
103
157
  const analyze = await initializeWasm(coreImports);
104
158
  if (typeof analyze !== "undefined") {
105
159
  const skipCustomDetect = typeof detect !== "function";
106
- return analyze.detectSensitiveInfo(candidate, {
160
+ return analyze.detectSensitiveInfo(value, {
107
161
  entities,
108
162
  contextWindowSize,
109
163
  skipCustomDetect,
@@ -114,5 +168,30 @@ async function detectSensitiveInfo(context, candidate, entities, contextWindowSi
114
168
  log.debug("WebAssembly is not supported in this runtime");
115
169
  throw new Error("SENSITIVE_INFO rule failed to run because Wasm is not supported in this environment.");
116
170
  }
171
+ /**
172
+ * Check if a filter matches a request.
173
+ *
174
+ * @param context
175
+ * Arcjet context.
176
+ * @param request
177
+ * Request.
178
+ * @param expressions
179
+ * Filter expressions.
180
+ * @returns
181
+ * Promise to whether the filter matches the request.
182
+ */
183
+ async function matchFilters(context, request, expressions, allowIfMatch) {
184
+ const coreImports = createCoreImports();
185
+ const analyze = await initializeWasm(coreImports);
186
+ if (typeof analyze !== "undefined") {
187
+ return analyze.matchFilters(JSON.stringify(request),
188
+ // @ts-expect-error: WebAssembly does not support readonly values.
189
+ expressions, allowIfMatch);
190
+ // Ignore the `else` branch as we test in places that have WebAssembly.
191
+ /* node:coverage ignore next 4 */
192
+ }
193
+ context.log.debug("WebAssembly is not supported in this runtime");
194
+ throw new Error("FILTER rule failed to run because Wasm is not supported in this environment.");
195
+ }
117
196
 
118
- export { detectBot, detectSensitiveInfo, generateFingerprint, isValidEmail };
197
+ export { detectBot, detectSensitiveInfo, generateFingerprint, isValidEmail, matchFilters };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcjet/analyze",
3
- "version": "1.0.0-beta.9",
3
+ "version": "1.0.0",
4
4
  "description": "Arcjet local analysis engine",
5
5
  "keywords": [
6
6
  "analyze",
@@ -27,7 +27,7 @@
27
27
  "url": "https://arcjet.com"
28
28
  },
29
29
  "engines": {
30
- "node": ">=18"
30
+ "node": ">=20"
31
31
  },
32
32
  "type": "module",
33
33
  "main": "./index.js",
@@ -40,23 +40,22 @@
40
40
  "build": "rollup --config rollup.config.js",
41
41
  "lint": "eslint .",
42
42
  "prepublishOnly": "npm run build",
43
- "test-api": "node --test",
44
- "test-coverage": "node --experimental-test-coverage --test",
43
+ "test-api": "node --test -- test/*.test.js",
44
+ "test-coverage": "node --experimental-test-coverage --test -- test/*.test.js",
45
45
  "test": "npm run build && npm run lint && npm run test-coverage"
46
46
  },
47
47
  "dependencies": {
48
- "@arcjet/analyze-wasm": "1.0.0-beta.9",
49
- "@arcjet/protocol": "1.0.0-beta.9"
48
+ "@arcjet/analyze-wasm": "1.0.0",
49
+ "@arcjet/protocol": "1.0.0"
50
50
  },
51
51
  "devDependencies": {
52
- "@arcjet/eslint-config": "1.0.0-beta.9",
53
- "@arcjet/rollup-config": "1.0.0-beta.9",
54
- "@arcjet/tsconfig": "1.0.0-beta.9",
52
+ "@arcjet/eslint-config": "1.0.0",
53
+ "@arcjet/rollup-config": "1.0.0",
55
54
  "@bytecodealliance/jco": "1.5.0",
56
- "@rollup/wasm-node": "4.44.2",
57
- "@types/node": "18.18.0",
58
- "eslint": "9.30.1",
59
- "typescript": "5.8.3"
55
+ "@rollup/wasm-node": "4.55.1",
56
+ "@types/node": "25.0.8",
57
+ "eslint": "9.39.2",
58
+ "typescript": "5.9.3"
60
59
  },
61
60
  "publishConfig": {
62
61
  "access": "public",