payload-sanitizer 0.0.2 → 0.2.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.
- package/README.md +36 -0
- package/dist/index.cjs +41 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +41 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/payload-sanitizer)
|
|
5
5
|
[](./LICENSE)
|
|
6
6
|
|
|
7
|
+
[](https://github.com/mohit838/payload-sanitizer/actions/workflows/ci.yml)
|
|
8
|
+
|
|
7
9
|
Tiny zero‑dependency sanitizer for JS/TS payloads that removes common junk (empty strings, whitespace-only strings, `null`, `undefined`, optional dash marker, `NaN`) without mutating the original value. Works in both frontend and backend code.
|
|
8
10
|
|
|
9
11
|
## Install
|
|
@@ -45,6 +47,8 @@ console.log(clean);
|
|
|
45
47
|
// }
|
|
46
48
|
```
|
|
47
49
|
|
|
50
|
+
See `examples/` for frontend + backend usage.
|
|
51
|
+
|
|
48
52
|
## Why this instead of validation libraries?
|
|
49
53
|
|
|
50
54
|
Validation libs (e.g., Zod) parse **against a schema**. `payload-sanitizer` just **cleans/normalizes** data with simple rules—no schema required. They pair well:
|
|
@@ -66,8 +70,12 @@ Returns a cleaned clone of `payload` (objects and arrays) without mutating the i
|
|
|
66
70
|
- `trimStrings` (default `true`): `value.trim()` on strings before checks.
|
|
67
71
|
- `cleanArrays` (default `true`): sanitize array items and drop ones that should be removed.
|
|
68
72
|
- `drop` (defaults to `["undefined","null","emptyString","whitespaceString"]`): presets to remove. Presets: `"undefined" | "null" | "emptyString" | "whitespaceString" | "dash" | "nan"`.
|
|
73
|
+
- `dropEmptyObjects` (default `false`): remove objects that become empty after sanitizing.
|
|
74
|
+
- `dropEmptyArrays` (default `false`): remove arrays that become empty after sanitizing.
|
|
69
75
|
- `keepKeys`: key names to always keep even if value looks droppable.
|
|
76
|
+
- `keepPaths`: exact paths to always keep (e.g., `"filters.status"`).
|
|
70
77
|
- `dropKeys`: key names to always remove.
|
|
78
|
+
- `dropPaths`: exact paths to always drop (e.g., `"meta.debug"`).
|
|
71
79
|
- `dropValues`: explicit values to remove (uses `Object.is`).
|
|
72
80
|
- `shouldDrop(value, keyPath)`: custom predicate; return `true` to drop. `keyPath` is an array of keys/indexes from root.
|
|
73
81
|
|
|
@@ -76,6 +84,25 @@ Notes:
|
|
|
76
84
|
- `0`, `false`, and `""` inside `keepKeys` are preserved by design.
|
|
77
85
|
- If everything is dropped, arrays become `[]`, objects become `{}`; primitives are returned as-is.
|
|
78
86
|
|
|
87
|
+
Example with empty-object/array dropping:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
sanitize(payload, {
|
|
91
|
+
drop: ["undefined", "null", "emptyString", "whitespaceString", "dash"],
|
|
92
|
+
dropEmptyObjects: true,
|
|
93
|
+
dropEmptyArrays: true,
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Example with path-based rules:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
sanitize(payload, {
|
|
101
|
+
keepPaths: ["filters.status"],
|
|
102
|
+
dropPaths: ["meta.debug"],
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
79
106
|
### `sanitize.with(baseOptions)`
|
|
80
107
|
|
|
81
108
|
Creates a preconfigured sanitizer.
|
|
@@ -132,6 +159,15 @@ pnpm test
|
|
|
132
159
|
pnpm build
|
|
133
160
|
```
|
|
134
161
|
|
|
162
|
+
## Contributing
|
|
163
|
+
|
|
164
|
+
Contributions are welcome!
|
|
165
|
+
If you have ideas, edge cases, or want to improve performance/docs, please open an issue or PR.
|
|
166
|
+
|
|
167
|
+
- Read: `CONTRIBUTING.md`
|
|
168
|
+
- Report bugs: GitHub Issues
|
|
169
|
+
- Feature requests: GitHub Issues
|
|
170
|
+
|
|
135
171
|
## License
|
|
136
172
|
|
|
137
173
|
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -11,6 +11,8 @@ var DEFAULT_OPTIONS = {
|
|
|
11
11
|
deep: true,
|
|
12
12
|
trimStrings: true,
|
|
13
13
|
cleanArrays: true,
|
|
14
|
+
dropEmptyObjects: false,
|
|
15
|
+
dropEmptyArrays: false,
|
|
14
16
|
drop: DEFAULT_DROP
|
|
15
17
|
};
|
|
16
18
|
function isPlainObject(value) {
|
|
@@ -59,11 +61,37 @@ function shouldDropExact(value, exactValues) {
|
|
|
59
61
|
function hasKey(list, key) {
|
|
60
62
|
return !!list && list.includes(key);
|
|
61
63
|
}
|
|
64
|
+
function toKeyPath(path) {
|
|
65
|
+
if (Array.isArray(path)) return path;
|
|
66
|
+
if (path.trim() === "") return [];
|
|
67
|
+
return path.split(".").map((seg) => {
|
|
68
|
+
const n = Number(seg);
|
|
69
|
+
return Number.isInteger(n) && String(n) === seg ? n : seg;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function pathEquals(a, b) {
|
|
73
|
+
if (a.length !== b.length) return false;
|
|
74
|
+
for (let i = 0; i < a.length; i++) {
|
|
75
|
+
if (a[i] !== b[i]) return false;
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
function hasPath(list, path) {
|
|
80
|
+
if (!list || list.length === 0) return false;
|
|
81
|
+
for (const item of list) {
|
|
82
|
+
if (pathEquals(toKeyPath(item), path)) return true;
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
62
86
|
function sanitizeAny(input, options, path) {
|
|
63
87
|
const normalized = normalizeValue(input, options);
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
if (
|
|
88
|
+
if (hasPath(options.dropPaths, path)) return void 0;
|
|
89
|
+
const isKeptPath = hasPath(options.keepPaths, path);
|
|
90
|
+
if (!isKeptPath) {
|
|
91
|
+
if (shouldDropPreset(normalized, options.drop)) return void 0;
|
|
92
|
+
if (shouldDropExact(normalized, options.dropValues)) return void 0;
|
|
93
|
+
if (options.shouldDrop?.(normalized, path)) return void 0;
|
|
94
|
+
}
|
|
67
95
|
if (Array.isArray(normalized)) {
|
|
68
96
|
if (!options.cleanArrays) return normalized.slice();
|
|
69
97
|
const out = [];
|
|
@@ -71,6 +99,7 @@ function sanitizeAny(input, options, path) {
|
|
|
71
99
|
const next = sanitizeAny(normalized[i], options, path.concat(i));
|
|
72
100
|
if (next !== void 0) out.push(next);
|
|
73
101
|
}
|
|
102
|
+
if (options.dropEmptyArrays && out.length === 0) return void 0;
|
|
74
103
|
return out;
|
|
75
104
|
}
|
|
76
105
|
if (isPlainObject(normalized)) {
|
|
@@ -85,6 +114,8 @@ function sanitizeAny(input, options, path) {
|
|
|
85
114
|
const next = sanitizeAny(v, options, path.concat(k));
|
|
86
115
|
if (next !== void 0) out2[k] = next;
|
|
87
116
|
}
|
|
117
|
+
if (options.dropEmptyObjects && Object.keys(out2).length === 0)
|
|
118
|
+
return void 0;
|
|
88
119
|
return out2;
|
|
89
120
|
}
|
|
90
121
|
const out = {};
|
|
@@ -97,6 +128,8 @@ function sanitizeAny(input, options, path) {
|
|
|
97
128
|
const next = sanitizeAny(v, options, path.concat(k));
|
|
98
129
|
if (next !== void 0) out[k] = next;
|
|
99
130
|
}
|
|
131
|
+
if (options.dropEmptyObjects && Object.keys(out).length === 0)
|
|
132
|
+
return void 0;
|
|
100
133
|
return out;
|
|
101
134
|
}
|
|
102
135
|
return normalized;
|
|
@@ -107,7 +140,11 @@ function sanitize(payload, options = {}) {
|
|
|
107
140
|
...options,
|
|
108
141
|
drop: options.drop ?? DEFAULT_OPTIONS.drop
|
|
109
142
|
};
|
|
110
|
-
const result = sanitizeAny(
|
|
143
|
+
const result = sanitizeAny(
|
|
144
|
+
payload,
|
|
145
|
+
merged,
|
|
146
|
+
[]
|
|
147
|
+
);
|
|
111
148
|
if (result === void 0) {
|
|
112
149
|
if (isPlainObject(payload)) return {};
|
|
113
150
|
if (Array.isArray(payload)) return [];
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["out"],"mappings":"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","file":"index.cjs","sourcesContent":["export type DropPreset =\n | \"null\"\n | \"undefined\"\n | \"emptyString\"\n | \"whitespaceString\"\n | \"dash\"\n | \"nan\";\n\nexport type KeyPath = Array<string | number>;\n\nexport type SanitizeOptions = {\n deep?: boolean;\n trimStrings?: boolean;\n cleanArrays?: boolean;\n drop?: DropPreset[];\n keepKeys?: string[];\n dropKeys?: string[];\n dropValues?: unknown[];\n shouldDrop?: (value: unknown, keyPath: KeyPath) => boolean;\n};\n\nconst DEFAULT_DROP: DropPreset[] = [\n \"undefined\",\n \"null\",\n \"emptyString\",\n \"whitespaceString\",\n];\n\nconst DEFAULT_OPTIONS: Required<\n Pick<SanitizeOptions, \"deep\" | \"trimStrings\" | \"cleanArrays\">\n> & { drop: DropPreset[] } = {\n deep: true,\n trimStrings: true,\n cleanArrays: true,\n drop: DEFAULT_DROP,\n};\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\") return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\nfunction normalizeValue(\n value: unknown,\n opts: Required<typeof DEFAULT_OPTIONS>,\n) {\n if (typeof value === \"string\" && opts.trimStrings) {\n return value.trim();\n }\n return value;\n}\n\nfunction shouldDropPreset(value: unknown, drop: DropPreset[]): boolean {\n for (const d of drop) {\n switch (d) {\n case \"undefined\":\n if (value === undefined) return true;\n break;\n case \"null\":\n if (value === null) return true;\n break;\n case \"emptyString\":\n if (value === \"\") return true;\n break;\n case \"whitespaceString\":\n // only matters if trimStrings is false; but still safe:\n if (typeof value === \"string\" && value.trim() === \"\") return true;\n break;\n case \"dash\":\n if (value === \"-\") return true;\n break;\n case \"nan\":\n if (typeof value === \"number\" && Number.isNaN(value)) return true;\n break;\n }\n }\n return false;\n}\n\nfunction shouldDropExact(value: unknown, exactValues?: unknown[]): boolean {\n if (!exactValues || exactValues.length === 0) return false;\n for (const v of exactValues) {\n if (Object.is(value, v)) return true;\n }\n return false;\n}\n\nfunction hasKey(list: string[] | undefined, key: string): boolean {\n return !!list && list.includes(key);\n}\n\nfunction sanitizeAny(\n input: unknown,\n options: Required<typeof DEFAULT_OPTIONS> & SanitizeOptions,\n path: KeyPath,\n): unknown {\n const normalized = normalizeValue(input, options);\n\n if (shouldDropPreset(normalized, options.drop)) return undefined;\n if (shouldDropExact(normalized, options.dropValues)) return undefined;\n if (options.shouldDrop?.(normalized, path)) return undefined;\n\n if (Array.isArray(normalized)) {\n if (!options.cleanArrays) return normalized.slice();\n const out: unknown[] = [];\n for (let i = 0; i < normalized.length; i++) {\n const next = sanitizeAny(normalized[i], options, path.concat(i));\n if (next !== undefined) out.push(next);\n }\n return out;\n }\n\n if (isPlainObject(normalized)) {\n if (!options.deep) {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(normalized)) {\n if (hasKey(options.dropKeys, k)) continue;\n if (hasKey(options.keepKeys, k)) {\n out[k] = v;\n continue;\n }\n const next = sanitizeAny(v, options, path.concat(k));\n if (next !== undefined) out[k] = next;\n }\n return out;\n }\n\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(normalized)) {\n if (hasKey(options.dropKeys, k)) continue;\n\n if (hasKey(options.keepKeys, k)) {\n out[k] = v;\n continue;\n }\n\n const next = sanitizeAny(v, options, path.concat(k));\n if (next !== undefined) out[k] = next;\n }\n return out;\n }\n\n return normalized;\n}\n\nexport function sanitize<T>(payload: T, options: SanitizeOptions = {}): T {\n const merged = {\n ...DEFAULT_OPTIONS,\n ...options,\n drop: options.drop ?? DEFAULT_OPTIONS.drop,\n };\n\n const result = sanitizeAny(payload, merged, []);\n\n if (result === undefined) {\n if (isPlainObject(payload)) return {} as T;\n if (Array.isArray(payload)) return [] as T;\n return payload;\n }\n\n return result as T;\n}\n\ntype SanitizerFn = {\n <T>(payload: T, options?: SanitizeOptions): T;\n with: (baseOptions?: SanitizeOptions) => ReturnType<typeof createSanitizer>;\n};\n\nexport const createSanitizer = (baseOptions: SanitizeOptions = {}) => {\n return <T>(payload: T, options: SanitizeOptions = {}) =>\n sanitize(payload, { ...baseOptions, ...options });\n};\n\n(sanitize as SanitizerFn).with = (baseOptions: SanitizeOptions = {}) =>\n createSanitizer(baseOptions);\n\nexport const sanitizeWith = (sanitize as SanitizerFn).with;\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["out"],"mappings":"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","file":"index.cjs","sourcesContent":["export type DropPreset =\n | \"null\"\n | \"undefined\"\n | \"emptyString\"\n | \"whitespaceString\"\n | \"dash\"\n | \"nan\";\n\nexport type KeyPath = Array<string | number>;\n\nexport type SanitizeOptions = {\n deep?: boolean;\n trimStrings?: boolean;\n cleanArrays?: boolean;\n drop?: DropPreset[];\n keepKeys?: string[];\n dropKeys?: string[];\n dropValues?: unknown[];\n /**\n * Always keep these paths (even if value is droppable).\n * Supports \"a.b.c\" or [\"a\",\"b\",\"c\"].\n */\n keepPaths?: Array<string | KeyPath>;\n /**\n * Always drop these paths (even if value is meaningful).\n * Supports \"a.b.c\" or [\"a\",\"b\",\"c\"].\n */\n dropPaths?: Array<string | KeyPath>;\n shouldDrop?: (value: unknown, keyPath: KeyPath) => boolean;\n /**\n * Drop objects that become empty after sanitizing.\n * Default: false\n */\n dropEmptyObjects?: boolean;\n\n /**\n * Drop arrays that become empty after sanitizing.\n * Default: false\n */\n dropEmptyArrays?: boolean;\n};\n\nconst DEFAULT_DROP: DropPreset[] = [\n \"undefined\",\n \"null\",\n \"emptyString\",\n \"whitespaceString\",\n];\n\nconst DEFAULT_OPTIONS = {\n deep: true,\n trimStrings: true,\n cleanArrays: true,\n dropEmptyObjects: false,\n dropEmptyArrays: false,\n drop: DEFAULT_DROP,\n} satisfies Required<\n Pick<\n SanitizeOptions,\n | \"deep\"\n | \"trimStrings\"\n | \"cleanArrays\"\n | \"dropEmptyObjects\"\n | \"dropEmptyArrays\"\n >\n> & { drop: DropPreset[] };\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\") return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\nfunction normalizeValue(value: unknown, opts: typeof DEFAULT_OPTIONS) {\n if (typeof value === \"string\" && opts.trimStrings) {\n return value.trim();\n }\n return value;\n}\n\nfunction shouldDropPreset(value: unknown, drop: DropPreset[]): boolean {\n for (const d of drop) {\n switch (d) {\n case \"undefined\":\n if (value === undefined) return true;\n break;\n case \"null\":\n if (value === null) return true;\n break;\n case \"emptyString\":\n if (value === \"\") return true;\n break;\n case \"whitespaceString\":\n // only matters if trimStrings is false; but still safe:\n if (typeof value === \"string\" && value.trim() === \"\") return true;\n break;\n case \"dash\":\n if (value === \"-\") return true;\n break;\n case \"nan\":\n if (typeof value === \"number\" && Number.isNaN(value)) return true;\n break;\n }\n }\n return false;\n}\n\nfunction shouldDropExact(value: unknown, exactValues?: unknown[]): boolean {\n if (!exactValues || exactValues.length === 0) return false;\n for (const v of exactValues) {\n if (Object.is(value, v)) return true;\n }\n return false;\n}\n\nfunction hasKey(list: string[] | undefined, key: string): boolean {\n return !!list && list.includes(key);\n}\n\nfunction toKeyPath(path: string | KeyPath): KeyPath {\n if (Array.isArray(path)) return path;\n if (path.trim() === \"\") return [];\n return path.split(\".\").map((seg) => {\n const n = Number(seg);\n return Number.isInteger(n) && String(n) === seg ? n : seg;\n });\n}\n\nfunction pathEquals(a: KeyPath, b: KeyPath): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\nfunction hasPath(list: Array<string | KeyPath> | undefined, path: KeyPath): boolean {\n if (!list || list.length === 0) return false;\n for (const item of list) {\n if (pathEquals(toKeyPath(item), path)) return true;\n }\n return false;\n}\n\nfunction sanitizeAny(\n input: unknown,\n options: typeof DEFAULT_OPTIONS & SanitizeOptions,\n path: KeyPath,\n): unknown {\n const normalized = normalizeValue(input, options);\n\n // Path-based overrides (highest priority)\n if (hasPath(options.dropPaths, path)) return undefined;\n\n const isKeptPath = hasPath(options.keepPaths, path);\n\n if (!isKeptPath) {\n if (shouldDropPreset(normalized, options.drop)) return undefined;\n if (shouldDropExact(normalized, options.dropValues)) return undefined;\n if (options.shouldDrop?.(normalized, path)) return undefined;\n }\n\n if (Array.isArray(normalized)) {\n if (!options.cleanArrays) return normalized.slice();\n const out: unknown[] = [];\n for (let i = 0; i < normalized.length; i++) {\n const next = sanitizeAny(normalized[i], options, path.concat(i));\n if (next !== undefined) out.push(next);\n }\n if (options.dropEmptyArrays && out.length === 0) return undefined;\n return out;\n }\n\n if (isPlainObject(normalized)) {\n if (!options.deep) {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(normalized)) {\n if (hasKey(options.dropKeys, k)) continue;\n if (hasKey(options.keepKeys, k)) {\n out[k] = v;\n continue;\n }\n const next = sanitizeAny(v, options, path.concat(k));\n if (next !== undefined) out[k] = next;\n }\n if (options.dropEmptyObjects && Object.keys(out).length === 0)\n return undefined;\n return out;\n }\n\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(normalized)) {\n if (hasKey(options.dropKeys, k)) continue;\n\n if (hasKey(options.keepKeys, k)) {\n out[k] = v;\n continue;\n }\n\n const next = sanitizeAny(v, options, path.concat(k));\n if (next !== undefined) out[k] = next;\n }\n if (options.dropEmptyObjects && Object.keys(out).length === 0)\n return undefined;\n return out;\n }\n\n return normalized;\n}\n\nexport function sanitize<T>(payload: T, options: SanitizeOptions = {}): T {\n const merged = {\n ...DEFAULT_OPTIONS,\n ...options,\n drop: options.drop ?? DEFAULT_OPTIONS.drop,\n };\n\n const result = sanitizeAny(\n payload,\n merged as typeof DEFAULT_OPTIONS & SanitizeOptions,\n [],\n );\n\n if (result === undefined) {\n if (isPlainObject(payload)) return {} as T;\n if (Array.isArray(payload)) return [] as T;\n return payload;\n }\n\n return result as T;\n}\n\ntype SanitizerFn = {\n <T>(payload: T, options?: SanitizeOptions): T;\n with: (baseOptions?: SanitizeOptions) => ReturnType<typeof createSanitizer>;\n};\n\nexport const createSanitizer = (baseOptions: SanitizeOptions = {}) => {\n return <T>(payload: T, options: SanitizeOptions = {}) =>\n sanitize(payload, { ...baseOptions, ...options });\n};\n\n(sanitize as SanitizerFn).with = (baseOptions: SanitizeOptions = {}) =>\n createSanitizer(baseOptions);\n\nexport const sanitizeWith = (sanitize as SanitizerFn).with;\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -8,7 +8,27 @@ type SanitizeOptions = {
|
|
|
8
8
|
keepKeys?: string[];
|
|
9
9
|
dropKeys?: string[];
|
|
10
10
|
dropValues?: unknown[];
|
|
11
|
+
/**
|
|
12
|
+
* Always keep these paths (even if value is droppable).
|
|
13
|
+
* Supports "a.b.c" or ["a","b","c"].
|
|
14
|
+
*/
|
|
15
|
+
keepPaths?: Array<string | KeyPath>;
|
|
16
|
+
/**
|
|
17
|
+
* Always drop these paths (even if value is meaningful).
|
|
18
|
+
* Supports "a.b.c" or ["a","b","c"].
|
|
19
|
+
*/
|
|
20
|
+
dropPaths?: Array<string | KeyPath>;
|
|
11
21
|
shouldDrop?: (value: unknown, keyPath: KeyPath) => boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Drop objects that become empty after sanitizing.
|
|
24
|
+
* Default: false
|
|
25
|
+
*/
|
|
26
|
+
dropEmptyObjects?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Drop arrays that become empty after sanitizing.
|
|
29
|
+
* Default: false
|
|
30
|
+
*/
|
|
31
|
+
dropEmptyArrays?: boolean;
|
|
12
32
|
};
|
|
13
33
|
declare function sanitize<T>(payload: T, options?: SanitizeOptions): T;
|
|
14
34
|
declare const createSanitizer: (baseOptions?: SanitizeOptions) => <T>(payload: T, options?: SanitizeOptions) => T;
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,27 @@ type SanitizeOptions = {
|
|
|
8
8
|
keepKeys?: string[];
|
|
9
9
|
dropKeys?: string[];
|
|
10
10
|
dropValues?: unknown[];
|
|
11
|
+
/**
|
|
12
|
+
* Always keep these paths (even if value is droppable).
|
|
13
|
+
* Supports "a.b.c" or ["a","b","c"].
|
|
14
|
+
*/
|
|
15
|
+
keepPaths?: Array<string | KeyPath>;
|
|
16
|
+
/**
|
|
17
|
+
* Always drop these paths (even if value is meaningful).
|
|
18
|
+
* Supports "a.b.c" or ["a","b","c"].
|
|
19
|
+
*/
|
|
20
|
+
dropPaths?: Array<string | KeyPath>;
|
|
11
21
|
shouldDrop?: (value: unknown, keyPath: KeyPath) => boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Drop objects that become empty after sanitizing.
|
|
24
|
+
* Default: false
|
|
25
|
+
*/
|
|
26
|
+
dropEmptyObjects?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Drop arrays that become empty after sanitizing.
|
|
29
|
+
* Default: false
|
|
30
|
+
*/
|
|
31
|
+
dropEmptyArrays?: boolean;
|
|
12
32
|
};
|
|
13
33
|
declare function sanitize<T>(payload: T, options?: SanitizeOptions): T;
|
|
14
34
|
declare const createSanitizer: (baseOptions?: SanitizeOptions) => <T>(payload: T, options?: SanitizeOptions) => T;
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,8 @@ var DEFAULT_OPTIONS = {
|
|
|
9
9
|
deep: true,
|
|
10
10
|
trimStrings: true,
|
|
11
11
|
cleanArrays: true,
|
|
12
|
+
dropEmptyObjects: false,
|
|
13
|
+
dropEmptyArrays: false,
|
|
12
14
|
drop: DEFAULT_DROP
|
|
13
15
|
};
|
|
14
16
|
function isPlainObject(value) {
|
|
@@ -57,11 +59,37 @@ function shouldDropExact(value, exactValues) {
|
|
|
57
59
|
function hasKey(list, key) {
|
|
58
60
|
return !!list && list.includes(key);
|
|
59
61
|
}
|
|
62
|
+
function toKeyPath(path) {
|
|
63
|
+
if (Array.isArray(path)) return path;
|
|
64
|
+
if (path.trim() === "") return [];
|
|
65
|
+
return path.split(".").map((seg) => {
|
|
66
|
+
const n = Number(seg);
|
|
67
|
+
return Number.isInteger(n) && String(n) === seg ? n : seg;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function pathEquals(a, b) {
|
|
71
|
+
if (a.length !== b.length) return false;
|
|
72
|
+
for (let i = 0; i < a.length; i++) {
|
|
73
|
+
if (a[i] !== b[i]) return false;
|
|
74
|
+
}
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
function hasPath(list, path) {
|
|
78
|
+
if (!list || list.length === 0) return false;
|
|
79
|
+
for (const item of list) {
|
|
80
|
+
if (pathEquals(toKeyPath(item), path)) return true;
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
60
84
|
function sanitizeAny(input, options, path) {
|
|
61
85
|
const normalized = normalizeValue(input, options);
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
if (
|
|
86
|
+
if (hasPath(options.dropPaths, path)) return void 0;
|
|
87
|
+
const isKeptPath = hasPath(options.keepPaths, path);
|
|
88
|
+
if (!isKeptPath) {
|
|
89
|
+
if (shouldDropPreset(normalized, options.drop)) return void 0;
|
|
90
|
+
if (shouldDropExact(normalized, options.dropValues)) return void 0;
|
|
91
|
+
if (options.shouldDrop?.(normalized, path)) return void 0;
|
|
92
|
+
}
|
|
65
93
|
if (Array.isArray(normalized)) {
|
|
66
94
|
if (!options.cleanArrays) return normalized.slice();
|
|
67
95
|
const out = [];
|
|
@@ -69,6 +97,7 @@ function sanitizeAny(input, options, path) {
|
|
|
69
97
|
const next = sanitizeAny(normalized[i], options, path.concat(i));
|
|
70
98
|
if (next !== void 0) out.push(next);
|
|
71
99
|
}
|
|
100
|
+
if (options.dropEmptyArrays && out.length === 0) return void 0;
|
|
72
101
|
return out;
|
|
73
102
|
}
|
|
74
103
|
if (isPlainObject(normalized)) {
|
|
@@ -83,6 +112,8 @@ function sanitizeAny(input, options, path) {
|
|
|
83
112
|
const next = sanitizeAny(v, options, path.concat(k));
|
|
84
113
|
if (next !== void 0) out2[k] = next;
|
|
85
114
|
}
|
|
115
|
+
if (options.dropEmptyObjects && Object.keys(out2).length === 0)
|
|
116
|
+
return void 0;
|
|
86
117
|
return out2;
|
|
87
118
|
}
|
|
88
119
|
const out = {};
|
|
@@ -95,6 +126,8 @@ function sanitizeAny(input, options, path) {
|
|
|
95
126
|
const next = sanitizeAny(v, options, path.concat(k));
|
|
96
127
|
if (next !== void 0) out[k] = next;
|
|
97
128
|
}
|
|
129
|
+
if (options.dropEmptyObjects && Object.keys(out).length === 0)
|
|
130
|
+
return void 0;
|
|
98
131
|
return out;
|
|
99
132
|
}
|
|
100
133
|
return normalized;
|
|
@@ -105,7 +138,11 @@ function sanitize(payload, options = {}) {
|
|
|
105
138
|
...options,
|
|
106
139
|
drop: options.drop ?? DEFAULT_OPTIONS.drop
|
|
107
140
|
};
|
|
108
|
-
const result = sanitizeAny(
|
|
141
|
+
const result = sanitizeAny(
|
|
142
|
+
payload,
|
|
143
|
+
merged,
|
|
144
|
+
[]
|
|
145
|
+
);
|
|
109
146
|
if (result === void 0) {
|
|
110
147
|
if (isPlainObject(payload)) return {};
|
|
111
148
|
if (Array.isArray(payload)) return [];
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["out"],"mappings":"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","file":"index.js","sourcesContent":["export type DropPreset =\n | \"null\"\n | \"undefined\"\n | \"emptyString\"\n | \"whitespaceString\"\n | \"dash\"\n | \"nan\";\n\nexport type KeyPath = Array<string | number>;\n\nexport type SanitizeOptions = {\n deep?: boolean;\n trimStrings?: boolean;\n cleanArrays?: boolean;\n drop?: DropPreset[];\n keepKeys?: string[];\n dropKeys?: string[];\n dropValues?: unknown[];\n shouldDrop?: (value: unknown, keyPath: KeyPath) => boolean;\n};\n\nconst DEFAULT_DROP: DropPreset[] = [\n \"undefined\",\n \"null\",\n \"emptyString\",\n \"whitespaceString\",\n];\n\nconst DEFAULT_OPTIONS: Required<\n Pick<SanitizeOptions, \"deep\" | \"trimStrings\" | \"cleanArrays\">\n> & { drop: DropPreset[] } = {\n deep: true,\n trimStrings: true,\n cleanArrays: true,\n drop: DEFAULT_DROP,\n};\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\") return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\nfunction normalizeValue(\n value: unknown,\n opts: Required<typeof DEFAULT_OPTIONS>,\n) {\n if (typeof value === \"string\" && opts.trimStrings) {\n return value.trim();\n }\n return value;\n}\n\nfunction shouldDropPreset(value: unknown, drop: DropPreset[]): boolean {\n for (const d of drop) {\n switch (d) {\n case \"undefined\":\n if (value === undefined) return true;\n break;\n case \"null\":\n if (value === null) return true;\n break;\n case \"emptyString\":\n if (value === \"\") return true;\n break;\n case \"whitespaceString\":\n // only matters if trimStrings is false; but still safe:\n if (typeof value === \"string\" && value.trim() === \"\") return true;\n break;\n case \"dash\":\n if (value === \"-\") return true;\n break;\n case \"nan\":\n if (typeof value === \"number\" && Number.isNaN(value)) return true;\n break;\n }\n }\n return false;\n}\n\nfunction shouldDropExact(value: unknown, exactValues?: unknown[]): boolean {\n if (!exactValues || exactValues.length === 0) return false;\n for (const v of exactValues) {\n if (Object.is(value, v)) return true;\n }\n return false;\n}\n\nfunction hasKey(list: string[] | undefined, key: string): boolean {\n return !!list && list.includes(key);\n}\n\nfunction sanitizeAny(\n input: unknown,\n options: Required<typeof DEFAULT_OPTIONS> & SanitizeOptions,\n path: KeyPath,\n): unknown {\n const normalized = normalizeValue(input, options);\n\n if (shouldDropPreset(normalized, options.drop)) return undefined;\n if (shouldDropExact(normalized, options.dropValues)) return undefined;\n if (options.shouldDrop?.(normalized, path)) return undefined;\n\n if (Array.isArray(normalized)) {\n if (!options.cleanArrays) return normalized.slice();\n const out: unknown[] = [];\n for (let i = 0; i < normalized.length; i++) {\n const next = sanitizeAny(normalized[i], options, path.concat(i));\n if (next !== undefined) out.push(next);\n }\n return out;\n }\n\n if (isPlainObject(normalized)) {\n if (!options.deep) {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(normalized)) {\n if (hasKey(options.dropKeys, k)) continue;\n if (hasKey(options.keepKeys, k)) {\n out[k] = v;\n continue;\n }\n const next = sanitizeAny(v, options, path.concat(k));\n if (next !== undefined) out[k] = next;\n }\n return out;\n }\n\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(normalized)) {\n if (hasKey(options.dropKeys, k)) continue;\n\n if (hasKey(options.keepKeys, k)) {\n out[k] = v;\n continue;\n }\n\n const next = sanitizeAny(v, options, path.concat(k));\n if (next !== undefined) out[k] = next;\n }\n return out;\n }\n\n return normalized;\n}\n\nexport function sanitize<T>(payload: T, options: SanitizeOptions = {}): T {\n const merged = {\n ...DEFAULT_OPTIONS,\n ...options,\n drop: options.drop ?? DEFAULT_OPTIONS.drop,\n };\n\n const result = sanitizeAny(payload, merged, []);\n\n if (result === undefined) {\n if (isPlainObject(payload)) return {} as T;\n if (Array.isArray(payload)) return [] as T;\n return payload;\n }\n\n return result as T;\n}\n\ntype SanitizerFn = {\n <T>(payload: T, options?: SanitizeOptions): T;\n with: (baseOptions?: SanitizeOptions) => ReturnType<typeof createSanitizer>;\n};\n\nexport const createSanitizer = (baseOptions: SanitizeOptions = {}) => {\n return <T>(payload: T, options: SanitizeOptions = {}) =>\n sanitize(payload, { ...baseOptions, ...options });\n};\n\n(sanitize as SanitizerFn).with = (baseOptions: SanitizeOptions = {}) =>\n createSanitizer(baseOptions);\n\nexport const sanitizeWith = (sanitize as SanitizerFn).with;\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["out"],"mappings":";AA0CA,IAAM,YAAA,GAA6B;AAAA,EACjC,WAAA;AAAA,EACA,MAAA;AAAA,EACA,aAAA;AAAA,EACA;AACF,CAAA;AAEA,IAAM,eAAA,GAAkB;AAAA,EACtB,IAAA,EAAM,IAAA;AAAA,EACN,WAAA,EAAa,IAAA;AAAA,EACb,WAAA,EAAa,IAAA;AAAA,EACb,gBAAA,EAAkB,KAAA;AAAA,EAClB,eAAA,EAAiB,KAAA;AAAA,EACjB,IAAA,EAAM;AACR,CAAA;AAWA,SAAS,cAAc,KAAA,EAAkD;AACvE,EAAA,IAAI,KAAA,KAAU,IAAA,IAAQ,OAAO,KAAA,KAAU,UAAU,OAAO,KAAA;AACxD,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,cAAA,CAAe,KAAK,CAAA;AACzC,EAAA,OAAO,KAAA,KAAU,MAAA,CAAO,SAAA,IAAa,KAAA,KAAU,IAAA;AACjD;AAEA,SAAS,cAAA,CAAe,OAAgB,IAAA,EAA8B;AACpE,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,IAAA,CAAK,WAAA,EAAa;AACjD,IAAA,OAAO,MAAM,IAAA,EAAK;AAAA,EACpB;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,gBAAA,CAAiB,OAAgB,IAAA,EAA6B;AACrE,EAAA,KAAA,MAAW,KAAK,IAAA,EAAM;AACpB,IAAA,QAAQ,CAAA;AAAG,MACT,KAAK,WAAA;AACH,QAAA,IAAI,KAAA,KAAU,QAAW,OAAO,IAAA;AAChC,QAAA;AAAA,MACF,KAAK,MAAA;AACH,QAAA,IAAI,KAAA,KAAU,MAAM,OAAO,IAAA;AAC3B,QAAA;AAAA,MACF,KAAK,aAAA;AACH,QAAA,IAAI,KAAA,KAAU,IAAI,OAAO,IAAA;AACzB,QAAA;AAAA,MACF,KAAK,kBAAA;AAEH,QAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,MAAM,IAAA,EAAK,KAAM,IAAI,OAAO,IAAA;AAC7D,QAAA;AAAA,MACF,KAAK,MAAA;AACH,QAAA,IAAI,KAAA,KAAU,KAAK,OAAO,IAAA;AAC1B,QAAA;AAAA,MACF,KAAK,KAAA;AACH,QAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,OAAO,KAAA,CAAM,KAAK,GAAG,OAAO,IAAA;AAC7D,QAAA;AAAA;AACJ,EACF;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,eAAA,CAAgB,OAAgB,WAAA,EAAkC;AACzE,EAAA,IAAI,CAAC,WAAA,IAAe,WAAA,CAAY,MAAA,KAAW,GAAG,OAAO,KAAA;AACrD,EAAA,KAAA,MAAW,KAAK,WAAA,EAAa;AAC3B,IAAA,IAAI,MAAA,CAAO,EAAA,CAAG,KAAA,EAAO,CAAC,GAAG,OAAO,IAAA;AAAA,EAClC;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,MAAA,CAAO,MAA4B,GAAA,EAAsB;AAChE,EAAA,OAAO,CAAC,CAAC,IAAA,IAAQ,IAAA,CAAK,SAAS,GAAG,CAAA;AACpC;AAEA,SAAS,UAAU,IAAA,EAAiC;AAClD,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,EAAG,OAAO,IAAA;AAChC,EAAA,IAAI,IAAA,CAAK,IAAA,EAAK,KAAM,EAAA,SAAW,EAAC;AAChC,EAAA,OAAO,KAAK,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAI,CAAC,GAAA,KAAQ;AAClC,IAAA,MAAM,CAAA,GAAI,OAAO,GAAG,CAAA;AACpB,IAAA,OAAO,MAAA,CAAO,UAAU,CAAC,CAAA,IAAK,OAAO,CAAC,CAAA,KAAM,MAAM,CAAA,GAAI,GAAA;AAAA,EACxD,CAAC,CAAA;AACH;AAEA,SAAS,UAAA,CAAW,GAAY,CAAA,EAAqB;AACnD,EAAA,IAAI,CAAA,CAAE,MAAA,KAAW,CAAA,CAAE,MAAA,EAAQ,OAAO,KAAA;AAClC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,CAAE,QAAQ,CAAA,EAAA,EAAK;AACjC,IAAA,IAAI,EAAE,CAAC,CAAA,KAAM,CAAA,CAAE,CAAC,GAAG,OAAO,KAAA;AAAA,EAC5B;AACA,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,OAAA,CAAQ,MAA2C,IAAA,EAAwB;AAClF,EAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,CAAK,MAAA,KAAW,GAAG,OAAO,KAAA;AACvC,EAAA,KAAA,MAAW,QAAQ,IAAA,EAAM;AACvB,IAAA,IAAI,WAAW,SAAA,CAAU,IAAI,CAAA,EAAG,IAAI,GAAG,OAAO,IAAA;AAAA,EAChD;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,WAAA,CACP,KAAA,EACA,OAAA,EACA,IAAA,EACS;AACT,EAAA,MAAM,UAAA,GAAa,cAAA,CAAe,KAAA,EAAO,OAAO,CAAA;AAGhD,EAAA,IAAI,OAAA,CAAQ,OAAA,CAAQ,SAAA,EAAW,IAAI,GAAG,OAAO,MAAA;AAE7C,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,OAAA,CAAQ,SAAA,EAAW,IAAI,CAAA;AAElD,EAAA,IAAI,CAAC,UAAA,EAAY;AACf,IAAA,IAAI,gBAAA,CAAiB,UAAA,EAAY,OAAA,CAAQ,IAAI,GAAG,OAAO,MAAA;AACvD,IAAA,IAAI,eAAA,CAAgB,UAAA,EAAY,OAAA,CAAQ,UAAU,GAAG,OAAO,MAAA;AAC5D,IAAA,IAAI,OAAA,CAAQ,UAAA,GAAa,UAAA,EAAY,IAAI,GAAG,OAAO,MAAA;AAAA,EACrD;AAEA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,UAAU,CAAA,EAAG;AAC7B,IAAA,IAAI,CAAC,OAAA,CAAQ,WAAA,EAAa,OAAO,WAAW,KAAA,EAAM;AAClD,IAAA,MAAM,MAAiB,EAAC;AACxB,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,UAAA,CAAW,QAAQ,CAAA,EAAA,EAAK;AAC1C,MAAA,MAAM,IAAA,GAAO,YAAY,UAAA,CAAW,CAAC,GAAG,OAAA,EAAS,IAAA,CAAK,MAAA,CAAO,CAAC,CAAC,CAAA;AAC/D,MAAA,IAAI,IAAA,KAAS,MAAA,EAAW,GAAA,CAAI,IAAA,CAAK,IAAI,CAAA;AAAA,IACvC;AACA,IAAA,IAAI,OAAA,CAAQ,eAAA,IAAmB,GAAA,CAAI,MAAA,KAAW,GAAG,OAAO,MAAA;AACxD,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,IAAI,aAAA,CAAc,UAAU,CAAA,EAAG;AAC7B,IAAA,IAAI,CAAC,QAAQ,IAAA,EAAM;AACjB,MAAA,MAAMA,OAA+B,EAAC;AACtC,MAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,KAAK,MAAA,CAAO,OAAA,CAAQ,UAAU,CAAA,EAAG;AAC/C,QAAA,IAAI,MAAA,CAAO,OAAA,CAAQ,QAAA,EAAU,CAAC,CAAA,EAAG;AACjC,QAAA,IAAI,MAAA,CAAO,OAAA,CAAQ,QAAA,EAAU,CAAC,CAAA,EAAG;AAC/B,UAAAA,IAAAA,CAAI,CAAC,CAAA,GAAI,CAAA;AACT,UAAA;AAAA,QACF;AACA,QAAA,MAAM,OAAO,WAAA,CAAY,CAAA,EAAG,SAAS,IAAA,CAAK,MAAA,CAAO,CAAC,CAAC,CAAA;AACnD,QAAA,IAAI,IAAA,KAAS,MAAA,EAAWA,IAAAA,CAAI,CAAC,CAAA,GAAI,IAAA;AAAA,MACnC;AACA,MAAA,IAAI,QAAQ,gBAAA,IAAoB,MAAA,CAAO,IAAA,CAAKA,IAAG,EAAE,MAAA,KAAW,CAAA;AAC1D,QAAA,OAAO,MAAA;AACT,MAAA,OAAOA,IAAAA;AAAA,IACT;AAEA,IAAA,MAAM,MAA+B,EAAC;AACtC,IAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,KAAK,MAAA,CAAO,OAAA,CAAQ,UAAU,CAAA,EAAG;AAC/C,MAAA,IAAI,MAAA,CAAO,OAAA,CAAQ,QAAA,EAAU,CAAC,CAAA,EAAG;AAEjC,MAAA,IAAI,MAAA,CAAO,OAAA,CAAQ,QAAA,EAAU,CAAC,CAAA,EAAG;AAC/B,QAAA,GAAA,CAAI,CAAC,CAAA,GAAI,CAAA;AACT,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,OAAO,WAAA,CAAY,CAAA,EAAG,SAAS,IAAA,CAAK,MAAA,CAAO,CAAC,CAAC,CAAA;AACnD,MAAA,IAAI,IAAA,KAAS,MAAA,EAAW,GAAA,CAAI,CAAC,CAAA,GAAI,IAAA;AAAA,IACnC;AACA,IAAA,IAAI,QAAQ,gBAAA,IAAoB,MAAA,CAAO,IAAA,CAAK,GAAG,EAAE,MAAA,KAAW,CAAA;AAC1D,MAAA,OAAO,MAAA;AACT,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,OAAO,UAAA;AACT;AAEO,SAAS,QAAA,CAAY,OAAA,EAAY,OAAA,GAA2B,EAAC,EAAM;AACxE,EAAA,MAAM,MAAA,GAAS;AAAA,IACb,GAAG,eAAA;AAAA,IACH,GAAG,OAAA;AAAA,IACH,IAAA,EAAM,OAAA,CAAQ,IAAA,IAAQ,eAAA,CAAgB;AAAA,GACxC;AAEA,EAAA,MAAM,MAAA,GAAS,WAAA;AAAA,IACb,OAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAC,GACH;AAEA,EAAA,IAAI,WAAW,MAAA,EAAW;AACxB,IAAA,IAAI,aAAA,CAAc,OAAO,CAAA,EAAG,OAAO,EAAC;AACpC,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,SAAU,EAAC;AACpC,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,OAAO,MAAA;AACT;AAOO,IAAM,eAAA,GAAkB,CAAC,WAAA,GAA+B,EAAC,KAAM;AACpE,EAAA,OAAO,CAAI,OAAA,EAAY,OAAA,GAA2B,EAAC,KACjD,QAAA,CAAS,OAAA,EAAS,EAAE,GAAG,WAAA,EAAa,GAAG,OAAA,EAAS,CAAA;AACpD;AAEC,QAAA,CAAyB,OAAO,CAAC,WAAA,GAA+B,EAAC,KAChE,gBAAgB,WAAW,CAAA;AAEtB,IAAM,eAAgB,QAAA,CAAyB","file":"index.js","sourcesContent":["export type DropPreset =\n | \"null\"\n | \"undefined\"\n | \"emptyString\"\n | \"whitespaceString\"\n | \"dash\"\n | \"nan\";\n\nexport type KeyPath = Array<string | number>;\n\nexport type SanitizeOptions = {\n deep?: boolean;\n trimStrings?: boolean;\n cleanArrays?: boolean;\n drop?: DropPreset[];\n keepKeys?: string[];\n dropKeys?: string[];\n dropValues?: unknown[];\n /**\n * Always keep these paths (even if value is droppable).\n * Supports \"a.b.c\" or [\"a\",\"b\",\"c\"].\n */\n keepPaths?: Array<string | KeyPath>;\n /**\n * Always drop these paths (even if value is meaningful).\n * Supports \"a.b.c\" or [\"a\",\"b\",\"c\"].\n */\n dropPaths?: Array<string | KeyPath>;\n shouldDrop?: (value: unknown, keyPath: KeyPath) => boolean;\n /**\n * Drop objects that become empty after sanitizing.\n * Default: false\n */\n dropEmptyObjects?: boolean;\n\n /**\n * Drop arrays that become empty after sanitizing.\n * Default: false\n */\n dropEmptyArrays?: boolean;\n};\n\nconst DEFAULT_DROP: DropPreset[] = [\n \"undefined\",\n \"null\",\n \"emptyString\",\n \"whitespaceString\",\n];\n\nconst DEFAULT_OPTIONS = {\n deep: true,\n trimStrings: true,\n cleanArrays: true,\n dropEmptyObjects: false,\n dropEmptyArrays: false,\n drop: DEFAULT_DROP,\n} satisfies Required<\n Pick<\n SanitizeOptions,\n | \"deep\"\n | \"trimStrings\"\n | \"cleanArrays\"\n | \"dropEmptyObjects\"\n | \"dropEmptyArrays\"\n >\n> & { drop: DropPreset[] };\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\") return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\nfunction normalizeValue(value: unknown, opts: typeof DEFAULT_OPTIONS) {\n if (typeof value === \"string\" && opts.trimStrings) {\n return value.trim();\n }\n return value;\n}\n\nfunction shouldDropPreset(value: unknown, drop: DropPreset[]): boolean {\n for (const d of drop) {\n switch (d) {\n case \"undefined\":\n if (value === undefined) return true;\n break;\n case \"null\":\n if (value === null) return true;\n break;\n case \"emptyString\":\n if (value === \"\") return true;\n break;\n case \"whitespaceString\":\n // only matters if trimStrings is false; but still safe:\n if (typeof value === \"string\" && value.trim() === \"\") return true;\n break;\n case \"dash\":\n if (value === \"-\") return true;\n break;\n case \"nan\":\n if (typeof value === \"number\" && Number.isNaN(value)) return true;\n break;\n }\n }\n return false;\n}\n\nfunction shouldDropExact(value: unknown, exactValues?: unknown[]): boolean {\n if (!exactValues || exactValues.length === 0) return false;\n for (const v of exactValues) {\n if (Object.is(value, v)) return true;\n }\n return false;\n}\n\nfunction hasKey(list: string[] | undefined, key: string): boolean {\n return !!list && list.includes(key);\n}\n\nfunction toKeyPath(path: string | KeyPath): KeyPath {\n if (Array.isArray(path)) return path;\n if (path.trim() === \"\") return [];\n return path.split(\".\").map((seg) => {\n const n = Number(seg);\n return Number.isInteger(n) && String(n) === seg ? n : seg;\n });\n}\n\nfunction pathEquals(a: KeyPath, b: KeyPath): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\nfunction hasPath(list: Array<string | KeyPath> | undefined, path: KeyPath): boolean {\n if (!list || list.length === 0) return false;\n for (const item of list) {\n if (pathEquals(toKeyPath(item), path)) return true;\n }\n return false;\n}\n\nfunction sanitizeAny(\n input: unknown,\n options: typeof DEFAULT_OPTIONS & SanitizeOptions,\n path: KeyPath,\n): unknown {\n const normalized = normalizeValue(input, options);\n\n // Path-based overrides (highest priority)\n if (hasPath(options.dropPaths, path)) return undefined;\n\n const isKeptPath = hasPath(options.keepPaths, path);\n\n if (!isKeptPath) {\n if (shouldDropPreset(normalized, options.drop)) return undefined;\n if (shouldDropExact(normalized, options.dropValues)) return undefined;\n if (options.shouldDrop?.(normalized, path)) return undefined;\n }\n\n if (Array.isArray(normalized)) {\n if (!options.cleanArrays) return normalized.slice();\n const out: unknown[] = [];\n for (let i = 0; i < normalized.length; i++) {\n const next = sanitizeAny(normalized[i], options, path.concat(i));\n if (next !== undefined) out.push(next);\n }\n if (options.dropEmptyArrays && out.length === 0) return undefined;\n return out;\n }\n\n if (isPlainObject(normalized)) {\n if (!options.deep) {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(normalized)) {\n if (hasKey(options.dropKeys, k)) continue;\n if (hasKey(options.keepKeys, k)) {\n out[k] = v;\n continue;\n }\n const next = sanitizeAny(v, options, path.concat(k));\n if (next !== undefined) out[k] = next;\n }\n if (options.dropEmptyObjects && Object.keys(out).length === 0)\n return undefined;\n return out;\n }\n\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(normalized)) {\n if (hasKey(options.dropKeys, k)) continue;\n\n if (hasKey(options.keepKeys, k)) {\n out[k] = v;\n continue;\n }\n\n const next = sanitizeAny(v, options, path.concat(k));\n if (next !== undefined) out[k] = next;\n }\n if (options.dropEmptyObjects && Object.keys(out).length === 0)\n return undefined;\n return out;\n }\n\n return normalized;\n}\n\nexport function sanitize<T>(payload: T, options: SanitizeOptions = {}): T {\n const merged = {\n ...DEFAULT_OPTIONS,\n ...options,\n drop: options.drop ?? DEFAULT_OPTIONS.drop,\n };\n\n const result = sanitizeAny(\n payload,\n merged as typeof DEFAULT_OPTIONS & SanitizeOptions,\n [],\n );\n\n if (result === undefined) {\n if (isPlainObject(payload)) return {} as T;\n if (Array.isArray(payload)) return [] as T;\n return payload;\n }\n\n return result as T;\n}\n\ntype SanitizerFn = {\n <T>(payload: T, options?: SanitizeOptions): T;\n with: (baseOptions?: SanitizeOptions) => ReturnType<typeof createSanitizer>;\n};\n\nexport const createSanitizer = (baseOptions: SanitizeOptions = {}) => {\n return <T>(payload: T, options: SanitizeOptions = {}) =>\n sanitize(payload, { ...baseOptions, ...options });\n};\n\n(sanitize as SanitizerFn).with = (baseOptions: SanitizeOptions = {}) =>\n createSanitizer(baseOptions);\n\nexport const sanitizeWith = (sanitize as SanitizerFn).with;\n"]}
|