@heroku/js-blanket 0.0.0 → 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 (82) hide show
  1. package/README.md +4 -1
  2. package/dist/cjs/.tsbuildinfo +1 -0
  3. package/dist/cjs/adapters/logging/generic.js +23 -0
  4. package/dist/cjs/adapters/logging/generic.js.map +1 -0
  5. package/dist/cjs/adapters/logging/generic.test.js +432 -0
  6. package/dist/cjs/adapters/logging/generic.test.js.map +1 -0
  7. package/dist/cjs/core/patterns.js +17 -0
  8. package/dist/cjs/core/patterns.js.map +1 -0
  9. package/dist/cjs/core/presets.js +116 -0
  10. package/dist/cjs/core/presets.js.map +1 -0
  11. package/dist/cjs/core/scrubber.js +260 -0
  12. package/dist/cjs/core/scrubber.js.map +1 -0
  13. package/dist/cjs/core/scrubber.test.js +392 -0
  14. package/dist/cjs/core/scrubber.test.js.map +1 -0
  15. package/dist/cjs/core/types.js +3 -0
  16. package/dist/cjs/core/types.js.map +1 -0
  17. package/dist/cjs/core/types.test.js +326 -0
  18. package/dist/cjs/core/types.test.js.map +1 -0
  19. package/dist/cjs/index.js +16 -0
  20. package/dist/cjs/index.js.map +1 -0
  21. package/dist/cjs/index.test.js +31 -0
  22. package/dist/cjs/index.test.js.map +1 -0
  23. package/dist/cjs/package.json +1 -0
  24. package/dist/esm/.tsbuildinfo +1 -0
  25. package/{src/adapters/logging/generic.ts → dist/esm/adapters/logging/generic.d.ts} +1 -4
  26. package/dist/esm/adapters/logging/generic.js +20 -0
  27. package/dist/esm/adapters/logging/generic.js.map +1 -0
  28. package/dist/esm/adapters/logging/generic.test.d.ts +7 -0
  29. package/dist/esm/adapters/logging/generic.test.js +430 -0
  30. package/dist/esm/adapters/logging/generic.test.js.map +1 -0
  31. package/dist/esm/core/patterns.d.ts +4 -0
  32. package/dist/esm/core/patterns.js +14 -0
  33. package/dist/esm/core/patterns.js.map +1 -0
  34. package/dist/esm/core/presets.d.ts +64 -0
  35. package/{src/core/presets.ts → dist/esm/core/presets.js} +46 -55
  36. package/dist/esm/core/presets.js.map +1 -0
  37. package/dist/esm/core/scrubber.d.ts +131 -0
  38. package/dist/esm/core/scrubber.js +256 -0
  39. package/dist/esm/core/scrubber.js.map +1 -0
  40. package/dist/esm/core/scrubber.test.d.ts +1 -0
  41. package/dist/esm/core/scrubber.test.js +390 -0
  42. package/dist/esm/core/scrubber.test.js.map +1 -0
  43. package/dist/esm/core/types.d.ts +169 -0
  44. package/dist/esm/core/types.js +2 -0
  45. package/dist/esm/core/types.js.map +1 -0
  46. package/dist/esm/core/types.test.d.ts +9 -0
  47. package/dist/esm/core/types.test.js +324 -0
  48. package/dist/esm/core/types.test.js.map +1 -0
  49. package/{src/index.ts → dist/esm/index.d.ts} +0 -3
  50. package/dist/esm/index.js +7 -0
  51. package/dist/esm/index.js.map +1 -0
  52. package/dist/esm/index.test.d.ts +1 -0
  53. package/dist/esm/index.test.js +29 -0
  54. package/dist/esm/index.test.js.map +1 -0
  55. package/package.json +45 -47
  56. package/.c8rc.json +0 -11
  57. package/.editorconfig +0 -11
  58. package/.github/PULL_REQUEST_TEMPLATE.md +0 -41
  59. package/.github/copilot-instructions.md +0 -117
  60. package/.github/workflows/ci.yml +0 -25
  61. package/.husky/pre-commit +0 -1
  62. package/.lintstagedrc.json +0 -4
  63. package/.tool-versions +0 -1
  64. package/CODEOWNERS +0 -8
  65. package/CODE_OF_CONDUCT.md +0 -111
  66. package/CONTRIBUTING.md +0 -123
  67. package/SECURITY.md +0 -8
  68. package/docs/examples/logging-integration.md +0 -736
  69. package/eslint.config.mjs +0 -108
  70. package/prettier.config.mjs +0 -10
  71. package/scripts/test-setup.mjs +0 -24
  72. package/src/adapters/logging/generic.test.ts +0 -531
  73. package/src/core/patterns.ts +0 -22
  74. package/src/core/scrubber.test.ts +0 -465
  75. package/src/core/scrubber.ts +0 -284
  76. package/src/core/types.test.ts +0 -516
  77. package/src/core/types.ts +0 -176
  78. package/src/index.test.ts +0 -41
  79. package/tsconfig.cjs.json +0 -12
  80. package/tsconfig.esm.json +0 -12
  81. package/tsconfig.json +0 -32
  82. package/tsconfig.test.json +0 -9
