no-pii 1.2.1 → 1.2.2

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 (2) hide show
  1. package/nopii.js +18 -108
  2. package/package.json +4 -1
package/nopii.js CHANGED
@@ -1,14 +1,8 @@
1
- import fs from 'node:fs/promises';
2
- import crypto from 'node:crypto';
3
- import { Buffer } from 'node:buffer';
4
- import { getPassword, setPassword } from 'cross-keychain';
5
-
6
- export class NoPii {
1
+ export default class NoPii {
7
2
  #strategies = [];
8
3
  #combinedRegex = null;
9
- #storage = null;
10
4
  #config = {};
11
- #currentRules = {};
5
+ #currentRules = {};
12
6
 
13
7
  static PRESETS = {
14
8
  COMMON: {
@@ -24,7 +18,6 @@ export class NoPii {
24
18
 
25
19
  constructor(options = {}) {
26
20
  this.#config = { verbose: false, ...options };
27
- this.#storage = this.#initStorage(options.storage);
28
21
 
29
22
  if (options.rules) {
30
23
  for (const [key, val] of Object.entries(options.rules)) {
@@ -33,13 +26,12 @@ export class NoPii {
33
26
  }
34
27
  }
35
28
 
36
- list() {
29
+ listRules() {
37
30
  return { ...this.#currentRules };
38
31
  }
39
32
 
40
- async addRule(key, value) {
33
+ addRule(key, value) {
41
34
  this.#applyRuleLogic(key, value);
42
- await this.#persist();
43
35
  return this;
44
36
  }
45
37
 
@@ -57,13 +49,12 @@ export class NoPii {
57
49
  else if (allPresets[upperKey] && value === true) {
58
50
  this.#register(upperKey, allPresets[upperKey]);
59
51
  }
60
- else if (Array.isArray(value)) {
61
- const escaped = value
62
- .map(v => v.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
63
- .join('|');
64
-
65
- this.#register(key, new RegExp(escaped, 'g'));
66
- }
52
+ else if (Array.isArray(value)) {
53
+ const escaped = value
54
+ .map(v => v.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
55
+ .join('|');
56
+ this.#register(key, new RegExp(escaped, 'g'));
57
+ }
67
58
  else {
68
59
  this.#register(key, value);
69
60
  }
@@ -71,7 +62,6 @@ export class NoPii {
71
62
 
72
63
  #register(name, regex) {
73
64
  let pattern;
74
- // FIX: Handle string-serialized regex like "/pattern/flags"
75
65
  if (typeof regex === 'string' && regex.startsWith('/') && regex.lastIndexOf('/') > 0) {
76
66
  const lastSlash = regex.lastIndexOf('/');
77
67
  const source = regex.slice(1, lastSlash);
@@ -101,38 +91,16 @@ export class NoPii {
101
91
  this.#combinedRegex = new RegExp(source, 'gu');
102
92
  }
103
93
 
104
- async #persist() {
105
- if (!this.#storage) return;
106
-
107
- // FIX: Serialize RegExp objects to strings so they don't become {} in JSON
108
- const serializable = {};
109
- for (const [k, v] of Object.entries(this.#currentRules)) {
110
- serializable[k] = (v instanceof RegExp) ? `/${v.source}/${v.flags}` : v;
111
- }
112
- await this.#storage.set('rules', serializable);
113
- }
114
-
115
- async load() {
116
- if (!this.#storage) return this;
117
- const savedRules = await this.#storage.get('rules');
118
- if (savedRules) {
119
- for (const [key, val] of Object.entries(savedRules)) {
120
- this.#applyRuleLogic(key, val);
121
- }
122
- }
123
- return this;
124
- }
125
-
126
- redact(text) {
94
+ redact(text, startCount) {
127
95
  if (!this.#combinedRegex || !text) return { safeText: text, vault: new Map() };
128
- const vault = new Map();
129
- let count = 0;
96
+ const vault = {};
97
+ let count = startCount ? startCount : 0;
130
98
  const safeText = text.replace(this.#combinedRegex, (...args) => {
131
99
  const groups = args.at(-1);
132
100
  const label = Object.keys(groups).find(key => groups[key] !== undefined);
133
101
  const value = groups[label];
134
102
  const placeholder = `[${label}_${++count}]`;
135
- vault.set(placeholder, value);
103
+ vault[placeholder] = value;
136
104
  return placeholder;
137
105
  });
138
106
  return { safeText, vault };
@@ -152,68 +120,10 @@ export class NoPii {
152
120
 
153
121
  return redactedText.replace(restoreRegex, (m) => lookup(m));
154
122
  }
123
+
124
+
125
+
155
126
 
156
- #initStorage(storage) {
157
- if (!storage) {
158
- const _mem = new Map();
159
- return { get: async (k) => _mem.get(k), set: async (k, v) => _mem.set(k, v) };
160
- }
161
-
162
- if (storage.method === 'os') {
163
- const { service = 'nopii' } = storage;
164
- const account = service;
165
- return {
166
- get: async (key) => {
167
- try {
168
- const val = await getPassword(service, `${account}_${key}`);
169
- return val ? JSON.parse(val) : null;
170
- } catch { return null; }
171
- },
172
- set: async (key, val) => {
173
- await setPassword(service, `${account}_${key}`, JSON.stringify(val));
174
- }
175
- };
176
- }
177
-
178
- if (typeof storage.method === 'string' && storage.method.endsWith('.json')) {
179
- const aesKey = Buffer.alloc(32, storage.aes || 'default-secret-key');
180
- const filePath = storage.method;
181
-
182
- const encrypt = (text) => {
183
- const iv = crypto.randomBytes(12);
184
- const cipher = crypto.createCipheriv('aes-256-gcm', aesKey, iv);
185
- const enc = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]);
186
- return Buffer.concat([iv, cipher.getAuthTag(), enc]).toString('base64');
187
- };
188
-
189
- const decrypt = (data) => {
190
- const buf = Buffer.from(data, 'base64');
191
- const iv = buf.subarray(0, 12), tag = buf.subarray(12, 28), enc = buf.subarray(28);
192
- const decipher = crypto.createDecipheriv('aes-256-gcm', aesKey, iv);
193
- decipher.setAuthTag(tag);
194
- return decipher.update(enc, 'utf8') + decipher.final('utf8');
195
- };
196
-
197
- return {
198
- async get(lookupKey) {
199
- try {
200
- const raw = await fs.readFile(filePath, 'utf8');
201
- const data = JSON.parse(decrypt(raw));
202
- return lookupKey ? data[lookupKey] : data;
203
- } catch { return null; }
204
- },
205
- async set(lookupKey, val) {
206
- let data = {};
207
- try {
208
- const raw = await fs.readFile(filePath, 'utf8');
209
- data = JSON.parse(decrypt(raw));
210
- } catch (e) {}
211
- data[lookupKey] = val;
212
- await fs.writeFile(filePath, encrypt(JSON.stringify(data)));
213
- }
214
- };
215
- }
216
- }
127
+
217
128
  }
218
129
 
219
- export const nopii = (options) => new NoPii(options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "no-pii",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "Production-grade PII redaction library with CLI support",
5
5
  "main": "nopii.js",
6
6
  "type": "module",
@@ -31,7 +31,10 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@huggingface/transformers": "^3.8.1",
34
+ "@nlpjs/lang-zh": "^5.0.0-alpha.5",
35
+ "@nlpjs/ner": "^5.0.0-alpha.5",
34
36
  "cross-keychain": "^1.0.0",
37
+ "nodejieba": "^3.5.7",
35
38
  "onnxruntime-node": "^1.24.3"
36
39
  },
37
40
  "files": [