Skip to content
This repository was archived by the owner on Apr 2, 2023. It is now read-only.

Commit 42f4896

Browse files
committed
feat: distinguish between Records and Objects when recursing. Expose visitPII
1 parent cbc7b3d commit 42f4896

File tree

2 files changed

+39
-9
lines changed

2 files changed

+39
-9
lines changed

src/__tests__/unwrapObject.spec.ts

+18
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,22 @@ describe("unwrapObject", () => {
3232
two: 2,
3333
})
3434
})
35+
36+
it("should ignore Map", () => {
37+
const map = new Map()
38+
39+
expect(unwrapObject({ test: map, two: PII(2) })).toEqual({
40+
test: map,
41+
two: 2,
42+
})
43+
})
44+
45+
it("should ignore weird Numbers", () => {
46+
const num = new Number(1)
47+
48+
expect(unwrapObject({ test: num, two: PII(2) })).toEqual({
49+
test: num,
50+
two: 2,
51+
})
52+
})
3553
})

src/pii.ts

+21-9
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export interface PII<T> {
88

99
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1010
const isPIIType = <T>(val: any): val is PII<T> =>
11-
isPojo(val) && val.__brand === "PII"
11+
isRecord(val) && val.__brand === "PII"
1212

1313
export const PII = <T>(val: T, msg = "REDACTED"): PII<T> =>
1414
isPIIType<T>(val)
@@ -83,46 +83,58 @@ export const zip4With = <A, B, C, D, E>(
8383
const proto = Object.prototype
8484
const gpo = Object.getPrototypeOf
8585

86-
// POJO: Plain Old Javascript Object
87-
const isPojo = (obj: unknown): obj is Record<string, unknown> =>
86+
const isRecord = (obj: unknown): obj is Record<string, unknown> =>
8887
obj === null || typeof obj !== "object" ? false : gpo(obj) === proto
8988

89+
// Function, regex, object, Number, String, etc
90+
const isObject = (value: unknown): boolean => {
91+
const type = typeof value
92+
return value != null && (type == "object" || type == "function")
93+
}
94+
9095
// Does not handle Set or Map for now.
91-
const visitPII = <A, T>(
96+
export const visitPII = <A, T>(
9297
input: A,
9398
visitors: {
94-
object: (value: Record<string, unknown>) => T
99+
record: (value: Record<string, unknown>) => T
100+
object: (value: unknown) => T
95101
array: (value: Array<unknown>) => T
96102
primitive: (value: A) => T
97103
},
98104
): T => {
99-
if (isPojo(input)) {
100-
return visitors.object(input)
105+
if (isRecord(input)) {
106+
return visitors.record(input)
101107
}
102108

103109
if (Array.isArray(input)) {
104110
return visitors.array(input)
105111
}
106112

113+
if (isObject(input)) {
114+
return visitors.object(input)
115+
}
116+
107117
return visitors.primitive(input)
108118
}
109119

110120
export const containsPII = (input: unknown): boolean =>
111121
isPIIType(input)
112122
? true
113123
: visitPII(input, {
114-
object: o => Object.values(o).some(containsPII),
124+
record: o => Object.values(o).some(containsPII),
115125
array: a => a.some(containsPII),
116126
primitive: p => isPIIType(p),
127+
object: p => isPIIType(p),
117128
})
118129

119130
export const unwrapObject = (input: unknown): unknown =>
120131
visitPII(isPIIType(input) ? unwrap(input) : input, {
121-
object: o =>
132+
record: o =>
122133
Object.keys(o).reduce((sum, key) => {
123134
sum[key] = unwrapObject(o[key])
124135
return sum
125136
}, {} as Record<string, unknown>),
126137
array: a => a.map(unwrapObject),
127138
primitive: p => p,
139+
object: p => p,
128140
})

0 commit comments

Comments
 (0)