@@ -0,0 +1,390 @@
1
+ import { describe, it } from 'mocha';
2
+ import { expect } from 'chai';
3
+ import { Scrubber } from './scrubber.js';
4
+ describe('Scrubber', () => {
5
+ describe('Field-based scrubbing', () => {
6
+ it('scrubs sensitive fields at any depth', () => {
7
+ const scrubber = new Scrubber({
8
+ fields: ['access_token'],
9
+ });
10
+ const input = {
11
+ user: {
12
+ profile: {
13
+ settings: {
14
+ auth: { access_token: 'secret123' },
15
+ },
16
+ },
17
+ },
18
+ };
19
+ const { data } = scrubber.scrub(input);
20
+ expect(data.user.profile.settings.auth.access_token).to.equal('[SCRUBBED]');
21
+ });
22
+ it('handles case-insensitive field matching', () => {
23
+ const scrubber = new Scrubber({
24
+ fields: ['password'],
25
+ });
26
+ const input = {
27
+ Password: 'secret1',
28
+ PASSWORD: 'secret2',
29
+ password: 'secret3',
30
+ };
31
+ const { data } = scrubber.scrub(input);
32
+ expect(data.Password).to.equal('[SCRUBBED]');
33
+ expect(data.PASSWORD).to.equal('[SCRUBBED]');
34
+ expect(data.password).to.equal('[SCRUBBED]');
35
+ });
36
+ it('supports regex field patterns', () => {
37
+ const scrubber = new Scrubber({
38
+ fields: [/api[-_]?key/i], // Matches api_key, api-key, apikey (case insensitive)
39
+ });
40
+ const input = {
41
+ user_api_key: 'secret',
42
+ API_KEY_V2: 'secret',
43
+ myApiKeyHere: 'secret',
44
+ };
45
+ const { data } = scrubber.scrub(input);
46
+ expect(data.user_api_key).to.equal('[SCRUBBED]');
47
+ expect(data.API_KEY_V2).to.equal('[SCRUBBED]');
48
+ expect(data.myApiKeyHere).to.equal('[SCRUBBED]');
49
+ });
50
+ });
51
+ describe('Path-based scrubbing', () => {
52
+ it('scrubs specific paths only', () => {
53
+ const scrubber = new Scrubber({
54
+ paths: ['user.profile.email'],
55
+ });
56
+ const input = {
57
+ user: {
58
+ profile: { email: 'bob@example.com', name: 'Bob' },
59
+ settings: { email: 'notifications@example.com' },
60
+ },
61
+ };
62
+ const { data } = scrubber.scrub(input);
63
+ expect(data.user.profile.email).to.equal('[SCRUBBED]');
64
+ expect(data.user.settings.email).to.equal('notifications@example.com');
65
+ });
66
+ it('scrubs array items by index', () => {
67
+ const scrubber = new Scrubber({
68
+ paths: ['users[0].password'],
69
+ });
70
+ const input = {
71
+ users: [
72
+ { name: 'bob', password: 'secret1' },
73
+ { name: 'alice', password: 'secret2' },
74
+ ],
75
+ };
76
+ const { data } = scrubber.scrub(input);
77
+ expect(data.users?.[0]?.password).to.equal('[SCRUBBED]');
78
+ expect(data.users?.[1]?.password).to.equal('secret2');
79
+ });
80
+ });
81
+ describe('Pattern-based scrubbing', () => {
82
+ it('scrubs SSN patterns in strings', () => {
83
+ const scrubber = new Scrubber({
84
+ patterns: [/\b\d{3}-\d{2}-\d{4}\b/g],
85
+ });
86
+ const input = { message: 'User SSN is 123-45-6789' };
87
+ const { data } = scrubber.scrub(input);
88
+ expect(data.message).to.contain('[SCRUBBED]');
89
+ expect(data.message).not.to.contain('123-45-6789');
90
+ });
91
+ it('scrubs email patterns in strings', () => {
92
+ const scrubber = new Scrubber({
93
+ patterns: [/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g],
94
+ });
95
+ const input = { log: 'Auth failed for user@example.com' };
96
+ const { data } = scrubber.scrub(input);
97
+ expect(data.log).to.contain('[SCRUBBED]');
98
+ expect(data.log).not.to.contain('user@example.com');
99
+ });
100
+ it('scrubs multiple patterns in same string', () => {
101
+ const scrubber = new Scrubber({
102
+ patterns: [
103
+ /\b\d{3}-\d{2}-\d{4}\b/g, // SSN
104
+ /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, // Email
105
+ ],
106
+ });
107
+ const input = {
108
+ log: 'User bob@example.com has SSN 123-45-6789',
109
+ };
110
+ const { data } = scrubber.scrub(input);
111
+ expect(data.log).not.to.contain('bob@example.com');
112
+ expect(data.log).not.to.contain('123-45-6789');
113
+ });
114
+ });
115
+ describe('Array handling', () => {
116
+ it('scrubs fields across all array items', () => {
117
+ const scrubber = new Scrubber({
118
+ fields: ['password'],
119
+ });
120
+ const users = [
121
+ { name: 'bob', password: 'secret' },
122
+ { name: 'alice', password: 'hidden' },
123
+ ];
124
+ const { data } = scrubber.scrub(users);
125
+ expect(data[0]?.password).to.equal('[SCRUBBED]');
126
+ expect(data[1]?.password).to.equal('[SCRUBBED]');
127
+ expect(data[0]?.name).to.equal('bob');
128
+ expect(data[1]?.name).to.equal('alice');
129
+ });
130
+ it('handles nested arrays', () => {
131
+ const scrubber = new Scrubber({
132
+ fields: ['api_key'],
133
+ });
134
+ const input = {
135
+ teams: [
136
+ {
137
+ members: [
138
+ { name: 'bob', api_key: 'secret1' },
139
+ { name: 'alice', api_key: 'secret2' },
140
+ ],
141
+ },
142
+ ],
143
+ };
144
+ const { data } = scrubber.scrub(input);
145
+ expect(data.teams?.[0]?.members?.[0]?.api_key).to.equal('[SCRUBBED]');
146
+ expect(data.teams?.[0]?.members?.[1]?.api_key).to.equal('[SCRUBBED]');
147
+ });
148
+ });
149
+ describe('Circular reference handling', () => {
150
+ it('handles circular references without crashing', () => {
151
+ const scrubber = new Scrubber({ fields: [] });
152
+ const input = { name: 'test' };
153
+ input.self = input;
154
+ const { data } = scrubber.scrub(input);
155
+ expect(data.self).to.equal('[Circular Reference]');
156
+ });
157
+ it('scrubs fields before detecting circular references', () => {
158
+ const scrubber = new Scrubber({ fields: ['password'] });
159
+ const input = { name: 'test', password: 'secret' };
160
+ input.self = input;
161
+ const { data } = scrubber.scrub(input);
162
+ expect(data.password).to.equal('[SCRUBBED]');
163
+ expect(data.self).to.equal('[Circular Reference]');
164
+ });
165
+ it('handles nested circular references', () => {
166
+ const scrubber = new Scrubber({ fields: [] });
167
+ const input = { name: 'test', nested: { level: 1 } };
168
+ input.self = input;
169
+ input.nested.parent = input;
170
+ const { data } = scrubber.scrub(input);
171
+ expect(data.self).to.equal('[Circular Reference]');
172
+ expect(data.nested.parent).to.equal('[Circular Reference]');
173
+ });
174
+ });
175
+ describe('Combined modes', () => {
176
+ it('applies field + path + pattern scrubbing together', () => {
177
+ const scrubber = new Scrubber({
178
+ fields: ['api_key'],
179
+ paths: ['user.email'],
180
+ patterns: [/\b\d{3}-\d{2}-\d{4}\b/g],
181
+ });
182
+ const input = {
183
+ user: {
184
+ email: 'bob@example.com', // Path-based
185
+ api_key: 'secret-key-123', // Field-based
186
+ },
187
+ log: 'SSN: 123-45-6789', // Pattern-based
188
+ nested: {
189
+ service: {
190
+ api_key: 'another-secret', // Field-based (any depth)
191
+ },
192
+ },
193
+ };
194
+ const { data, scrubbedPaths } = scrubber.scrub(input);
195
+ expect(data.user?.email).to.equal('[SCRUBBED]');
196
+ expect(data.user?.api_key).to.equal('[SCRUBBED]');
197
+ expect(data.log).not.to.contain('123-45-6789');
198
+ expect(data.nested?.service?.api_key).to.equal('[SCRUBBED]');
199
+ expect(scrubbedPaths.length).to.be.greaterThan(0);
200
+ });
201
+ });
202
+ describe('Scrub result metadata', () => {
203
+ it('tracks scrubbed paths', () => {
204
+ const scrubber = new Scrubber({
205
+ fields: ['password'],
206
+ paths: ['user.email'],
207
+ });
208
+ const input = {
209
+ user: { email: 'test@example.com', password: 'secret' },
210
+ };
211
+ const result = scrubber.scrub(input);
212
+ expect(result.scrubbed).to.be.true;
213
+ expect(result.scrubbedPaths).to.include.members([
214
+ 'user.email',
215
+ 'user.password',
216
+ ]);
217
+ });
218
+ it('reports scrubbed=false when nothing was scrubbed', () => {
219
+ const scrubber = new Scrubber({
220
+ fields: ['password'],
221
+ });
222
+ const input = { name: 'Bob', age: 30 };
223
+ const result = scrubber.scrub(input);
224
+ expect(result.scrubbed).to.be.false;
225
+ expect(result.scrubbedPaths).to.have.length(0);
226
+ });
227
+ });
228
+ describe('Immutability', () => {
229
+ it('does not mutate original object', () => {
230
+ const scrubber = new Scrubber({
231
+ fields: ['password'],
232
+ });
233
+ const input = { user: { password: 'secret', name: 'Bob' } };
234
+ const original = JSON.stringify(input);
235
+ scrubber.scrub(input);
236
+ expect(JSON.stringify(input)).to.equal(original);
237
+ expect(input.user.password).to.equal('secret');
238
+ });
239
+ });
240
+ describe('Custom replacement text', () => {
241
+ it('uses custom replacement string', () => {
242
+ const scrubber = new Scrubber({
243
+ fields: ['password'],
244
+ replacement: '***REDACTED***',
245
+ });
246
+ const input = { password: 'secret' };
247
+ const { data } = scrubber.scrub(input);
248
+ expect(data.password).to.equal('***REDACTED***');
249
+ });
250
+ });
251
+ describe('Edge cases', () => {
252
+ it('handles null values', () => {
253
+ const scrubber = new Scrubber({ fields: ['password'] });
254
+ const input = { user: null };
255
+ const { data } = scrubber.scrub(input);
256
+ expect(data.user).to.be.null;
257
+ });
258
+ it('handles undefined values', () => {
259
+ const scrubber = new Scrubber({ fields: ['password'] });
260
+ const input = { user: undefined };
261
+ const { data } = scrubber.scrub(input);
262
+ expect(data.user).to.be.undefined;
263
+ });
264
+ it('handles empty objects', () => {
265
+ const scrubber = new Scrubber({ fields: ['password'] });
266
+ const input = {};
267
+ const { data } = scrubber.scrub(input);
268
+ expect(data).to.deep.equal({});
269
+ });
270
+ it('handles empty arrays', () => {
271
+ const scrubber = new Scrubber({ fields: ['password'] });
272
+ const input = [];
273
+ const { data } = scrubber.scrub(input);
274
+ expect(data).to.deep.equal([]);
275
+ });
276
+ it('handles primitive values', () => {
277
+ const scrubber = new Scrubber({ fields: ['password'] });
278
+ expect(scrubber.scrub('test').data).to.equal('test');
279
+ expect(scrubber.scrub(123).data).to.equal(123);
280
+ expect(scrubber.scrub(true).data).to.equal(true);
281
+ });
282
+ it('scrubs entire array element by index path', () => {
283
+ // Tests lines 76-78: scrubbing entire array element, not just a field
284
+ const scrubber = new Scrubber({
285
+ paths: ['users[0]', 'items[1]'], // Scrub specific array elements by full path
286
+ });
287
+ const input = {
288
+ users: [
289
+ { name: 'bob', email: 'bob@example.com' }, // Should be scrubbed entirely
290
+ { name: 'alice', email: 'alice@example.com' }, // Not scrubbed
291
+ ],
292
+ items: [
293
+ { id: 1, value: 'keep' }, // Not scrubbed
294
+ { id: 2, value: 'scrub' }, // Should be scrubbed entirely
295
+ ],
296
+ };
297
+ const { data } = scrubber.scrub(input);
298
+ expect(data.users?.[0]).to.equal('[SCRUBBED]');
299
+ expect(data.users?.[1]?.name).to.equal('alice'); // Not scrubbed
300
+ expect(data.items?.[0]?.value).to.equal('keep'); // Not scrubbed
301
+ expect(data.items?.[1]).to.equal('[SCRUBBED]'); // Entire element scrubbed
302
+ });
303
+ it('scrubs array index across all arrays', () => {
304
+ // Tests that index-only paths (e.g., '0') scrub that index in ALL arrays
305
+ const scrubber = new Scrubber({
306
+ paths: ['1'], // Scrub index 1 of ANY array
307
+ });
308
+ const input = {
309
+ users: [
310
+ { name: 'bob' }, // Index 0 - not scrubbed
311
+ { name: 'alice' }, // Index 1 - scrubbed
312
+ { name: 'charlie' }, // Index 2 - not scrubbed
313
+ ],
314
+ teams: [
315
+ { id: 'team-a' }, // Index 0 - not scrubbed
316
+ { id: 'team-b' }, // Index 1 - scrubbed
317
+ ],
318
+ };
319
+ const { data } = scrubber.scrub(input);
320
+ expect(data.users?.[0]?.name).to.equal('bob');
321
+ expect(data.users?.[1]).to.equal('[SCRUBBED]'); // Scrubbed by index
322
+ expect(data.users?.[2]?.name).to.equal('charlie');
323
+ expect(data.teams?.[0]?.id).to.equal('team-a');
324
+ expect(data.teams?.[1]).to.equal('[SCRUBBED]'); // Scrubbed by index
325
+ });
326
+ it('handles deeply nested objects (10+ levels)', () => {
327
+ // Validates discovery doc requirement: "Scrubs nested objects 10+ levels deep"
328
+ const scrubber = new Scrubber({
329
+ fields: ['secret'],
330
+ });
331
+ // Build a 15-level deep object
332
+ const input = { level: 1 };
333
+ let current = input;
334
+ for (let i = 2; i <= 15; i++) {
335
+ current.nested = { level: i };
336
+ current = current.nested;
337
+ }
338
+ // Add secret at the deepest level
339
+ current.secret = 'deep-secret';
340
+ current.public = 'visible';
341
+ const { data } = scrubber.scrub(input);
342
+ // Navigate to the deepest level
343
+ let deepest = data;
344
+ for (let i = 1; i < 15; i++) {
345
+ expect(deepest.level).to.equal(i);
346
+ deepest = deepest.nested;
347
+ }
348
+ // Verify scrubbing worked at 15 levels deep
349
+ expect(deepest.level).to.equal(15);
350
+ expect(deepest.secret).to.equal('[SCRUBBED]');
351
+ expect(deepest.public).to.equal('visible');
352
+ });
353
+ it('handles circular references in deep clone fallback (arrays)', () => {
354
+ // Tests lines 166-172: circular reference deep clone for arrays
355
+ const scrubber = new Scrubber({
356
+ fields: ['password'],
357
+ });
358
+ // Create an object with circular references that will trigger the fallback clone
359
+ const parent = { name: 'parent', password: 'secret' };
360
+ const child1 = { name: 'child1', items: [] };
361
+ const child2 = { name: 'child2', password: 'hidden' };
362
+ // Create circular reference in an array
363
+ child1.items = [child2, parent]; // Array contains parent
364
+ parent.children = [child1]; // Parent contains array with circular ref
365
+ const { data } = scrubber.scrub(parent);
366
+ // Verify scrubbing happened
367
+ expect(data.password).to.equal('[SCRUBBED]');
368
+ expect(data.children?.[0]?.name).to.equal('child1');
369
+ // Verify circular reference was handled in the array
370
+ expect(data.children?.[0]?.items?.[1]).to.equal('[Circular Reference]');
371
+ });
372
+ it('handles arrays with circular self-reference', () => {
373
+ // Additional test for circular array deep cloning
374
+ const scrubber = new Scrubber({
375
+ fields: ['token'],
376
+ });
377
+ const obj = {
378
+ token: 'secret-token',
379
+ list: [{ name: 'item1' }],
380
+ };
381
+ // Array references the parent object
382
+ obj.list.push(obj);
383
+ const { data } = scrubber.scrub(obj);
384
+ expect(data.token).to.equal('[SCRUBBED]');
385
+ expect(data.list?.[0]?.name).to.equal('item1');
386
+ expect(data.list?.[1]).to.equal('[Circular Reference]');
387
+ });
388
+ });
389
+ });
390
+ //# sourceMappingURL=scrubber.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrubber.test.js","sourceRoot":"","sources":["../../../src/core/scrubber.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACxB,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;QACrC,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;YAC9C,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,MAAM,EAAE,CAAC,cAAc,CAAC;aACzB,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG;gBACZ,IAAI,EAAE;oBACJ,OAAO,EAAE;wBACP,QAAQ,EAAE;4BACR,IAAI,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE;yBACpC;qBACF;iBACF;aACF,CAAC;YAEF,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,KAAK,CAC3D,YAAY,CACb,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;YACjD,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,MAAM,EAAE,CAAC,UAAU,CAAC;aACrB,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG;gBACZ,QAAQ,EAAE,SAAS;gBACnB,QAAQ,EAAE,SAAS;gBACnB,QAAQ,EAAE,SAAS;aACpB,CAAC;YAEF,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC7C,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC7C,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;YACvC,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,MAAM,EAAE,CAAC,cAAc,CAAC,EAAE,sDAAsD;aACjF,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG;gBACZ,YAAY,EAAE,QAAQ;gBACtB,UAAU,EAAE,QAAQ;gBACpB,YAAY,EAAE,QAAQ;aACvB,CAAC;YAEF,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YACjD,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC/C,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;QACpC,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;YACpC,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,KAAK,EAAE,CAAC,oBAAoB,CAAC;aAC9B,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG;gBACZ,IAAI,EAAE;oBACJ,OAAO,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,KAAK,EAAE;oBAClD,QAAQ,EAAE,EAAE,KAAK,EAAE,2BAA2B,EAAE;iBACjD;aACF,CAAC;YAEF,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YACvD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;QACzE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;YACrC,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,KAAK,EAAE,CAAC,mBAAmB,CAAC;aAC7B,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG;gBACZ,KAAK,EAAE;oBACL,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE;oBACpC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE;iBACvC;aACF,CAAC;YAEF,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YACzD,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACvC,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;YACxC,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,QAAQ,EAAE,CAAC,wBAAwB,CAAC;aACrC,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC;YACrD,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YAC9C,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC1C,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,QAAQ,EAAE,CAAC,sDAAsD,CAAC;aACnE,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG,EAAE,GAAG,EAAE,kCAAkC,EAAE,CAAC;YAC1D,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YAC1C,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;YACjD,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,QAAQ,EAAE;oBACR,wBAAwB,EAAE,MAAM;oBAChC,sDAAsD,EAAE,QAAQ;iBACjE;aACF,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG;gBACZ,GAAG,EAAE,0CAA0C;aAChD,CAAC;YAEF,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;YACnD,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;YAC9C,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,MAAM,EAAE,CAAC,UAAU,CAAC;aACrB,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG;gBACZ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE;gBACnC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE;aACtC,CAAC;YAEF,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YACjD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YACjD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;YAC/B,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,MAAM,EAAE,CAAC,SAAS,CAAC;aACpB,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG;gBACZ,KAAK,EAAE;oBACL;wBACE,OAAO,EAAE;4BACP,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE;4BACnC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE;yBACtC;qBACF;iBACF;aACF,CAAC;YAEF,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YACtE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;QAC3C,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;YAC9C,MAAM,KAAK,GAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;YACpC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;YAEnB,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;YAC5D,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACxD,MAAM,KAAK,GAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;YACxD,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;YAEnB,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC7C,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC5C,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;YAC9C,MAAM,KAAK,GAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC1D,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;YACnB,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC;YAE5B,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;YACnD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;YAC3D,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,MAAM,EAAE,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,CAAC,YAAY,CAAC;gBACrB,QAAQ,EAAE,CAAC,wBAAwB,CAAC;aACrC,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG;gBACZ,IAAI,EAAE;oBACJ,KAAK,EAAE,iBAAiB,EAAE,aAAa;oBACvC,OAAO,EAAE,gBAAgB,EAAE,cAAc;iBAC1C;gBACD,GAAG,EAAE,kBAAkB,EAAE,gBAAgB;gBACzC,MAAM,EAAE;oBACN,OAAO,EAAE;wBACP,OAAO,EAAE,gBAAgB,EAAE,0BAA0B;qBACtD;iBACF;aACF,CAAC;YAEF,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACtD,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAChD,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAClD,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;YAC/C,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC7D,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;QACrC,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;YAC/B,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,MAAM,EAAE,CAAC,UAAU,CAAC;gBACpB,KAAK,EAAE,CAAC,YAAY,CAAC;aACtB,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG;gBACZ,IAAI,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,QAAQ,EAAE,QAAQ,EAAE;aACxD,CAAC;YAEF,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC;gBAC9C,YAAY;gBACZ,eAAe;aAChB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;YAC1D,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,MAAM,EAAE,CAAC,UAAU,CAAC;aACrB,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC;YACvC,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC;YACpC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;YACzC,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,MAAM,EAAE,CAAC,UAAU,CAAC;aACrB,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YAEvC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAEtB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YACjD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACvC,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;YACxC,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,MAAM,EAAE,CAAC,UAAU,CAAC;gBACpB,WAAW,EAAE,gBAAgB;aAC9B,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;YACrC,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;QAC1B,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;YAC7B,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACxD,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;YAC7B,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC;QAC/B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;YAClC,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACxD,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YAClC,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC;QACpC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;YAC/B,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACxD,MAAM,KAAK,GAAG,EAAE,CAAC;YACjB,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;YAC9B,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACxD,MAAM,KAAK,GAAU,EAAE,CAAC;YACxB,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;YAClC,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACxD,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACrD,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC/C,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;YACnD,sEAAsE;YACtE,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,KAAK,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,6CAA6C;aAC/E,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG;gBACZ,KAAK,EAAE;oBACL,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,8BAA8B;oBACzE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,eAAe;iBAC/D;gBACD,KAAK,EAAE;oBACL,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,eAAe;oBACzC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,8BAA8B;iBAC1D;aACF,CAAC;YAEF,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC/C,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;YAChE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe;YAChE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,0BAA0B;QAC5E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;YAC9C,yEAAyE;YACzE,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,6BAA6B;aAC5C,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG;gBACZ,KAAK,EAAE;oBACL,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,yBAAyB;oBAC1C,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,qBAAqB;oBACxC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,yBAAyB;iBAC/C;gBACD,KAAK,EAAE;oBACL,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,yBAAyB;oBAC3C,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,qBAAqB;iBACxC;aACF,CAAC;YAEF,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC9C,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,oBAAoB;YACpE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAClD,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC/C,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,oBAAoB;QACtE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;YACpD,+EAA+E;YAC/E,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,MAAM,EAAE,CAAC,QAAQ,CAAC;aACnB,CAAC,CAAC;YAEH,+BAA+B;YAC/B,MAAM,KAAK,GAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;YAChC,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC7B,OAAO,CAAC,MAAM,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;gBAC9B,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;YAC3B,CAAC;YACD,kCAAkC;YAClC,OAAO,CAAC,MAAM,GAAG,aAAa,CAAC;YAC/B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;YAE3B,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAEvC,gCAAgC;YAChC,IAAI,OAAO,GAAQ,IAAI,CAAC;YACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC5B,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBAClC,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;YAC3B,CAAC;YAED,4CAA4C;YAC5C,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACnC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC9C,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;YACrE,gEAAgE;YAChE,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,MAAM,EAAE,CAAC,UAAU,CAAC;aACrB,CAAC,CAAC;YAEH,iFAAiF;YACjF,MAAM,MAAM,GAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;YAC3D,MAAM,MAAM,GAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;YAClD,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;YAEtD,wCAAwC;YACxC,MAAM,CAAC,KAAK,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,wBAAwB;YACzD,MAAM,CAAC,QAAQ,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,0CAA0C;YAEtE,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAExC,4BAA4B;YAC5B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC7C,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAEpD,qDAAqD;YACrD,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAC1E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;YACrD,kDAAkD;YAClD,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;gBAC5B,MAAM,EAAE,CAAC,OAAO,CAAC;aAClB,CAAC,CAAC;YAEH,MAAM,GAAG,GAAQ;gBACf,KAAK,EAAE,cAAc;gBACrB,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;aAC1B,CAAC;YACF,qCAAqC;YACrC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAEnB,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAErC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC1C,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC/C,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Configuration for the Scrubber
3
+ *
4
+ * Defines how the scrubber should identify and replace sensitive data.
5
+ * Supports three complementary scrubbing strategies:
6
+ *
7
+ * 1. **Field-based scrubbing** (`fields`): Matches field names at any depth in the object tree
8
+ * 2. **Path-based scrubbing** (`paths`): Matches specific dot-notation paths
9
+ * 3. **Pattern-based scrubbing** (`patterns`): Matches regex patterns in string content
10
+ *
11
+ * All three strategies can be used together for comprehensive data scrubbing.
12
+ *
13
+ * @example Field-based configuration
14
+ * ```typescript
15
+ * const config: ScrubConfig = {
16
+ * fields: ['password', 'apiToken', /api[-_]?key/i], // Strings and regex patterns
17
+ * replacement: '[REDACTED]'
18
+ * };
19
+ * ```
20
+ *
21
+ * @example Path-based configuration
22
+ * ```typescript
23
+ * const config: ScrubConfig = {
24
+ * paths: [
25
+ * 'user.email',
26
+ * 'request.headers.authorization',
27
+ * 'items[0].password' // Array index notation
28
+ * ]
29
+ * };
30
+ * ```
31
+ *
32
+ * @example Pattern-based configuration
33
+ * ```typescript
34
+ * const config: ScrubConfig = {
35
+ * patterns: [
36
+ * /\b\d{3}-\d{2}-\d{4}\b/g, // SSN
37
+ * /\d{4}-\d{4}-\d{4}-\d{4}/g, // Credit card
38
+ * /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}/g // Email
39
+ * ]
40
+ * };
41
+ * ```
42
+ */
43
+ export interface ScrubConfig {
44
+ /**
45
+ * Field-based scrubbing: matches field names at any depth
46
+ *
47
+ * Supports both exact string matches and regular expressions for flexible matching.
48
+ * String matches are case-insensitive and use substring matching.
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * fields: [
53
+ * 'password', // Matches 'password', 'Password', 'old_password', etc.
54
+ * 'apiToken', // Matches 'apiToken', 'api_token', etc.
55
+ * /api[-_]?key/i, // Regex: matches 'api_key', 'api-key', 'apikey' (case insensitive)
56
+ * /^secret$/ // Exact match: only 'secret', not 'my_secret'
57
+ * ]
58
+ * ```
59
+ */
60
+ fields?: (string | RegExp)[];
61
+ /**
62
+ * Path-based scrubbing: matches specific dot-notation paths
63
+ *
64
+ * Use dot notation to target specific fields in nested objects.
65
+ * Supports array index notation (e.g., `items[0].password`).
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * paths: [
70
+ * 'user.email', // Scrubs obj.user.email
71
+ * 'request.headers.authorization', // Nested path
72
+ * 'items[0].secret', // Array index notation
73
+ * 'users[0]' // Scrubs entire array element
74
+ * ]
75
+ * ```
76
+ */
77
+ paths?: string[];
78
+ /**
79
+ * Pattern-based scrubbing: regex patterns for content scrubbing
80
+ *
81
+ * Scans string values and replaces content matching the patterns.
82
+ * Use the global flag (`/pattern/g`) to replace all matches in a string.
83
+ *
84
+ * **Note**: Patterns are applied to string values only, not to field names or paths.
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * patterns: [
89
+ * /\b\d{3}-\d{2}-\d{4}\b/g, // Social Security Number
90
+ * /\d{4}-\d{4}-\d{4}-\d{4}/g, // Credit Card
91
+ * /Bearer\s+[A-Za-z0-9._-]+/g // Bearer tokens
92
+ * ]
93
+ * ```
94
+ */
95
+ patterns?: RegExp[];
96
+ /**
97
+ * Replacement string for scrubbed values
98
+ *
99
+ * @default '[SCRUBBED]'
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * replacement: '[REDACTED]' // Custom replacement text
104
+ * replacement: '***' // Simple masking
105
+ * replacement: '' // Empty string (removes content)
106
+ * ```
107
+ */
108
+ replacement?: string;
109
+ /**
110
+ * Whether to recursively scrub nested objects
111
+ *
112
+ * When `true`, the scrubber traverses the entire object tree.
113
+ * When `false`, only top-level fields are scrubbed.
114
+ *
115
+ * @default true
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * recursive: false // Only scrub top-level fields
120
+ * ```
121
+ */
122
+ recursive?: boolean;
123
+ }
124
+ /**
125
+ * Result of a scrub operation
126
+ *
127
+ * Contains the scrubbed data along with metadata about what was scrubbed.
128
+ *
129
+ * @template T - The type of the scrubbed data (same as input type)
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * const scrubber = new Scrubber({ fields: ['password'] });
134
+ * const result = scrubber.scrub({ user: 'john', password: 'secret' });
135
+ *
136
+ * console.log(result.data); // { user: 'john', password: '[SCRUBBED]' }
137
+ * console.log(result.scrubbed); // true
138
+ * console.log(result.scrubbedPaths); // ['password']
139
+ * ```
140
+ */
141
+ export interface ScrubResult<T> {
142
+ /**
143
+ * The scrubbed data with sensitive values replaced
144
+ *
145
+ * This is a deep clone of the input with scrubbed values replaced.
146
+ * The original input is never mutated.
147
+ */
148
+ data: T;
149
+ /**
150
+ * Whether any scrubbing occurred
151
+ *
152
+ * `true` if at least one value was scrubbed, `false` if no sensitive data was found.
153
+ *
154
+ * Useful for logging or metrics to track scrubbing activity.
155
+ */
156
+ scrubbed: boolean;
157
+ /**
158
+ * Array of paths that were scrubbed
159
+ *
160
+ * Contains dot-notation paths for all fields that were scrubbed.
161
+ * Useful for debugging, auditing, or understanding what data was redacted.
162
+ *
163
+ * @example
164
+ * ```typescript
165
+ * ['password', 'user.email', 'request.headers.authorization']
166
+ * ```
167
+ */
168
+ scrubbedPaths: string[];
169
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Type Safety Tests for Core Scrubber
3
+ *
4
+ * These tests validate that TypeScript types are preserved correctly through scrubbing operations.
5
+ * They use compile-time type assertions to ensure type safety without runtime overhead.
6
+ *
7
+ * Run with: pnpm test (type-checked during pretest)
8
+ */
9
+ export {};