@arcjet/redact 1.0.0-beta.8 → 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 +40 -12
  2. package/index.d.ts +64 -8
  3. package/index.js +43 -36
  4. package/package.json +21 -19
package/README.md CHANGED
@@ -23,34 +23,62 @@ against common attacks.
23
23
  This is the [Arcjet][arcjet] TypeScript and JavaScript sensitive information
24
24
  redaction library.
25
25
 
26
- ## Installation
26
+ - [npm package (`@arcjet/redact`)](https://www.npmjs.com/package/@arcjet/redact)
27
+ - [GitHub source code (`redact/` in `arcjet/arcjet-js`)](https://github.com/arcjet/arcjet-js/tree/main/redact)
27
28
 
28
- ```shell
29
- npm install -S @arcjet/redact
29
+ ## What is this?
30
+
31
+ This package provides functionality to redact sensitive info.
32
+ The work is done in WebAssembly but is called here from JavaScript.
33
+
34
+ The WebAssembly files are in [`@arcjet/redact-wasm`][github-arcjet-redact-wasm].
35
+ For more info on why we maintain our WebAssembly and JavaScript projects like
36
+ this,
37
+ see [“What is this?” in the readme of
38
+ `@arcjet/analyze-wasm`][github-arcjet-analyze-wasm-what].
39
+
40
+ ## When should I use this?
41
+
42
+ You can use this package to redact sensitive information locally.
43
+ You can redact email addresses, credit card numbers, and more.
44
+ It is also possible to reverse the process: to un-redact what was found.
45
+
46
+ ## Install
47
+
48
+ This package is ESM only.
49
+ Install with npm in Node.js:
50
+
51
+ ```sh
52
+ npm install @arcjet/redact
30
53
  ```
31
54
 
32
55
  ## Reference
33
56
 
34
57
  The full reference documentation can be found in the [Arcjet docs][redact-ref].
35
58
 
36
- ## Example
59
+ ## Use
60
+
61
+ ```ts
62
+ import { redact } from "@arcjet/redact";
63
+
64
+ const value = "Hi, my name is John and my email adress is john@example.com";
37
65
 
38
- ```typescript
39
- const text = "Hi, my name is John and my email adress is john@example.com";
40
- const [redacted, unredact] = await redact(text, {
41
- redact: ["email", "phone-number"],
66
+ const [redacted, unredact] = await redact(value, {
67
+ entities: ["email", "phone-number"],
42
68
  });
43
69
  console.log(redacted);
44
- // Hi, my name is John and my email address is <Redacted email #0>
70
+ // => "Hi, my name is John and my email adress is <Redacted email #0>"
45
71
 
46
72
  const unredacted = unredact("Your email address is <Redacted email #0>");
47
- console.log(unredacted); // Your email address is john@example.com
73
+ console.log(unredacted); // "Your email address is john@example.com"
48
74
  ```
49
75
 
50
76
  ## License
51
77
 
52
- Licensed under the [Apache License, Version 2.0][apache-license].
78
+ [Apache License, Version 2.0][apache-license] © [Arcjet Labs, Inc.][arcjet]
53
79
 
80
+ [apache-license]: http://www.apache.org/licenses/LICENSE-2.0
54
81
  [arcjet]: https://arcjet.com
55
82
  [redact-ref]: https://docs.arcjet.com/redact/reference
56
- [apache-license]: http://www.apache.org/licenses/LICENSE-2.0
83
+ [github-arcjet-analyze-wasm-what]: https://github.com/arcjet/arcjet-js/tree/main/analyze-wasm#what-is-this
84
+ [github-arcjet-redact-wasm]: https://github.com/arcjet/arcjet-js/tree/main/redact-wasm
package/index.d.ts CHANGED
@@ -1,12 +1,68 @@
1
- export type ArcjetSensitiveInfoType = "email" | "phone-number" | "ip-address" | "credit-card-number";
2
- type DetectSensitiveInfoEntities<T> = (tokens: string[]) => Array<ArcjetSensitiveInfoType | T | undefined>;
3
- type ValidEntities<Detect> = Array<undefined extends Detect ? ArcjetSensitiveInfoType : Detect extends DetectSensitiveInfoEntities<infer CustomEntities> ? ArcjetSensitiveInfoType | CustomEntities : never>;
4
- export type RedactOptions<Detect> = {
5
- entities?: ValidEntities<Detect>;
1
+ import type { SensitiveInfoEntity } from "@arcjet/redact-wasm";
2
+ /**
3
+ * Types of standard sensitive information that can be detected.
4
+ */
5
+ export type ArcjetSensitiveInfoType = Exclude<SensitiveInfoEntity["tag"], "custom">;
6
+ /**
7
+ * Options for the `redact` function.
8
+ *
9
+ * @template DetectedEntities
10
+ * Custom entity names that are returned from `detect` and optionally listed in `entities`.
11
+ * @template ListedEntities
12
+ * Entity names that can be listed in the `entities` option.
13
+ */
14
+ export type RedactOptions<DetectedEntities extends string | undefined = undefined, ListedEntities extends ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined> = ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined>> = {
15
+ /**
16
+ * Entities to redact.
17
+ */
18
+ entities?: ReadonlyArray<ListedEntities | Exclude<DetectedEntities, undefined>>;
19
+ /**
20
+ * Size of tokens to consider.
21
+ */
6
22
  contextWindowSize?: number;
7
- detect?: Detect;
8
- replace?: (entity: ValidEntities<Detect>[number], plaintext: string) => string | undefined;
23
+ /**
24
+ * Custom detection function to identify sensitive information.
25
+ *
26
+ * @template DetectedEntities
27
+ * Custom entity names that are returned from `detect` and optionally listed in `entities`.
28
+ * @template ListedEntities
29
+ * Entity names that can be listed in the `entities` option.
30
+ * @param tokens
31
+ * Tokens.
32
+ * @returns
33
+ * List of entities (or undefined).
34
+ */
35
+ detect?: (tokens: string[]) => ReadonlyArray<DetectedEntities>;
36
+ /**
37
+ * Custom replace function to redact sensitive information.
38
+ *
39
+ * @template DetectedEntities
40
+ * Custom entity names that are returned from `detect` and optionally listed in `entities`.
41
+ * @template ListedEntities
42
+ * Entity names that can be listed in the `entities` option.
43
+ * @param entity
44
+ * Entity to redact.
45
+ * @param plaintext
46
+ * The plaintext string to redact.
47
+ * @returns
48
+ * Redacted string or nothing.
49
+ */
50
+ replace?: (entity: ListedEntities, plaintext: string) => string | undefined;
9
51
  };
10
52
  type Unredact = (input: string) => string;
11
- export declare function redact<const Detect extends DetectSensitiveInfoEntities<CustomEntities> | undefined, const CustomEntities extends string>(candidate: string, options?: RedactOptions<Detect>): Promise<[string, Unredact]>;
53
+ /**
54
+ * Redact sensitive info.
55
+ *
56
+ * @template DetectedEntities
57
+ * Custom entity names that are returned from `detect` and optionally listed in `entities`.
58
+ * @template ListedEntities
59
+ * Entity names that can be listed in the `entities` option.
60
+ * @param candidate
61
+ * Value to redact.
62
+ * @param options
63
+ * Configuration.
64
+ * @returns
65
+ * Promise to a tuple with the redacted string and a function to unredact it.
66
+ */
67
+ export declare function redact<const DetectedEntities extends string | undefined = undefined, const ListedEntities extends ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined> = ArcjetSensitiveInfoType | Exclude<DetectedEntities, undefined>>(candidate: string, options?: RedactOptions<DetectedEntities, ListedEntities>): Promise<[string, Unredact]>;
12
68
  export {};
package/index.js CHANGED
@@ -4,17 +4,11 @@ function userEntitiesToWasm(entity) {
4
4
  if (typeof entity !== "string") {
5
5
  throw new Error("redaction entities must be strings");
6
6
  }
7
- if (entity === "email") {
8
- return { tag: "email" };
9
- }
10
- if (entity === "phone-number") {
11
- return { tag: "phone-number" };
12
- }
13
- if (entity === "ip-address") {
14
- return { tag: "ip-address" };
15
- }
16
- if (entity === "credit-card-number") {
17
- return { tag: "credit-card-number" };
7
+ if (entity === "credit-card-number" ||
8
+ entity === "email" ||
9
+ entity === "ip-address" ||
10
+ entity === "phone-number") {
11
+ return { tag: entity };
18
12
  }
19
13
  return {
20
14
  tag: "custom",
@@ -22,17 +16,11 @@ function userEntitiesToWasm(entity) {
22
16
  };
23
17
  }
24
18
  function wasmEntitiesToString(entity) {
25
- if (entity.tag === "email") {
26
- return "email";
27
- }
28
- if (entity.tag === "ip-address") {
29
- return "ip-address";
30
- }
31
- if (entity.tag === "credit-card-number") {
32
- return "credit-card-number";
33
- }
34
- if (entity.tag === "phone-number") {
35
- return "phone-number";
19
+ if (entity.tag === "credit-card-number" ||
20
+ entity.tag === "email" ||
21
+ entity.tag === "ip-address" ||
22
+ entity.tag === "phone-number") {
23
+ return entity.tag;
36
24
  }
37
25
  return entity.val;
38
26
  }
@@ -50,26 +38,31 @@ function noOpReplace(_input, _plaintext) {
50
38
  }
51
39
  function getWasmOptions(options) {
52
40
  if (typeof options === "object" && options !== null) {
53
- if (typeof options.entities !== "undefined") {
54
- if (Array.isArray(options.entities)) {
55
- if (options.entities.length < 1) {
56
- throw new Error("no entities configured for redaction");
57
- }
58
- }
59
- else {
41
+ const entities = options.entities;
42
+ if (entities !== undefined) {
43
+ if (!Array.isArray(entities)) {
60
44
  throw new Error("entities must be an array");
61
45
  }
46
+ if (entities.length < 1) {
47
+ throw new Error("no entities configured for redaction");
48
+ }
62
49
  }
63
- return {
64
- entities: options.entities?.map(userEntitiesToWasm),
65
- contextWindowSize: options.contextWindowSize || 1,
66
- skipCustomDetect: typeof options.detect !== "function",
67
- skipCustomRedact: typeof options.replace !== "function",
68
- };
50
+ // `entities` is an optional field but not allowed to be `undefined`.
51
+ return entities
52
+ ? {
53
+ entities: entities.map(userEntitiesToWasm),
54
+ contextWindowSize: options.contextWindowSize || 1,
55
+ skipCustomDetect: typeof options.detect !== "function",
56
+ skipCustomRedact: typeof options.replace !== "function",
57
+ }
58
+ : {
59
+ contextWindowSize: options.contextWindowSize || 1,
60
+ skipCustomDetect: typeof options.detect !== "function",
61
+ skipCustomRedact: typeof options.replace !== "function",
62
+ };
69
63
  }
70
64
  else {
71
65
  return {
72
- entities: undefined,
73
66
  contextWindowSize: 1,
74
67
  skipCustomDetect: true,
75
68
  skipCustomRedact: true,
@@ -109,6 +102,20 @@ async function callRedactWasm(candidate, options) {
109
102
  throw new Error("redact failed to run because Wasm is not supported in this environment");
110
103
  }
111
104
  }
105
+ /**
106
+ * Redact sensitive info.
107
+ *
108
+ * @template DetectedEntities
109
+ * Custom entity names that are returned from `detect` and optionally listed in `entities`.
110
+ * @template ListedEntities
111
+ * Entity names that can be listed in the `entities` option.
112
+ * @param candidate
113
+ * Value to redact.
114
+ * @param options
115
+ * Configuration.
116
+ * @returns
117
+ * Promise to a tuple with the redacted string and a function to unredact it.
118
+ */
112
119
  async function redact(candidate, options) {
113
120
  const redactions = await callRedactWasm(candidate, options);
114
121
  // Need to apply the redactions in reverse order so that the offsets aren't changed
package/package.json CHANGED
@@ -1,7 +1,13 @@
1
1
  {
2
2
  "name": "@arcjet/redact",
3
- "version": "1.0.0-beta.8",
3
+ "version": "1.0.0",
4
4
  "description": "Arcjet sensitive information redaction library",
5
+ "keywords": [
6
+ "arcjet",
7
+ "redact",
8
+ "utility",
9
+ "util"
10
+ ],
5
11
  "license": "Apache-2.0",
6
12
  "homepage": "https://arcjet.com",
7
13
  "repository": {
@@ -19,37 +25,33 @@
19
25
  "url": "https://arcjet.com"
20
26
  },
21
27
  "engines": {
22
- "node": ">=18"
28
+ "node": ">=20"
23
29
  },
24
30
  "type": "module",
25
31
  "main": "./index.js",
26
32
  "types": "./index.d.ts",
27
33
  "files": [
28
- "LICENSE",
29
- "README.md",
30
- "*.js",
31
- "*.d.ts",
32
- "!*.config.js"
34
+ "index.d.ts",
35
+ "index.js"
33
36
  ],
34
37
  "scripts": {
35
- "prepublishOnly": "npm run build",
36
38
  "build": "rollup --config rollup.config.js",
37
39
  "lint": "eslint .",
38
- "pretest": "npm run build",
39
- "test": "node --test --experimental-test-coverage"
40
+ "prepublishOnly": "npm run build",
41
+ "test-api": "node --test -- test/*.test.js",
42
+ "test-coverage": "node --experimental-test-coverage --test -- test/*.test.js",
43
+ "test": "npm run build && npm run lint && npm run test-coverage"
40
44
  },
41
45
  "dependencies": {
42
- "@arcjet/redact-wasm": "1.0.0-beta.8"
46
+ "@arcjet/redact-wasm": "1.0.0"
43
47
  },
44
48
  "devDependencies": {
45
- "@arcjet/eslint-config": "1.0.0-beta.8",
46
- "@arcjet/rollup-config": "1.0.0-beta.8",
47
- "@arcjet/tsconfig": "1.0.0-beta.8",
48
- "@rollup/wasm-node": "4.41.1",
49
- "@types/node": "18.18.0",
50
- "eslint": "9.27.0",
51
- "expect": "29.7.0",
52
- "typescript": "5.8.3"
49
+ "@arcjet/eslint-config": "1.0.0",
50
+ "@arcjet/rollup-config": "1.0.0",
51
+ "@rollup/wasm-node": "4.55.1",
52
+ "@types/node": "25.0.8",
53
+ "eslint": "9.39.2",
54
+ "typescript": "5.9.3"
53
55
  },
54
56
  "publishConfig": {
55
57
  "access": "public",