payload-sanitizer 0.2.0 → 0.3.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/LICENSE CHANGED
File without changes
package/README.md CHANGED
@@ -4,24 +4,17 @@
4
4
  [![npm downloads](https://img.shields.io/npm/dm/payload-sanitizer.svg)](https://www.npmjs.com/package/payload-sanitizer)
5
5
  [![license](https://img.shields.io/npm/l/payload-sanitizer.svg)](./LICENSE)
6
6
 
7
- [![CI](https://github.com/mohit838/payload-sanitizer/actions/workflows/ci.yml/badge.svg)](https://github.com/mohit838/payload-sanitizer/actions/workflows/ci.yml)
8
-
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.
7
+ Tiny zero-dependency sanitizer for JS/TS payloads that removes common junk (`""`, whitespace-only strings, `null`, `undefined`, optional `"-"`, `NaN`) without mutating the original value.
10
8
 
11
9
  ## Install
12
10
 
13
11
  ```bash
14
- # pnpm
15
12
  pnpm add payload-sanitizer
16
-
17
- # npm
18
- npm i payload-sanitizer
19
-
20
- # yarn
21
- yarn add payload-sanitizer
13
+ # or npm i payload-sanitizer
14
+ # or yarn add payload-sanitizer
22
15
  ```
23
16
 
24
- ## Quick start
17
+ ## Quick Start
25
18
 
26
19
  ```ts
27
20
  import { sanitize } from "payload-sanitizer";
@@ -32,124 +25,123 @@ const input = {
32
25
  fromDate: " 2026-02-01 ",
33
26
  toDate: null,
34
27
  page: 1,
35
- includeInactive: false,
36
28
  };
37
29
 
38
30
  const clean = sanitize(input, {
39
31
  drop: ["undefined", "null", "emptyString", "whitespaceString", "dash"],
40
32
  });
41
33
 
42
- console.log(clean);
43
- // {
44
- // fromDate: "2026-02-01",
45
- // page: 1,
46
- // includeInactive: false
47
- // }
34
+ // { fromDate: "2026-02-01", page: 1 }
48
35
  ```
49
36
 
50
- See `examples/` for frontend + backend usage.
37
+ ## API
51
38
 
52
- ## Why this instead of validation libraries?
39
+ ### `sanitize(payload, options?)`
53
40
 
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:
41
+ Returns a cleaned clone of payload.
55
42
 
56
- ```ts
57
- const cleaned = sanitize(formValues);
58
- const parsed = schema.parse(cleaned);
59
- ```
43
+ Options:
60
44
 
61
- ## API
45
+ - `deep` (default `true`): recurse into nested objects.
46
+ - `trimStrings` (default `true`): trim strings before checks.
47
+ - `cleanArrays` (default `true`): sanitize array items and remove dropped values.
48
+ - `drop` (default `["undefined", "null", "emptyString", "whitespaceString"]`): drop presets.
49
+ - `dropEmptyObjects` (default `false`): drop objects that become empty.
50
+ - `dropEmptyArrays` (default `false`): drop arrays that become empty.
51
+ - `keepKeys`, `dropKeys`: key-based keep/drop controls.
52
+ - `keepPaths`, `dropPaths`: exact path-based keep/drop controls.
53
+ - `dropValues`: explicit values to drop (uses `Object.is`).
54
+ - `shouldDrop(value, keyPath)`: custom drop predicate.
55
+ - `strict` (default `false`): validates option shapes and throws on invalid config.
56
+ - `debug` (default `false`): emit debug events.
57
+ - `logger(event)`: custom debug logger.
58
+
59
+ Drop presets:
60
+
61
+ `"undefined" | "null" | "emptyString" | "whitespaceString" | "dash" | "nan"`
62
62
 
63
- ### `sanitize(payload, options?)`
63
+ ### `sanitize.with(baseOptions)`
64
64
 
65
- Returns a cleaned clone of `payload` (objects and arrays) without mutating the input.
65
+ Create a reusable sanitizer with base options.
66
66
 
67
- **Options**
67
+ ### `createSanitizer(baseOptions)`
68
68
 
69
- - `deep` (default `true`): recurse into nested objects.
70
- - `trimStrings` (default `true`): `value.trim()` on strings before checks.
71
- - `cleanArrays` (default `true`): sanitize array items and drop ones that should be removed.
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.
75
- - `keepKeys`: key names to always keep even if value looks droppable.
76
- - `keepPaths`: exact paths to always keep (e.g., `"filters.status"`).
77
- - `dropKeys`: key names to always remove.
78
- - `dropPaths`: exact paths to always drop (e.g., `"meta.debug"`).
79
- - `dropValues`: explicit values to remove (uses `Object.is`).
80
- - `shouldDrop(value, keyPath)`: custom predicate; return `true` to drop. `keyPath` is an array of keys/indexes from root.
69
+ Factory equivalent of `sanitize.with`.
81
70
 
82
- Notes:
71
+ ### `sanitizeWith(baseOptions)`
83
72
 
84
- - `0`, `false`, and `""` inside `keepKeys` are preserved by design.
85
- - If everything is dropped, arrays become `[]`, objects become `{}`; primitives are returned as-is.
73
+ Alias of `sanitize.with`.
86
74
 
87
- Example with empty-object/array dropping:
75
+ ## Debug (Global + Instance)
88
76
 
89
77
  ```ts
90
- sanitize(payload, {
91
- drop: ["undefined", "null", "emptyString", "whitespaceString", "dash"],
92
- dropEmptyObjects: true,
93
- dropEmptyArrays: true,
78
+ import { configureDebug, sanitize } from "payload-sanitizer";
79
+
80
+ configureDebug({
81
+ debug: true,
82
+ logger: (event) => {
83
+ console.log(event.type, event.path.join("."));
84
+ },
94
85
  });
95
- ```
96
86
 
97
- Example with path-based rules:
87
+ sanitize({ q: " " });
98
88
 
99
- ```ts
100
- sanitize(payload, {
101
- keepPaths: ["filters.status"],
102
- dropPaths: ["meta.debug"],
103
- });
89
+ sanitize(
90
+ { q: " " },
91
+ {
92
+ debug: true,
93
+ logger: (event) => console.log("instance", event),
94
+ },
95
+ );
104
96
  ```
105
97
 
106
- ### `sanitize.with(baseOptions)`
98
+ Exports:
107
99
 
108
- Creates a preconfigured sanitizer.
100
+ - `configureDebug(options)`
101
+ - `getDebugOptions()`
102
+ - `resetDebug()`
109
103
 
110
- ```ts
111
- const sanitizeSearch = sanitize.with({
112
- drop: ["undefined", "null", "emptyString", "whitespaceString", "dash"],
113
- });
104
+ Debug event types:
114
105
 
115
- sanitizeSearch({ q: " hello ", status: "-" });
116
- // { q: "hello" }
117
- ```
106
+ `"normalize" | "drop" | "keep" | "empty-object" | "empty-array" | "circular-skip"`
118
107
 
119
- ### `createSanitizer(baseOptions)`
108
+ ## Circular Reference Safety
120
109
 
121
- Factory equivalent to `sanitize.with`.
110
+ Circular references are detected and skipped safely during traversal to avoid infinite recursion.
111
+
112
+ ## Utility APIs
122
113
 
123
114
  ```ts
124
- import { createSanitizer } from "payload-sanitizer";
125
- const sanitizePayload = createSanitizer({ deep: true });
115
+ import {
116
+ pick,
117
+ omit,
118
+ isEmpty,
119
+ compact,
120
+ diff,
121
+ safeParse,
122
+ } from "payload-sanitizer";
123
+ ```
124
+
125
+ - `pick(obj, keys)`
126
+ - `omit(obj, keys)`
127
+ - `isEmpty(value)`
128
+ - `compact(array)`
129
+ - `diff(before, after)`
130
+ - `safeParse(json, fallback?)`
131
+
132
+ ## CDN (IIFE Global)
133
+
134
+ ```html
135
+ <script src="https://cdn.jsdelivr.net/npm/payload-sanitizer@0.3.0/dist/index.global.js"></script>
136
+ <script>
137
+ const out = PayloadSanitizer.sanitize({ q: " hello " });
138
+ console.log(out);
139
+ </script>
126
140
  ```
127
141
 
128
- ## Common patterns
129
-
130
- - **Frontend forms** — clean before sending:
131
- ```ts
132
- await api.post("/search", sanitize(values));
133
- ```
134
- - **Backend filters** — strip empty query params:
135
- ```ts
136
- const filters = sanitize(req.query, { drop: ["dash"] });
137
- db.find(filters);
138
- ```
139
- - **Custom rule** — drop empty `filters` objects:
140
- ```ts
141
- sanitize(payload, {
142
- shouldDrop: (value, path) =>
143
- path.at(-1) === "filters" &&
144
- typeof value === "object" &&
145
- value !== null &&
146
- Object.keys(value as any).length === 0,
147
- });
148
- ```
149
- - **Drop exact values**:
150
- ```ts
151
- sanitize(data, { dropValues: ["N/A", Number.NaN] });
152
- ```
142
+ Also available via unpkg:
143
+
144
+ `https://unpkg.com/payload-sanitizer@0.3.0/dist/index.global.js`
153
145
 
154
146
  ## Development
155
147
 
@@ -157,17 +149,9 @@ const sanitizePayload = createSanitizer({ deep: true });
157
149
  pnpm install
158
150
  pnpm test
159
151
  pnpm build
152
+ pnpm typecheck
160
153
  ```
161
154
 
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
-
171
155
  ## License
172
156
 
173
157
  MIT
package/dist/index.cjs CHANGED
@@ -1,165 +1,2 @@
1
- 'use strict';
2
-
3
- // src/index.ts
4
- var DEFAULT_DROP = [
5
- "undefined",
6
- "null",
7
- "emptyString",
8
- "whitespaceString"
9
- ];
10
- var DEFAULT_OPTIONS = {
11
- deep: true,
12
- trimStrings: true,
13
- cleanArrays: true,
14
- dropEmptyObjects: false,
15
- dropEmptyArrays: false,
16
- drop: DEFAULT_DROP
17
- };
18
- function isPlainObject(value) {
19
- if (value === null || typeof value !== "object") return false;
20
- const proto = Object.getPrototypeOf(value);
21
- return proto === Object.prototype || proto === null;
22
- }
23
- function normalizeValue(value, opts) {
24
- if (typeof value === "string" && opts.trimStrings) {
25
- return value.trim();
26
- }
27
- return value;
28
- }
29
- function shouldDropPreset(value, drop) {
30
- for (const d of drop) {
31
- switch (d) {
32
- case "undefined":
33
- if (value === void 0) return true;
34
- break;
35
- case "null":
36
- if (value === null) return true;
37
- break;
38
- case "emptyString":
39
- if (value === "") return true;
40
- break;
41
- case "whitespaceString":
42
- if (typeof value === "string" && value.trim() === "") return true;
43
- break;
44
- case "dash":
45
- if (value === "-") return true;
46
- break;
47
- case "nan":
48
- if (typeof value === "number" && Number.isNaN(value)) return true;
49
- break;
50
- }
51
- }
52
- return false;
53
- }
54
- function shouldDropExact(value, exactValues) {
55
- if (!exactValues || exactValues.length === 0) return false;
56
- for (const v of exactValues) {
57
- if (Object.is(value, v)) return true;
58
- }
59
- return false;
60
- }
61
- function hasKey(list, key) {
62
- return !!list && list.includes(key);
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
- }
86
- function sanitizeAny(input, options, path) {
87
- const normalized = normalizeValue(input, options);
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
- }
95
- if (Array.isArray(normalized)) {
96
- if (!options.cleanArrays) return normalized.slice();
97
- const out = [];
98
- for (let i = 0; i < normalized.length; i++) {
99
- const next = sanitizeAny(normalized[i], options, path.concat(i));
100
- if (next !== void 0) out.push(next);
101
- }
102
- if (options.dropEmptyArrays && out.length === 0) return void 0;
103
- return out;
104
- }
105
- if (isPlainObject(normalized)) {
106
- if (!options.deep) {
107
- const out2 = {};
108
- for (const [k, v] of Object.entries(normalized)) {
109
- if (hasKey(options.dropKeys, k)) continue;
110
- if (hasKey(options.keepKeys, k)) {
111
- out2[k] = v;
112
- continue;
113
- }
114
- const next = sanitizeAny(v, options, path.concat(k));
115
- if (next !== void 0) out2[k] = next;
116
- }
117
- if (options.dropEmptyObjects && Object.keys(out2).length === 0)
118
- return void 0;
119
- return out2;
120
- }
121
- const out = {};
122
- for (const [k, v] of Object.entries(normalized)) {
123
- if (hasKey(options.dropKeys, k)) continue;
124
- if (hasKey(options.keepKeys, k)) {
125
- out[k] = v;
126
- continue;
127
- }
128
- const next = sanitizeAny(v, options, path.concat(k));
129
- if (next !== void 0) out[k] = next;
130
- }
131
- if (options.dropEmptyObjects && Object.keys(out).length === 0)
132
- return void 0;
133
- return out;
134
- }
135
- return normalized;
136
- }
137
- function sanitize(payload, options = {}) {
138
- const merged = {
139
- ...DEFAULT_OPTIONS,
140
- ...options,
141
- drop: options.drop ?? DEFAULT_OPTIONS.drop
142
- };
143
- const result = sanitizeAny(
144
- payload,
145
- merged,
146
- []
147
- );
148
- if (result === void 0) {
149
- if (isPlainObject(payload)) return {};
150
- if (Array.isArray(payload)) return [];
151
- return payload;
152
- }
153
- return result;
154
- }
155
- var createSanitizer = (baseOptions = {}) => {
156
- return (payload, options = {}) => sanitize(payload, { ...baseOptions, ...options });
157
- };
158
- sanitize.with = (baseOptions = {}) => createSanitizer(baseOptions);
159
- var sanitizeWith = sanitize.with;
160
-
161
- exports.createSanitizer = createSanitizer;
162
- exports.sanitize = sanitize;
163
- exports.sanitizeWith = sanitizeWith;
164
- //# sourceMappingURL=index.cjs.map
1
+ 'use strict';function k(){let e=new WeakSet;return {has(t){return e.has(t)},add(t){e.add(t);}}}var y={};function K(e={}){y={...y,...e};}function A(){return y}function S(){y={};}function E(e){return {debug:e.debug??y.debug??false,logger:e.logger??y.logger??z}}function z(e){globalThis.console?.log("[payload-sanitizer]",e);}function a(e,t){let r=E(e);r.debug&&r.logger(t);}function g(e){if(e===null||typeof e!="object")return false;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function c(e,t){if(!e)throw new Error(`[payload-sanitizer] ${t}`)}function j(e){return Array.isArray(e)?e:e.trim()===""?[]:e.split(".").map(t=>{let r=Number(t);return Number.isInteger(r)&&String(r)===t?r:t})}function R(e,t){if(e.length!==t.length)return false;for(let r=0;r<e.length;r++)if(e[r]!==t[r])return false;return true}function l(e,t){if(!e||e.length===0)return false;for(let r of e)if(R(j(r),t))return true;return false}var h=["undefined","null","emptyString","whitespaceString"];function D(e,t){for(let r of t)switch(r){case "undefined":if(e===void 0)return true;break;case "null":if(e===null)return true;break;case "emptyString":if(e==="")return true;break;case "whitespaceString":if(typeof e=="string"&&e.trim()==="")return true;break;case "dash":if(e==="-")return true;break;case "nan":if(typeof e=="number"&&Number.isNaN(e))return true;break}return false}var P={deep:true,trimStrings:true,cleanArrays:true,dropEmptyObjects:false,dropEmptyArrays:false,drop:h};function v(e,t){return typeof e=="string"&&t?e.trim():e}function N(e,t){if(!t||t.length===0)return false;for(let r of t)if(Object.is(e,r))return true;return false}function O(e,t){return !!e&&e.includes(t)}function C(e){c(e.drop===void 0||Array.isArray(e.drop),"`drop` must be an array"),c(e.keepKeys===void 0||Array.isArray(e.keepKeys),"`keepKeys` must be an array"),c(e.dropKeys===void 0||Array.isArray(e.dropKeys),"`dropKeys` must be an array"),c(e.dropValues===void 0||Array.isArray(e.dropValues),"`dropValues` must be an array"),c(e.keepPaths===void 0||Array.isArray(e.keepPaths),"`keepPaths` must be an array"),c(e.dropPaths===void 0||Array.isArray(e.dropPaths),"`dropPaths` must be an array"),c(e.shouldDrop===void 0||typeof e.shouldDrop=="function","`shouldDrop` must be a function");}function x(e,t={}){let r={...P,...t,drop:t.drop??P.drop};r.strict&&C(r);let s=k();function d(b,o){let n=v(b,r.trimStrings);if(a(r,{type:"normalize",path:o,original:b,result:n}),l(r.dropPaths,o)){a(r,{type:"drop",path:o,original:n});return}if(l(r.keepPaths,o))a(r,{type:"keep",path:o,original:n});else {if(D(n,r.drop)){a(r,{type:"drop",path:o,original:n});return}if(N(n,r.dropValues)){a(r,{type:"drop",path:o,original:n});return}if(r.shouldDrop?.(n,o)){a(r,{type:"drop",path:o,original:n});return}}if(Array.isArray(n)){if(s.has(n))return a(r,{type:"circular-skip",path:o,original:n}),n;if(s.add(n),!r.cleanArrays)return n.slice();let p=[];for(let u=0;u<n.length;u++){let f=d(n[u],o.concat(u));f!==void 0&&p.push(f);}if(r.dropEmptyArrays&&p.length===0){a(r,{type:"empty-array",path:o,original:n});return}return p}if(g(n)){if(s.has(n))return a(r,{type:"circular-skip",path:o,original:n}),n;s.add(n);let p={};for(let[u,f]of Object.entries(n)){if(O(r.dropKeys,u))continue;if(O(r.keepKeys,u)){a(r,{type:"keep",path:o.concat(u),original:f}),p[u]=f;continue}if(!r.deep&&g(f)){p[u]=f;continue}let m=d(f,o.concat(u));m!==void 0&&(p[u]=m);}if(r.dropEmptyObjects&&Object.keys(p).length===0){a(r,{type:"empty-object",path:o,original:n});return}return p}return n}let i=d(e,[]);return i===void 0?g(e)?{}:Array.isArray(e)?[]:e:i}var T=(e={})=>(t,r={})=>x(t,{...e,...r}),w=Object.assign(x,{with:(e={})=>T(e)}),F=w.with;function L(e,t){let r={};for(let s of t)s in e&&(r[s]=e[s]);return r}function V(e,t){let r={...e};for(let s of t)delete r[s];return r}function q(e){return Array.isArray(e)?e.length===0:e&&typeof e=="object"?Object.keys(e).length===0:false}function I(e){return e.filter(Boolean)}function U(e,t){let r={},s={},d={};for(let i of Object.keys(e)){if(!(i in t)){s[i]=e[i];continue}Object.is(e[i],t[i])||(d[i]={from:e[i],to:t[i]});}for(let i of Object.keys(t))i in e||(r[i]=t[i]);return {added:r,removed:s,changed:d}}function W(e,t){try{return JSON.parse(e)}catch{return t}}exports.compact=I;exports.configureDebug=K;exports.createSanitizer=T;exports.diff=U;exports.getDebugOptions=A;exports.isEmpty=q;exports.omit=V;exports.pick=L;exports.resetDebug=S;exports.safeParse=W;exports.sanitize=w;exports.sanitizeWith=F;//# sourceMappingURL=index.cjs.map
165
2
  //# sourceMappingURL=index.cjs.map
@@ -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 /**\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"]}
1
+ {"version":3,"sources":["../src/core/circular.ts","../src/core/debug.ts","../src/core/guards.ts","../src/core/path.ts","../src/core/presets.ts","../src/core/sanitize.ts","../src/utils/pick.ts","../src/utils/omit.ts","../src/utils/isEmpty.ts","../src/utils/compact.ts","../src/utils/diff.ts","../src/utils/safeParse.ts"],"names":["createCircularTracker","seen","value","globalDebugOptions","configureDebug","options","getDebugOptions","resetDebug","resolveDebugOptions","opts","defaultLogger","event","emitDebug","resolved","isPlainObject","proto","assert","condition","message","toKeyPath","path","seg","n","pathEquals","a","b","i","hasPath","list","item","DEFAULT_DROP","shouldDropPreset","drop","d","DEFAULT_OPTIONS","normalizeValue","trim","shouldDropExact","exactValues","v","hasKey","key","validateStrict","sanitizeImpl","payload","merged","circular","walk","input","normalized","out","next","k","result","createSanitizer","baseOptions","sanitize","sanitizeWith","pick","obj","keys","omit","isEmpty","compact","arr","diff","before","after","added","removed","changed","safeParse","json","fallback"],"mappings":"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","file":"index.cjs","sourcesContent":["export function createCircularTracker() {\n const seen = new WeakSet<object>();\n\n return {\n has(value: object) {\n return seen.has(value);\n },\n add(value: object) {\n seen.add(value);\n },\n };\n}\n","import type { KeyPath } from \"./path\";\n\nexport type DebugEventType =\n | \"normalize\"\n | \"drop\"\n | \"keep\"\n | \"empty-object\"\n | \"empty-array\"\n | \"circular-skip\";\n\nexport interface DebugEvent {\n type: DebugEventType;\n path: KeyPath;\n original: unknown;\n result?: unknown;\n}\n\nexport type DebugOptions = {\n debug?: boolean;\n logger?: (event: DebugEvent) => void;\n};\n\nlet globalDebugOptions: DebugOptions = {};\n\nexport function configureDebug(options: DebugOptions = {}): void {\n globalDebugOptions = { ...globalDebugOptions, ...options };\n}\n\nexport function getDebugOptions(): DebugOptions {\n return globalDebugOptions;\n}\n\nexport function resetDebug(): void {\n globalDebugOptions = {};\n}\n\nfunction resolveDebugOptions(opts: DebugOptions): Required<DebugOptions> {\n return {\n debug: opts.debug ?? globalDebugOptions.debug ?? false,\n logger: opts.logger ?? globalDebugOptions.logger ?? defaultLogger,\n };\n}\n\nfunction defaultLogger(event: DebugEvent): void {\n const c = (globalThis as { console?: { log: (...args: unknown[]) => void } })\n .console;\n c?.log(\"[payload-sanitizer]\", event);\n}\n\nexport function emitDebug(opts: DebugOptions, event: DebugEvent): void {\n const resolved = resolveDebugOptions(opts);\n if (!resolved.debug) return;\n resolved.logger(event);\n}\n","export function 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\nexport function assert(condition: unknown, message: string): asserts condition {\n if (!condition) {\n throw new Error(`[payload-sanitizer] ${message}`);\n }\n}\n","export type KeyPath = Array<string | number>;\n\nexport function 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\nexport function 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\nexport function hasPath(\n list: Array<string | KeyPath> | undefined,\n path: KeyPath,\n): 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","export type DropPreset =\n | \"null\"\n | \"undefined\"\n | \"emptyString\"\n | \"whitespaceString\"\n | \"dash\"\n | \"nan\";\n\nexport const DEFAULT_DROP: DropPreset[] = [\n \"undefined\",\n \"null\",\n \"emptyString\",\n \"whitespaceString\",\n];\n\nexport function 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 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","import { createCircularTracker } from \"./circular\";\nimport type { DebugOptions } from \"./debug\";\nimport { emitDebug } from \"./debug\";\nimport { assert, isPlainObject } from \"./guards\";\nimport { hasPath, type KeyPath } from \"./path\";\nimport { DEFAULT_DROP, type DropPreset, shouldDropPreset } from \"./presets\";\n\nexport type SanitizeOptions = DebugOptions & {\n deep?: boolean;\n trimStrings?: boolean;\n cleanArrays?: boolean;\n drop?: DropPreset[];\n keepKeys?: string[];\n dropKeys?: string[];\n dropValues?: unknown[];\n keepPaths?: Array<string | KeyPath>;\n dropPaths?: Array<string | KeyPath>;\n shouldDrop?: (value: unknown, keyPath: KeyPath) => boolean;\n dropEmptyObjects?: boolean;\n dropEmptyArrays?: boolean;\n strict?: boolean;\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 normalizeValue(value: unknown, trim: boolean): unknown {\n if (typeof value === \"string\" && trim) {\n return value.trim();\n }\n return value;\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 validateStrict(options: SanitizeOptions): void {\n assert(\n options.drop === undefined || Array.isArray(options.drop),\n \"`drop` must be an array\",\n );\n assert(\n options.keepKeys === undefined || Array.isArray(options.keepKeys),\n \"`keepKeys` must be an array\",\n );\n assert(\n options.dropKeys === undefined || Array.isArray(options.dropKeys),\n \"`dropKeys` must be an array\",\n );\n assert(\n options.dropValues === undefined || Array.isArray(options.dropValues),\n \"`dropValues` must be an array\",\n );\n assert(\n options.keepPaths === undefined || Array.isArray(options.keepPaths),\n \"`keepPaths` must be an array\",\n );\n assert(\n options.dropPaths === undefined || Array.isArray(options.dropPaths),\n \"`dropPaths` must be an array\",\n );\n assert(\n options.shouldDrop === undefined || typeof options.shouldDrop === \"function\",\n \"`shouldDrop` must be a function\",\n );\n}\n\nfunction sanitizeImpl<T>(payload: T, options: SanitizeOptions = {}): T {\n const merged = {\n ...DEFAULT_OPTIONS,\n ...options,\n drop: options.drop ?? DEFAULT_OPTIONS.drop,\n };\n\n if (merged.strict) {\n validateStrict(merged);\n }\n\n const circular = createCircularTracker();\n\n function walk(input: unknown, path: KeyPath): unknown {\n const normalized = normalizeValue(input, merged.trimStrings);\n\n emitDebug(merged, {\n type: \"normalize\",\n path,\n original: input,\n result: normalized,\n });\n\n if (hasPath(merged.dropPaths, path)) {\n emitDebug(merged, { type: \"drop\", path, original: normalized });\n return undefined;\n }\n\n const isKeptPath = hasPath(merged.keepPaths, path);\n\n if (isKeptPath) {\n emitDebug(merged, { type: \"keep\", path, original: normalized });\n } else {\n if (shouldDropPreset(normalized, merged.drop)) {\n emitDebug(merged, { type: \"drop\", path, original: normalized });\n return undefined;\n }\n\n if (shouldDropExact(normalized, merged.dropValues)) {\n emitDebug(merged, { type: \"drop\", path, original: normalized });\n return undefined;\n }\n\n if (merged.shouldDrop?.(normalized, path)) {\n emitDebug(merged, { type: \"drop\", path, original: normalized });\n return undefined;\n }\n }\n\n if (Array.isArray(normalized)) {\n if (circular.has(normalized)) {\n emitDebug(merged, { type: \"circular-skip\", path, original: normalized });\n return normalized;\n }\n circular.add(normalized);\n\n if (!merged.cleanArrays) return normalized.slice();\n\n const out: unknown[] = [];\n for (let i = 0; i < normalized.length; i++) {\n const next = walk(normalized[i], path.concat(i));\n if (next !== undefined) out.push(next);\n }\n\n if (merged.dropEmptyArrays && out.length === 0) {\n emitDebug(merged, { type: \"empty-array\", path, original: normalized });\n return undefined;\n }\n\n return out;\n }\n\n if (isPlainObject(normalized)) {\n if (circular.has(normalized)) {\n emitDebug(merged, { type: \"circular-skip\", path, original: normalized });\n return normalized;\n }\n circular.add(normalized);\n\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(normalized)) {\n if (hasKey(merged.dropKeys, k)) continue;\n\n if (hasKey(merged.keepKeys, k)) {\n emitDebug(merged, { type: \"keep\", path: path.concat(k), original: v });\n out[k] = v;\n continue;\n }\n\n if (!merged.deep && isPlainObject(v)) {\n out[k] = v;\n continue;\n }\n\n const next = walk(v, path.concat(k));\n if (next !== undefined) out[k] = next;\n }\n\n if (merged.dropEmptyObjects && Object.keys(out).length === 0) {\n emitDebug(merged, { type: \"empty-object\", path, original: normalized });\n return undefined;\n }\n\n return out;\n }\n\n return normalized;\n }\n\n const result = walk(payload, []);\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 sanitizeImpl(payload, { ...baseOptions, ...options });\n};\n\nexport const sanitize: SanitizerFn = Object.assign(sanitizeImpl, {\n with: (baseOptions: SanitizeOptions = {}) => createSanitizer(baseOptions),\n});\n\nexport const sanitizeWith = sanitize.with;\n","export function pick<T extends object, K extends keyof T>(\n obj: T,\n keys: K[],\n): Pick<T, K> {\n const out = {} as Pick<T, K>;\n for (const k of keys) {\n if (k in obj) out[k] = obj[k];\n }\n return out;\n}\n","export function omit<T extends object, K extends keyof T>(\n obj: T,\n keys: K[],\n): Omit<T, K> {\n const out = { ...obj };\n for (const k of keys) {\n delete out[k];\n }\n return out;\n}\n","export function isEmpty(value: unknown): boolean {\n if (Array.isArray(value)) return value.length === 0;\n if (value && typeof value === \"object\") {\n return Object.keys(value as object).length === 0;\n }\n return false;\n}\n","export function compact<T>(arr: T[]): T[] {\n return arr.filter(Boolean);\n}\n","export type ChangedEntry = {\n from: unknown;\n to: unknown;\n};\n\nexport type DiffResult = {\n added: Record<string, unknown>;\n removed: Record<string, unknown>;\n changed: Record<string, ChangedEntry>;\n};\n\nexport function diff(\n before: Record<string, unknown>,\n after: Record<string, unknown>,\n): DiffResult {\n const added: Record<string, unknown> = {};\n const removed: Record<string, unknown> = {};\n const changed: Record<string, ChangedEntry> = {};\n\n for (const key of Object.keys(before)) {\n if (!(key in after)) {\n removed[key] = before[key];\n continue;\n }\n\n if (!Object.is(before[key], after[key])) {\n changed[key] = { from: before[key], to: after[key] };\n }\n }\n\n for (const key of Object.keys(after)) {\n if (!(key in before)) {\n added[key] = after[key];\n }\n }\n\n return { added, removed, changed };\n}\n","export function safeParse<T>(json: string, fallback?: T): T | undefined {\n try {\n return JSON.parse(json) as T;\n } catch {\n return fallback;\n }\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,23 @@
1
- type DropPreset = "null" | "undefined" | "emptyString" | "whitespaceString" | "dash" | "nan";
2
1
  type KeyPath = Array<string | number>;
3
- type SanitizeOptions = {
2
+
3
+ type DebugEventType = "normalize" | "drop" | "keep" | "empty-object" | "empty-array" | "circular-skip";
4
+ interface DebugEvent {
5
+ type: DebugEventType;
6
+ path: KeyPath;
7
+ original: unknown;
8
+ result?: unknown;
9
+ }
10
+ type DebugOptions = {
11
+ debug?: boolean;
12
+ logger?: (event: DebugEvent) => void;
13
+ };
14
+ declare function configureDebug(options?: DebugOptions): void;
15
+ declare function getDebugOptions(): DebugOptions;
16
+ declare function resetDebug(): void;
17
+
18
+ type DropPreset = "null" | "undefined" | "emptyString" | "whitespaceString" | "dash" | "nan";
19
+
20
+ type SanitizeOptions = DebugOptions & {
4
21
  deep?: boolean;
5
22
  trimStrings?: boolean;
6
23
  cleanArrays?: boolean;
@@ -8,30 +25,40 @@ type SanitizeOptions = {
8
25
  keepKeys?: string[];
9
26
  dropKeys?: string[];
10
27
  dropValues?: unknown[];
11
- /**
12
- * Always keep these paths (even if value is droppable).
13
- * Supports "a.b.c" or ["a","b","c"].
14
- */
15
28
  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
29
  dropPaths?: Array<string | KeyPath>;
21
30
  shouldDrop?: (value: unknown, keyPath: KeyPath) => boolean;
22
- /**
23
- * Drop objects that become empty after sanitizing.
24
- * Default: false
25
- */
26
31
  dropEmptyObjects?: boolean;
27
- /**
28
- * Drop arrays that become empty after sanitizing.
29
- * Default: false
30
- */
31
32
  dropEmptyArrays?: boolean;
33
+ strict?: boolean;
34
+ };
35
+ type SanitizerFn = {
36
+ <T>(payload: T, options?: SanitizeOptions): T;
37
+ with: (baseOptions?: SanitizeOptions) => ReturnType<typeof createSanitizer>;
32
38
  };
33
- declare function sanitize<T>(payload: T, options?: SanitizeOptions): T;
34
39
  declare const createSanitizer: (baseOptions?: SanitizeOptions) => <T>(payload: T, options?: SanitizeOptions) => T;
40
+ declare const sanitize: SanitizerFn;
35
41
  declare const sanitizeWith: (baseOptions?: SanitizeOptions) => ReturnType<typeof createSanitizer>;
36
42
 
37
- export { type DropPreset, type KeyPath, type SanitizeOptions, createSanitizer, sanitize, sanitizeWith };
43
+ declare function pick<T extends object, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>;
44
+
45
+ declare function omit<T extends object, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>;
46
+
47
+ declare function isEmpty(value: unknown): boolean;
48
+
49
+ declare function compact<T>(arr: T[]): T[];
50
+
51
+ type ChangedEntry = {
52
+ from: unknown;
53
+ to: unknown;
54
+ };
55
+ type DiffResult = {
56
+ added: Record<string, unknown>;
57
+ removed: Record<string, unknown>;
58
+ changed: Record<string, ChangedEntry>;
59
+ };
60
+ declare function diff(before: Record<string, unknown>, after: Record<string, unknown>): DiffResult;
61
+
62
+ declare function safeParse<T>(json: string, fallback?: T): T | undefined;
63
+
64
+ export { type ChangedEntry, type DebugEvent, type DebugEventType, type DebugOptions, type DiffResult, type DropPreset, type KeyPath, type SanitizeOptions, compact, configureDebug, createSanitizer, diff, getDebugOptions, isEmpty, omit, pick, resetDebug, safeParse, sanitize, sanitizeWith };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,23 @@
1
- type DropPreset = "null" | "undefined" | "emptyString" | "whitespaceString" | "dash" | "nan";
2
1
  type KeyPath = Array<string | number>;
3
- type SanitizeOptions = {
2
+
3
+ type DebugEventType = "normalize" | "drop" | "keep" | "empty-object" | "empty-array" | "circular-skip";
4
+ interface DebugEvent {
5
+ type: DebugEventType;
6
+ path: KeyPath;
7
+ original: unknown;
8
+ result?: unknown;
9
+ }
10
+ type DebugOptions = {
11
+ debug?: boolean;
12
+ logger?: (event: DebugEvent) => void;
13
+ };
14
+ declare function configureDebug(options?: DebugOptions): void;
15
+ declare function getDebugOptions(): DebugOptions;
16
+ declare function resetDebug(): void;
17
+
18
+ type DropPreset = "null" | "undefined" | "emptyString" | "whitespaceString" | "dash" | "nan";
19
+
20
+ type SanitizeOptions = DebugOptions & {
4
21
  deep?: boolean;
5
22
  trimStrings?: boolean;
6
23
  cleanArrays?: boolean;
@@ -8,30 +25,40 @@ type SanitizeOptions = {
8
25
  keepKeys?: string[];
9
26
  dropKeys?: string[];
10
27
  dropValues?: unknown[];
11
- /**
12
- * Always keep these paths (even if value is droppable).
13
- * Supports "a.b.c" or ["a","b","c"].
14
- */
15
28
  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
29
  dropPaths?: Array<string | KeyPath>;
21
30
  shouldDrop?: (value: unknown, keyPath: KeyPath) => boolean;
22
- /**
23
- * Drop objects that become empty after sanitizing.
24
- * Default: false
25
- */
26
31
  dropEmptyObjects?: boolean;
27
- /**
28
- * Drop arrays that become empty after sanitizing.
29
- * Default: false
30
- */
31
32
  dropEmptyArrays?: boolean;
33
+ strict?: boolean;
34
+ };
35
+ type SanitizerFn = {
36
+ <T>(payload: T, options?: SanitizeOptions): T;
37
+ with: (baseOptions?: SanitizeOptions) => ReturnType<typeof createSanitizer>;
32
38
  };
33
- declare function sanitize<T>(payload: T, options?: SanitizeOptions): T;
34
39
  declare const createSanitizer: (baseOptions?: SanitizeOptions) => <T>(payload: T, options?: SanitizeOptions) => T;
40
+ declare const sanitize: SanitizerFn;
35
41
  declare const sanitizeWith: (baseOptions?: SanitizeOptions) => ReturnType<typeof createSanitizer>;
36
42
 
37
- export { type DropPreset, type KeyPath, type SanitizeOptions, createSanitizer, sanitize, sanitizeWith };
43
+ declare function pick<T extends object, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>;
44
+
45
+ declare function omit<T extends object, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>;
46
+
47
+ declare function isEmpty(value: unknown): boolean;
48
+
49
+ declare function compact<T>(arr: T[]): T[];
50
+
51
+ type ChangedEntry = {
52
+ from: unknown;
53
+ to: unknown;
54
+ };
55
+ type DiffResult = {
56
+ added: Record<string, unknown>;
57
+ removed: Record<string, unknown>;
58
+ changed: Record<string, ChangedEntry>;
59
+ };
60
+ declare function diff(before: Record<string, unknown>, after: Record<string, unknown>): DiffResult;
61
+
62
+ declare function safeParse<T>(json: string, fallback?: T): T | undefined;
63
+
64
+ export { type ChangedEntry, type DebugEvent, type DebugEventType, type DebugOptions, type DiffResult, type DropPreset, type KeyPath, type SanitizeOptions, compact, configureDebug, createSanitizer, diff, getDebugOptions, isEmpty, omit, pick, resetDebug, safeParse, sanitize, sanitizeWith };
@@ -0,0 +1,2 @@
1
+ var PayloadSanitizer=(function(exports){'use strict';function k(){let e=new WeakSet;return {has(t){return e.has(t)},add(t){e.add(t);}}}var y={};function K(e={}){y={...y,...e};}function A(){return y}function S(){y={};}function E(e){return {debug:e.debug??y.debug??false,logger:e.logger??y.logger??z}}function z(e){globalThis.console?.log("[payload-sanitizer]",e);}function a(e,t){let r=E(e);r.debug&&r.logger(t);}function g(e){if(e===null||typeof e!="object")return false;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function c(e,t){if(!e)throw new Error(`[payload-sanitizer] ${t}`)}function j(e){return Array.isArray(e)?e:e.trim()===""?[]:e.split(".").map(t=>{let r=Number(t);return Number.isInteger(r)&&String(r)===t?r:t})}function R(e,t){if(e.length!==t.length)return false;for(let r=0;r<e.length;r++)if(e[r]!==t[r])return false;return true}function l(e,t){if(!e||e.length===0)return false;for(let r of e)if(R(j(r),t))return true;return false}var h=["undefined","null","emptyString","whitespaceString"];function D(e,t){for(let r of t)switch(r){case "undefined":if(e===void 0)return true;break;case "null":if(e===null)return true;break;case "emptyString":if(e==="")return true;break;case "whitespaceString":if(typeof e=="string"&&e.trim()==="")return true;break;case "dash":if(e==="-")return true;break;case "nan":if(typeof e=="number"&&Number.isNaN(e))return true;break}return false}var P={deep:true,trimStrings:true,cleanArrays:true,dropEmptyObjects:false,dropEmptyArrays:false,drop:h};function v(e,t){return typeof e=="string"&&t?e.trim():e}function N(e,t){if(!t||t.length===0)return false;for(let r of t)if(Object.is(e,r))return true;return false}function O(e,t){return !!e&&e.includes(t)}function C(e){c(e.drop===void 0||Array.isArray(e.drop),"`drop` must be an array"),c(e.keepKeys===void 0||Array.isArray(e.keepKeys),"`keepKeys` must be an array"),c(e.dropKeys===void 0||Array.isArray(e.dropKeys),"`dropKeys` must be an array"),c(e.dropValues===void 0||Array.isArray(e.dropValues),"`dropValues` must be an array"),c(e.keepPaths===void 0||Array.isArray(e.keepPaths),"`keepPaths` must be an array"),c(e.dropPaths===void 0||Array.isArray(e.dropPaths),"`dropPaths` must be an array"),c(e.shouldDrop===void 0||typeof e.shouldDrop=="function","`shouldDrop` must be a function");}function x(e,t={}){let r={...P,...t,drop:t.drop??P.drop};r.strict&&C(r);let s=k();function d(b,o){let n=v(b,r.trimStrings);if(a(r,{type:"normalize",path:o,original:b,result:n}),l(r.dropPaths,o)){a(r,{type:"drop",path:o,original:n});return}if(l(r.keepPaths,o))a(r,{type:"keep",path:o,original:n});else {if(D(n,r.drop)){a(r,{type:"drop",path:o,original:n});return}if(N(n,r.dropValues)){a(r,{type:"drop",path:o,original:n});return}if(r.shouldDrop?.(n,o)){a(r,{type:"drop",path:o,original:n});return}}if(Array.isArray(n)){if(s.has(n))return a(r,{type:"circular-skip",path:o,original:n}),n;if(s.add(n),!r.cleanArrays)return n.slice();let p=[];for(let u=0;u<n.length;u++){let f=d(n[u],o.concat(u));f!==void 0&&p.push(f);}if(r.dropEmptyArrays&&p.length===0){a(r,{type:"empty-array",path:o,original:n});return}return p}if(g(n)){if(s.has(n))return a(r,{type:"circular-skip",path:o,original:n}),n;s.add(n);let p={};for(let[u,f]of Object.entries(n)){if(O(r.dropKeys,u))continue;if(O(r.keepKeys,u)){a(r,{type:"keep",path:o.concat(u),original:f}),p[u]=f;continue}if(!r.deep&&g(f)){p[u]=f;continue}let m=d(f,o.concat(u));m!==void 0&&(p[u]=m);}if(r.dropEmptyObjects&&Object.keys(p).length===0){a(r,{type:"empty-object",path:o,original:n});return}return p}return n}let i=d(e,[]);return i===void 0?g(e)?{}:Array.isArray(e)?[]:e:i}var T=(e={})=>(t,r={})=>x(t,{...e,...r}),w=Object.assign(x,{with:(e={})=>T(e)}),F=w.with;function L(e,t){let r={};for(let s of t)s in e&&(r[s]=e[s]);return r}function V(e,t){let r={...e};for(let s of t)delete r[s];return r}function q(e){return Array.isArray(e)?e.length===0:e&&typeof e=="object"?Object.keys(e).length===0:false}function I(e){return e.filter(Boolean)}function U(e,t){let r={},s={},d={};for(let i of Object.keys(e)){if(!(i in t)){s[i]=e[i];continue}Object.is(e[i],t[i])||(d[i]={from:e[i],to:t[i]});}for(let i of Object.keys(t))i in e||(r[i]=t[i]);return {added:r,removed:s,changed:d}}function W(e,t){try{return JSON.parse(e)}catch{return t}}exports.compact=I;exports.configureDebug=K;exports.createSanitizer=T;exports.diff=U;exports.getDebugOptions=A;exports.isEmpty=q;exports.omit=V;exports.pick=L;exports.resetDebug=S;exports.safeParse=W;exports.sanitize=w;exports.sanitizeWith=F;return exports;})({});//# sourceMappingURL=index.global.js.map
2
+ //# sourceMappingURL=index.global.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/circular.ts","../src/core/debug.ts","../src/core/guards.ts","../src/core/path.ts","../src/core/presets.ts","../src/core/sanitize.ts","../src/utils/pick.ts","../src/utils/omit.ts","../src/utils/isEmpty.ts","../src/utils/compact.ts","../src/utils/diff.ts","../src/utils/safeParse.ts"],"names":["createCircularTracker","seen","value","globalDebugOptions","configureDebug","options","getDebugOptions","resetDebug","resolveDebugOptions","opts","defaultLogger","event","emitDebug","resolved","isPlainObject","proto","assert","condition","message","toKeyPath","path","seg","n","pathEquals","a","b","i","hasPath","list","item","DEFAULT_DROP","shouldDropPreset","drop","d","DEFAULT_OPTIONS","normalizeValue","trim","shouldDropExact","exactValues","v","hasKey","key","validateStrict","sanitizeImpl","payload","merged","circular","walk","input","normalized","out","next","k","result","createSanitizer","baseOptions","sanitize","sanitizeWith","pick","obj","keys","omit","isEmpty","compact","arr","diff","before","after","added","removed","changed","safeParse","json","fallback"],"mappings":"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","file":"index.global.js","sourcesContent":["export function createCircularTracker() {\n const seen = new WeakSet<object>();\n\n return {\n has(value: object) {\n return seen.has(value);\n },\n add(value: object) {\n seen.add(value);\n },\n };\n}\n","import type { KeyPath } from \"./path\";\n\nexport type DebugEventType =\n | \"normalize\"\n | \"drop\"\n | \"keep\"\n | \"empty-object\"\n | \"empty-array\"\n | \"circular-skip\";\n\nexport interface DebugEvent {\n type: DebugEventType;\n path: KeyPath;\n original: unknown;\n result?: unknown;\n}\n\nexport type DebugOptions = {\n debug?: boolean;\n logger?: (event: DebugEvent) => void;\n};\n\nlet globalDebugOptions: DebugOptions = {};\n\nexport function configureDebug(options: DebugOptions = {}): void {\n globalDebugOptions = { ...globalDebugOptions, ...options };\n}\n\nexport function getDebugOptions(): DebugOptions {\n return globalDebugOptions;\n}\n\nexport function resetDebug(): void {\n globalDebugOptions = {};\n}\n\nfunction resolveDebugOptions(opts: DebugOptions): Required<DebugOptions> {\n return {\n debug: opts.debug ?? globalDebugOptions.debug ?? false,\n logger: opts.logger ?? globalDebugOptions.logger ?? defaultLogger,\n };\n}\n\nfunction defaultLogger(event: DebugEvent): void {\n const c = (globalThis as { console?: { log: (...args: unknown[]) => void } })\n .console;\n c?.log(\"[payload-sanitizer]\", event);\n}\n\nexport function emitDebug(opts: DebugOptions, event: DebugEvent): void {\n const resolved = resolveDebugOptions(opts);\n if (!resolved.debug) return;\n resolved.logger(event);\n}\n","export function 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\nexport function assert(condition: unknown, message: string): asserts condition {\n if (!condition) {\n throw new Error(`[payload-sanitizer] ${message}`);\n }\n}\n","export type KeyPath = Array<string | number>;\n\nexport function 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\nexport function 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\nexport function hasPath(\n list: Array<string | KeyPath> | undefined,\n path: KeyPath,\n): 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","export type DropPreset =\n | \"null\"\n | \"undefined\"\n | \"emptyString\"\n | \"whitespaceString\"\n | \"dash\"\n | \"nan\";\n\nexport const DEFAULT_DROP: DropPreset[] = [\n \"undefined\",\n \"null\",\n \"emptyString\",\n \"whitespaceString\",\n];\n\nexport function 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 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","import { createCircularTracker } from \"./circular\";\nimport type { DebugOptions } from \"./debug\";\nimport { emitDebug } from \"./debug\";\nimport { assert, isPlainObject } from \"./guards\";\nimport { hasPath, type KeyPath } from \"./path\";\nimport { DEFAULT_DROP, type DropPreset, shouldDropPreset } from \"./presets\";\n\nexport type SanitizeOptions = DebugOptions & {\n deep?: boolean;\n trimStrings?: boolean;\n cleanArrays?: boolean;\n drop?: DropPreset[];\n keepKeys?: string[];\n dropKeys?: string[];\n dropValues?: unknown[];\n keepPaths?: Array<string | KeyPath>;\n dropPaths?: Array<string | KeyPath>;\n shouldDrop?: (value: unknown, keyPath: KeyPath) => boolean;\n dropEmptyObjects?: boolean;\n dropEmptyArrays?: boolean;\n strict?: boolean;\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 normalizeValue(value: unknown, trim: boolean): unknown {\n if (typeof value === \"string\" && trim) {\n return value.trim();\n }\n return value;\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 validateStrict(options: SanitizeOptions): void {\n assert(\n options.drop === undefined || Array.isArray(options.drop),\n \"`drop` must be an array\",\n );\n assert(\n options.keepKeys === undefined || Array.isArray(options.keepKeys),\n \"`keepKeys` must be an array\",\n );\n assert(\n options.dropKeys === undefined || Array.isArray(options.dropKeys),\n \"`dropKeys` must be an array\",\n );\n assert(\n options.dropValues === undefined || Array.isArray(options.dropValues),\n \"`dropValues` must be an array\",\n );\n assert(\n options.keepPaths === undefined || Array.isArray(options.keepPaths),\n \"`keepPaths` must be an array\",\n );\n assert(\n options.dropPaths === undefined || Array.isArray(options.dropPaths),\n \"`dropPaths` must be an array\",\n );\n assert(\n options.shouldDrop === undefined || typeof options.shouldDrop === \"function\",\n \"`shouldDrop` must be a function\",\n );\n}\n\nfunction sanitizeImpl<T>(payload: T, options: SanitizeOptions = {}): T {\n const merged = {\n ...DEFAULT_OPTIONS,\n ...options,\n drop: options.drop ?? DEFAULT_OPTIONS.drop,\n };\n\n if (merged.strict) {\n validateStrict(merged);\n }\n\n const circular = createCircularTracker();\n\n function walk(input: unknown, path: KeyPath): unknown {\n const normalized = normalizeValue(input, merged.trimStrings);\n\n emitDebug(merged, {\n type: \"normalize\",\n path,\n original: input,\n result: normalized,\n });\n\n if (hasPath(merged.dropPaths, path)) {\n emitDebug(merged, { type: \"drop\", path, original: normalized });\n return undefined;\n }\n\n const isKeptPath = hasPath(merged.keepPaths, path);\n\n if (isKeptPath) {\n emitDebug(merged, { type: \"keep\", path, original: normalized });\n } else {\n if (shouldDropPreset(normalized, merged.drop)) {\n emitDebug(merged, { type: \"drop\", path, original: normalized });\n return undefined;\n }\n\n if (shouldDropExact(normalized, merged.dropValues)) {\n emitDebug(merged, { type: \"drop\", path, original: normalized });\n return undefined;\n }\n\n if (merged.shouldDrop?.(normalized, path)) {\n emitDebug(merged, { type: \"drop\", path, original: normalized });\n return undefined;\n }\n }\n\n if (Array.isArray(normalized)) {\n if (circular.has(normalized)) {\n emitDebug(merged, { type: \"circular-skip\", path, original: normalized });\n return normalized;\n }\n circular.add(normalized);\n\n if (!merged.cleanArrays) return normalized.slice();\n\n const out: unknown[] = [];\n for (let i = 0; i < normalized.length; i++) {\n const next = walk(normalized[i], path.concat(i));\n if (next !== undefined) out.push(next);\n }\n\n if (merged.dropEmptyArrays && out.length === 0) {\n emitDebug(merged, { type: \"empty-array\", path, original: normalized });\n return undefined;\n }\n\n return out;\n }\n\n if (isPlainObject(normalized)) {\n if (circular.has(normalized)) {\n emitDebug(merged, { type: \"circular-skip\", path, original: normalized });\n return normalized;\n }\n circular.add(normalized);\n\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(normalized)) {\n if (hasKey(merged.dropKeys, k)) continue;\n\n if (hasKey(merged.keepKeys, k)) {\n emitDebug(merged, { type: \"keep\", path: path.concat(k), original: v });\n out[k] = v;\n continue;\n }\n\n if (!merged.deep && isPlainObject(v)) {\n out[k] = v;\n continue;\n }\n\n const next = walk(v, path.concat(k));\n if (next !== undefined) out[k] = next;\n }\n\n if (merged.dropEmptyObjects && Object.keys(out).length === 0) {\n emitDebug(merged, { type: \"empty-object\", path, original: normalized });\n return undefined;\n }\n\n return out;\n }\n\n return normalized;\n }\n\n const result = walk(payload, []);\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 sanitizeImpl(payload, { ...baseOptions, ...options });\n};\n\nexport const sanitize: SanitizerFn = Object.assign(sanitizeImpl, {\n with: (baseOptions: SanitizeOptions = {}) => createSanitizer(baseOptions),\n});\n\nexport const sanitizeWith = sanitize.with;\n","export function pick<T extends object, K extends keyof T>(\n obj: T,\n keys: K[],\n): Pick<T, K> {\n const out = {} as Pick<T, K>;\n for (const k of keys) {\n if (k in obj) out[k] = obj[k];\n }\n return out;\n}\n","export function omit<T extends object, K extends keyof T>(\n obj: T,\n keys: K[],\n): Omit<T, K> {\n const out = { ...obj };\n for (const k of keys) {\n delete out[k];\n }\n return out;\n}\n","export function isEmpty(value: unknown): boolean {\n if (Array.isArray(value)) return value.length === 0;\n if (value && typeof value === \"object\") {\n return Object.keys(value as object).length === 0;\n }\n return false;\n}\n","export function compact<T>(arr: T[]): T[] {\n return arr.filter(Boolean);\n}\n","export type ChangedEntry = {\n from: unknown;\n to: unknown;\n};\n\nexport type DiffResult = {\n added: Record<string, unknown>;\n removed: Record<string, unknown>;\n changed: Record<string, ChangedEntry>;\n};\n\nexport function diff(\n before: Record<string, unknown>,\n after: Record<string, unknown>,\n): DiffResult {\n const added: Record<string, unknown> = {};\n const removed: Record<string, unknown> = {};\n const changed: Record<string, ChangedEntry> = {};\n\n for (const key of Object.keys(before)) {\n if (!(key in after)) {\n removed[key] = before[key];\n continue;\n }\n\n if (!Object.is(before[key], after[key])) {\n changed[key] = { from: before[key], to: after[key] };\n }\n }\n\n for (const key of Object.keys(after)) {\n if (!(key in before)) {\n added[key] = after[key];\n }\n }\n\n return { added, removed, changed };\n}\n","export function safeParse<T>(json: string, fallback?: T): T | undefined {\n try {\n return JSON.parse(json) as T;\n } catch {\n return fallback;\n }\n}\n"]}
package/dist/index.js CHANGED
@@ -1,161 +1,2 @@
1
- // src/index.ts
2
- var DEFAULT_DROP = [
3
- "undefined",
4
- "null",
5
- "emptyString",
6
- "whitespaceString"
7
- ];
8
- var DEFAULT_OPTIONS = {
9
- deep: true,
10
- trimStrings: true,
11
- cleanArrays: true,
12
- dropEmptyObjects: false,
13
- dropEmptyArrays: false,
14
- drop: DEFAULT_DROP
15
- };
16
- function isPlainObject(value) {
17
- if (value === null || typeof value !== "object") return false;
18
- const proto = Object.getPrototypeOf(value);
19
- return proto === Object.prototype || proto === null;
20
- }
21
- function normalizeValue(value, opts) {
22
- if (typeof value === "string" && opts.trimStrings) {
23
- return value.trim();
24
- }
25
- return value;
26
- }
27
- function shouldDropPreset(value, drop) {
28
- for (const d of drop) {
29
- switch (d) {
30
- case "undefined":
31
- if (value === void 0) return true;
32
- break;
33
- case "null":
34
- if (value === null) return true;
35
- break;
36
- case "emptyString":
37
- if (value === "") return true;
38
- break;
39
- case "whitespaceString":
40
- if (typeof value === "string" && value.trim() === "") return true;
41
- break;
42
- case "dash":
43
- if (value === "-") return true;
44
- break;
45
- case "nan":
46
- if (typeof value === "number" && Number.isNaN(value)) return true;
47
- break;
48
- }
49
- }
50
- return false;
51
- }
52
- function shouldDropExact(value, exactValues) {
53
- if (!exactValues || exactValues.length === 0) return false;
54
- for (const v of exactValues) {
55
- if (Object.is(value, v)) return true;
56
- }
57
- return false;
58
- }
59
- function hasKey(list, key) {
60
- return !!list && list.includes(key);
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
- }
84
- function sanitizeAny(input, options, path) {
85
- const normalized = normalizeValue(input, options);
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
- }
93
- if (Array.isArray(normalized)) {
94
- if (!options.cleanArrays) return normalized.slice();
95
- const out = [];
96
- for (let i = 0; i < normalized.length; i++) {
97
- const next = sanitizeAny(normalized[i], options, path.concat(i));
98
- if (next !== void 0) out.push(next);
99
- }
100
- if (options.dropEmptyArrays && out.length === 0) return void 0;
101
- return out;
102
- }
103
- if (isPlainObject(normalized)) {
104
- if (!options.deep) {
105
- const out2 = {};
106
- for (const [k, v] of Object.entries(normalized)) {
107
- if (hasKey(options.dropKeys, k)) continue;
108
- if (hasKey(options.keepKeys, k)) {
109
- out2[k] = v;
110
- continue;
111
- }
112
- const next = sanitizeAny(v, options, path.concat(k));
113
- if (next !== void 0) out2[k] = next;
114
- }
115
- if (options.dropEmptyObjects && Object.keys(out2).length === 0)
116
- return void 0;
117
- return out2;
118
- }
119
- const out = {};
120
- for (const [k, v] of Object.entries(normalized)) {
121
- if (hasKey(options.dropKeys, k)) continue;
122
- if (hasKey(options.keepKeys, k)) {
123
- out[k] = v;
124
- continue;
125
- }
126
- const next = sanitizeAny(v, options, path.concat(k));
127
- if (next !== void 0) out[k] = next;
128
- }
129
- if (options.dropEmptyObjects && Object.keys(out).length === 0)
130
- return void 0;
131
- return out;
132
- }
133
- return normalized;
134
- }
135
- function sanitize(payload, options = {}) {
136
- const merged = {
137
- ...DEFAULT_OPTIONS,
138
- ...options,
139
- drop: options.drop ?? DEFAULT_OPTIONS.drop
140
- };
141
- const result = sanitizeAny(
142
- payload,
143
- merged,
144
- []
145
- );
146
- if (result === void 0) {
147
- if (isPlainObject(payload)) return {};
148
- if (Array.isArray(payload)) return [];
149
- return payload;
150
- }
151
- return result;
152
- }
153
- var createSanitizer = (baseOptions = {}) => {
154
- return (payload, options = {}) => sanitize(payload, { ...baseOptions, ...options });
155
- };
156
- sanitize.with = (baseOptions = {}) => createSanitizer(baseOptions);
157
- var sanitizeWith = sanitize.with;
158
-
159
- export { createSanitizer, sanitize, sanitizeWith };
160
- //# sourceMappingURL=index.js.map
1
+ function k(){let e=new WeakSet;return {has(t){return e.has(t)},add(t){e.add(t);}}}var y={};function K(e={}){y={...y,...e};}function A(){return y}function S(){y={};}function E(e){return {debug:e.debug??y.debug??false,logger:e.logger??y.logger??z}}function z(e){globalThis.console?.log("[payload-sanitizer]",e);}function a(e,t){let r=E(e);r.debug&&r.logger(t);}function g(e){if(e===null||typeof e!="object")return false;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function c(e,t){if(!e)throw new Error(`[payload-sanitizer] ${t}`)}function j(e){return Array.isArray(e)?e:e.trim()===""?[]:e.split(".").map(t=>{let r=Number(t);return Number.isInteger(r)&&String(r)===t?r:t})}function R(e,t){if(e.length!==t.length)return false;for(let r=0;r<e.length;r++)if(e[r]!==t[r])return false;return true}function l(e,t){if(!e||e.length===0)return false;for(let r of e)if(R(j(r),t))return true;return false}var h=["undefined","null","emptyString","whitespaceString"];function D(e,t){for(let r of t)switch(r){case "undefined":if(e===void 0)return true;break;case "null":if(e===null)return true;break;case "emptyString":if(e==="")return true;break;case "whitespaceString":if(typeof e=="string"&&e.trim()==="")return true;break;case "dash":if(e==="-")return true;break;case "nan":if(typeof e=="number"&&Number.isNaN(e))return true;break}return false}var P={deep:true,trimStrings:true,cleanArrays:true,dropEmptyObjects:false,dropEmptyArrays:false,drop:h};function v(e,t){return typeof e=="string"&&t?e.trim():e}function N(e,t){if(!t||t.length===0)return false;for(let r of t)if(Object.is(e,r))return true;return false}function O(e,t){return !!e&&e.includes(t)}function C(e){c(e.drop===void 0||Array.isArray(e.drop),"`drop` must be an array"),c(e.keepKeys===void 0||Array.isArray(e.keepKeys),"`keepKeys` must be an array"),c(e.dropKeys===void 0||Array.isArray(e.dropKeys),"`dropKeys` must be an array"),c(e.dropValues===void 0||Array.isArray(e.dropValues),"`dropValues` must be an array"),c(e.keepPaths===void 0||Array.isArray(e.keepPaths),"`keepPaths` must be an array"),c(e.dropPaths===void 0||Array.isArray(e.dropPaths),"`dropPaths` must be an array"),c(e.shouldDrop===void 0||typeof e.shouldDrop=="function","`shouldDrop` must be a function");}function x(e,t={}){let r={...P,...t,drop:t.drop??P.drop};r.strict&&C(r);let s=k();function d(b,o){let n=v(b,r.trimStrings);if(a(r,{type:"normalize",path:o,original:b,result:n}),l(r.dropPaths,o)){a(r,{type:"drop",path:o,original:n});return}if(l(r.keepPaths,o))a(r,{type:"keep",path:o,original:n});else {if(D(n,r.drop)){a(r,{type:"drop",path:o,original:n});return}if(N(n,r.dropValues)){a(r,{type:"drop",path:o,original:n});return}if(r.shouldDrop?.(n,o)){a(r,{type:"drop",path:o,original:n});return}}if(Array.isArray(n)){if(s.has(n))return a(r,{type:"circular-skip",path:o,original:n}),n;if(s.add(n),!r.cleanArrays)return n.slice();let p=[];for(let u=0;u<n.length;u++){let f=d(n[u],o.concat(u));f!==void 0&&p.push(f);}if(r.dropEmptyArrays&&p.length===0){a(r,{type:"empty-array",path:o,original:n});return}return p}if(g(n)){if(s.has(n))return a(r,{type:"circular-skip",path:o,original:n}),n;s.add(n);let p={};for(let[u,f]of Object.entries(n)){if(O(r.dropKeys,u))continue;if(O(r.keepKeys,u)){a(r,{type:"keep",path:o.concat(u),original:f}),p[u]=f;continue}if(!r.deep&&g(f)){p[u]=f;continue}let m=d(f,o.concat(u));m!==void 0&&(p[u]=m);}if(r.dropEmptyObjects&&Object.keys(p).length===0){a(r,{type:"empty-object",path:o,original:n});return}return p}return n}let i=d(e,[]);return i===void 0?g(e)?{}:Array.isArray(e)?[]:e:i}var T=(e={})=>(t,r={})=>x(t,{...e,...r}),w=Object.assign(x,{with:(e={})=>T(e)}),F=w.with;function L(e,t){let r={};for(let s of t)s in e&&(r[s]=e[s]);return r}function V(e,t){let r={...e};for(let s of t)delete r[s];return r}function q(e){return Array.isArray(e)?e.length===0:e&&typeof e=="object"?Object.keys(e).length===0:false}function I(e){return e.filter(Boolean)}function U(e,t){let r={},s={},d={};for(let i of Object.keys(e)){if(!(i in t)){s[i]=e[i];continue}Object.is(e[i],t[i])||(d[i]={from:e[i],to:t[i]});}for(let i of Object.keys(t))i in e||(r[i]=t[i]);return {added:r,removed:s,changed:d}}function W(e,t){try{return JSON.parse(e)}catch{return t}}export{I as compact,K as configureDebug,T as createSanitizer,U as diff,A as getDebugOptions,q as isEmpty,V as omit,L as pick,S as resetDebug,W as safeParse,w as sanitize,F as sanitizeWith};//# sourceMappingURL=index.js.map
161
2
  //# sourceMappingURL=index.js.map
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 /**\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"]}
1
+ {"version":3,"sources":["../src/core/circular.ts","../src/core/debug.ts","../src/core/guards.ts","../src/core/path.ts","../src/core/presets.ts","../src/core/sanitize.ts","../src/utils/pick.ts","../src/utils/omit.ts","../src/utils/isEmpty.ts","../src/utils/compact.ts","../src/utils/diff.ts","../src/utils/safeParse.ts"],"names":["createCircularTracker","seen","value","globalDebugOptions","configureDebug","options","getDebugOptions","resetDebug","resolveDebugOptions","opts","defaultLogger","event","emitDebug","resolved","isPlainObject","proto","assert","condition","message","toKeyPath","path","seg","n","pathEquals","a","b","i","hasPath","list","item","DEFAULT_DROP","shouldDropPreset","drop","d","DEFAULT_OPTIONS","normalizeValue","trim","shouldDropExact","exactValues","v","hasKey","key","validateStrict","sanitizeImpl","payload","merged","circular","walk","input","normalized","out","next","k","result","createSanitizer","baseOptions","sanitize","sanitizeWith","pick","obj","keys","omit","isEmpty","compact","arr","diff","before","after","added","removed","changed","safeParse","json","fallback"],"mappings":"AAAO,SAASA,CAAAA,EAAwB,CACtC,IAAMC,CAAAA,CAAO,IAAI,OAAA,CAEjB,OAAO,CACL,GAAA,CAAIC,CAAAA,CAAe,CACjB,OAAOD,EAAK,GAAA,CAAIC,CAAK,CACvB,CAAA,CACA,GAAA,CAAIA,CAAAA,CAAe,CACjBD,CAAAA,CAAK,GAAA,CAAIC,CAAK,EAChB,CACF,CACF,CCWA,IAAIC,CAAAA,CAAmC,EAAC,CAEjC,SAASC,CAAAA,CAAeC,CAAAA,CAAwB,EAAC,CAAS,CAC/DF,CAAAA,CAAqB,CAAE,GAAGA,CAAAA,CAAoB,GAAGE,CAAQ,EAC3D,CAEO,SAASC,GAAgC,CAC9C,OAAOH,CACT,CAEO,SAASI,CAAAA,EAAmB,CACjCJ,CAAAA,CAAqB,GACvB,CAEA,SAASK,CAAAA,CAAoBC,CAAAA,CAA4C,CACvE,OAAO,CACL,KAAA,CAAOA,CAAAA,CAAK,KAAA,EAASN,CAAAA,CAAmB,KAAA,EAAS,KAAA,CACjD,MAAA,CAAQM,CAAAA,CAAK,MAAA,EAAUN,CAAAA,CAAmB,MAAA,EAAUO,CACtD,CACF,CAEA,SAASA,CAAAA,CAAcC,EAAyB,CACnC,UAAA,CACR,OAAA,EACA,GAAA,CAAI,qBAAA,CAAuBA,CAAK,EACrC,CAEO,SAASC,CAAAA,CAAUH,CAAAA,CAAoBE,CAAAA,CAAyB,CACrE,IAAME,CAAAA,CAAWL,CAAAA,CAAoBC,CAAI,CAAA,CACpCI,CAAAA,CAAS,KAAA,EACdA,CAAAA,CAAS,MAAA,CAAOF,CAAK,EACvB,CCrDO,SAASG,CAAAA,CAAcZ,CAAAA,CAAkD,CAC9E,GAAIA,CAAAA,GAAU,IAAA,EAAQ,OAAOA,GAAU,QAAA,CAAU,OAAO,MAAA,CACxD,IAAMa,CAAAA,CAAQ,MAAA,CAAO,cAAA,CAAeb,CAAK,EACzC,OAAOa,CAAAA,GAAU,MAAA,CAAO,SAAA,EAAaA,CAAAA,GAAU,IACjD,CAEO,SAASC,EAAOC,CAAAA,CAAoBC,CAAAA,CAAoC,CAC7E,GAAI,CAACD,CAAAA,CACH,MAAM,IAAI,KAAA,CAAM,CAAA,oBAAA,EAAuBC,CAAO,CAAA,CAAE,CAEpD,CCRO,SAASC,CAAAA,CAAUC,EAAiC,CACzD,OAAI,KAAA,CAAM,OAAA,CAAQA,CAAI,CAAA,CAAUA,CAAAA,CAC5BA,CAAAA,CAAK,IAAA,EAAK,GAAM,EAAA,CAAW,EAAC,CACzBA,CAAAA,CAAK,KAAA,CAAM,GAAG,EAAE,GAAA,CAAKC,CAAAA,EAAQ,CAClC,IAAMC,CAAAA,CAAI,MAAA,CAAOD,CAAG,CAAA,CACpB,OAAO,MAAA,CAAO,SAAA,CAAUC,CAAC,CAAA,EAAK,MAAA,CAAOA,CAAC,CAAA,GAAMD,EAAMC,CAAAA,CAAID,CACxD,CAAC,CACH,CAEO,SAASE,CAAAA,CAAWC,CAAAA,CAAYC,EAAqB,CAC1D,GAAID,CAAAA,CAAE,MAAA,GAAWC,CAAAA,CAAE,MAAA,CAAQ,OAAO,MAAA,CAClC,QAASC,CAAAA,CAAI,CAAA,CAAGA,CAAAA,CAAIF,CAAAA,CAAE,MAAA,CAAQE,CAAAA,EAAAA,CAC5B,GAAIF,CAAAA,CAAEE,CAAC,CAAA,GAAMD,CAAAA,CAAEC,CAAC,CAAA,CAAG,OAAO,MAAA,CAE5B,OAAO,KACT,CAEO,SAASC,CAAAA,CACdC,CAAAA,CACAR,CAAAA,CACS,CACT,GAAI,CAACQ,CAAAA,EAAQA,CAAAA,CAAK,MAAA,GAAW,CAAA,CAAG,OAAO,MAAA,CACvC,IAAA,IAAWC,CAAAA,IAAQD,EACjB,GAAIL,CAAAA,CAAWJ,CAAAA,CAAUU,CAAI,CAAA,CAAGT,CAAI,CAAA,CAAG,OAAO,KAAA,CAEhD,OAAO,MACT,CCpBO,IAAMU,CAAAA,CAA6B,CACxC,WAAA,CACA,OACA,aAAA,CACA,kBACF,CAAA,CAEO,SAASC,CAAAA,CAAiB7B,CAAAA,CAAgB8B,CAAAA,CAA6B,CAC5E,QAAWC,CAAAA,IAAKD,CAAAA,CACd,OAAQC,CAAAA,EACN,KAAK,WAAA,CACH,GAAI/B,IAAU,MAAA,CAAW,OAAO,KAAA,CAChC,MACF,KAAK,MAAA,CACH,GAAIA,CAAAA,GAAU,IAAA,CAAM,OAAO,KAAA,CAC3B,MACF,KAAK,aAAA,CACH,GAAIA,CAAAA,GAAU,GAAI,OAAO,KAAA,CACzB,MACF,KAAK,kBAAA,CACH,GAAI,OAAOA,CAAAA,EAAU,QAAA,EAAYA,CAAAA,CAAM,IAAA,EAAK,GAAM,EAAA,CAAI,OAAO,KAAA,CAC7D,MACF,KAAK,MAAA,CACH,GAAIA,CAAAA,GAAU,GAAA,CAAK,OAAO,KAAA,CAC1B,MACF,KAAK,KAAA,CACH,GAAI,OAAOA,CAAAA,EAAU,QAAA,EAAY,MAAA,CAAO,KAAA,CAAMA,CAAK,EAAG,OAAO,KAAA,CAC7D,KACJ,CAEF,OAAO,MACT,CChBA,IAAMgC,CAAAA,CAAkB,CACtB,IAAA,CAAM,IAAA,CACN,WAAA,CAAa,IAAA,CACb,WAAA,CAAa,IAAA,CACb,iBAAkB,KAAA,CAClB,eAAA,CAAiB,KAAA,CACjB,IAAA,CAAMJ,CACR,CAAA,CAWA,SAASK,CAAAA,CAAejC,CAAAA,CAAgBkC,CAAAA,CAAwB,CAC9D,OAAI,OAAOlC,CAAAA,EAAU,QAAA,EAAYkC,CAAAA,CACxBlC,EAAM,IAAA,EAAK,CAEbA,CACT,CAEA,SAASmC,CAAAA,CAAgBnC,CAAAA,CAAgBoC,CAAAA,CAAkC,CACzE,GAAI,CAACA,CAAAA,EAAeA,CAAAA,CAAY,MAAA,GAAW,CAAA,CAAG,OAAO,OACrD,IAAA,IAAWC,CAAAA,IAAKD,CAAAA,CACd,GAAI,MAAA,CAAO,EAAA,CAAGpC,CAAAA,CAAOqC,CAAC,CAAA,CAAG,OAAO,KAAA,CAElC,OAAO,MACT,CAEA,SAASC,CAAAA,CAAOZ,EAA4Ba,CAAAA,CAAsB,CAChE,OAAO,CAAC,CAACb,CAAAA,EAAQA,CAAAA,CAAK,QAAA,CAASa,CAAG,CACpC,CAEA,SAASC,CAAAA,CAAerC,CAAAA,CAAgC,CACtDW,CAAAA,CACEX,CAAAA,CAAQ,OAAS,MAAA,EAAa,KAAA,CAAM,OAAA,CAAQA,CAAAA,CAAQ,IAAI,CAAA,CACxD,yBACF,CAAA,CACAW,CAAAA,CACEX,CAAAA,CAAQ,QAAA,GAAa,MAAA,EAAa,KAAA,CAAM,OAAA,CAAQA,CAAAA,CAAQ,QAAQ,EAChE,6BACF,CAAA,CACAW,CAAAA,CACEX,CAAAA,CAAQ,QAAA,GAAa,MAAA,EAAa,KAAA,CAAM,OAAA,CAAQA,CAAAA,CAAQ,QAAQ,CAAA,CAChE,6BACF,CAAA,CACAW,CAAAA,CACEX,CAAAA,CAAQ,UAAA,GAAe,QAAa,KAAA,CAAM,OAAA,CAAQA,CAAAA,CAAQ,UAAU,CAAA,CACpE,+BACF,CAAA,CACAW,CAAAA,CACEX,CAAAA,CAAQ,SAAA,GAAc,MAAA,EAAa,KAAA,CAAM,OAAA,CAAQA,CAAAA,CAAQ,SAAS,CAAA,CAClE,8BACF,CAAA,CACAW,CAAAA,CACEX,CAAAA,CAAQ,SAAA,GAAc,MAAA,EAAa,KAAA,CAAM,OAAA,CAAQA,CAAAA,CAAQ,SAAS,CAAA,CAClE,8BACF,CAAA,CACAW,CAAAA,CACEX,CAAAA,CAAQ,UAAA,GAAe,MAAA,EAAa,OAAOA,EAAQ,UAAA,EAAe,UAAA,CAClE,iCACF,EACF,CAEA,SAASsC,CAAAA,CAAgBC,CAAAA,CAAYvC,CAAAA,CAA2B,EAAC,CAAM,CACrE,IAAMwC,CAAAA,CAAS,CACb,GAAGX,EACH,GAAG7B,CAAAA,CACH,IAAA,CAAMA,CAAAA,CAAQ,IAAA,EAAQ6B,CAAAA,CAAgB,IACxC,CAAA,CAEIW,CAAAA,CAAO,MAAA,EACTH,CAAAA,CAAeG,CAAM,CAAA,CAGvB,IAAMC,CAAAA,CAAW9C,CAAAA,GAEjB,SAAS+C,CAAAA,CAAKC,CAAAA,CAAgB5B,CAAAA,CAAwB,CACpD,IAAM6B,CAAAA,CAAad,CAAAA,CAAea,CAAAA,CAAOH,CAAAA,CAAO,WAAW,CAAA,CAS3D,GAPAjC,CAAAA,CAAUiC,CAAAA,CAAQ,CAChB,KAAM,WAAA,CACN,IAAA,CAAAzB,CAAAA,CACA,QAAA,CAAU4B,CAAAA,CACV,MAAA,CAAQC,CACV,CAAC,EAEGtB,CAAAA,CAAQkB,CAAAA,CAAO,SAAA,CAAWzB,CAAI,CAAA,CAAG,CACnCR,CAAAA,CAAUiC,CAAAA,CAAQ,CAAE,IAAA,CAAM,MAAA,CAAQ,IAAA,CAAAzB,CAAAA,CAAM,QAAA,CAAU6B,CAAW,CAAC,CAAA,CAC9D,MACF,CAIA,GAFmBtB,CAAAA,CAAQkB,CAAAA,CAAO,SAAA,CAAWzB,CAAI,CAAA,CAG/CR,EAAUiC,CAAAA,CAAQ,CAAE,IAAA,CAAM,MAAA,CAAQ,IAAA,CAAAzB,CAAAA,CAAM,QAAA,CAAU6B,CAAW,CAAC,CAAA,CAAA,KACzD,CACL,GAAIlB,CAAAA,CAAiBkB,CAAAA,CAAYJ,CAAAA,CAAO,IAAI,EAAG,CAC7CjC,CAAAA,CAAUiC,CAAAA,CAAQ,CAAE,IAAA,CAAM,MAAA,CAAQ,IAAA,CAAAzB,CAAAA,CAAM,QAAA,CAAU6B,CAAW,CAAC,CAAA,CAC9D,MACF,CAEA,GAAIZ,CAAAA,CAAgBY,EAAYJ,CAAAA,CAAO,UAAU,CAAA,CAAG,CAClDjC,CAAAA,CAAUiC,CAAAA,CAAQ,CAAE,IAAA,CAAM,MAAA,CAAQ,IAAA,CAAAzB,CAAAA,CAAM,QAAA,CAAU6B,CAAW,CAAC,CAAA,CAC9D,MACF,CAEA,GAAIJ,CAAAA,CAAO,UAAA,GAAaI,CAAAA,CAAY7B,CAAI,CAAA,CAAG,CACzCR,CAAAA,CAAUiC,CAAAA,CAAQ,CAAE,IAAA,CAAM,MAAA,CAAQ,IAAA,CAAAzB,CAAAA,CAAM,QAAA,CAAU6B,CAAW,CAAC,CAAA,CAC9D,MACF,CACF,CAEA,GAAI,KAAA,CAAM,OAAA,CAAQA,CAAU,CAAA,CAAG,CAC7B,GAAIH,CAAAA,CAAS,GAAA,CAAIG,CAAU,CAAA,CACzB,OAAArC,EAAUiC,CAAAA,CAAQ,CAAE,IAAA,CAAM,eAAA,CAAiB,IAAA,CAAAzB,CAAAA,CAAM,QAAA,CAAU6B,CAAW,CAAC,CAAA,CAChEA,CAAAA,CAIT,GAFAH,CAAAA,CAAS,GAAA,CAAIG,CAAU,CAAA,CAEnB,CAACJ,CAAAA,CAAO,WAAA,CAAa,OAAOI,CAAAA,CAAW,KAAA,EAAM,CAEjD,IAAMC,CAAAA,CAAiB,EAAC,CACxB,IAAA,IAASxB,CAAAA,CAAI,CAAA,CAAGA,CAAAA,CAAIuB,CAAAA,CAAW,MAAA,CAAQvB,CAAAA,EAAAA,CAAK,CAC1C,IAAMyB,CAAAA,CAAOJ,CAAAA,CAAKE,CAAAA,CAAWvB,CAAC,CAAA,CAAGN,CAAAA,CAAK,MAAA,CAAOM,CAAC,CAAC,CAAA,CAC3CyB,CAAAA,GAAS,MAAA,EAAWD,CAAAA,CAAI,IAAA,CAAKC,CAAI,EACvC,CAEA,GAAIN,CAAAA,CAAO,eAAA,EAAmBK,CAAAA,CAAI,MAAA,GAAW,CAAA,CAAG,CAC9CtC,CAAAA,CAAUiC,CAAAA,CAAQ,CAAE,IAAA,CAAM,aAAA,CAAe,IAAA,CAAAzB,CAAAA,CAAM,QAAA,CAAU6B,CAAW,CAAC,CAAA,CACrE,MACF,CAEA,OAAOC,CACT,CAEA,GAAIpC,CAAAA,CAAcmC,CAAU,CAAA,CAAG,CAC7B,GAAIH,CAAAA,CAAS,GAAA,CAAIG,CAAU,EACzB,OAAArC,CAAAA,CAAUiC,CAAAA,CAAQ,CAAE,IAAA,CAAM,eAAA,CAAiB,IAAA,CAAAzB,CAAAA,CAAM,SAAU6B,CAAW,CAAC,CAAA,CAChEA,CAAAA,CAETH,CAAAA,CAAS,GAAA,CAAIG,CAAU,CAAA,CAEvB,IAAMC,CAAAA,CAA+B,EAAC,CACtC,IAAA,GAAW,CAACE,CAAAA,CAAGb,CAAC,CAAA,GAAK,MAAA,CAAO,OAAA,CAAQU,CAAU,CAAA,CAAG,CAC/C,GAAIT,CAAAA,CAAOK,CAAAA,CAAO,SAAUO,CAAC,CAAA,CAAG,SAEhC,GAAIZ,CAAAA,CAAOK,CAAAA,CAAO,QAAA,CAAUO,CAAC,CAAA,CAAG,CAC9BxC,CAAAA,CAAUiC,CAAAA,CAAQ,CAAE,IAAA,CAAM,MAAA,CAAQ,IAAA,CAAMzB,EAAK,MAAA,CAAOgC,CAAC,CAAA,CAAG,QAAA,CAAUb,CAAE,CAAC,CAAA,CACrEW,CAAAA,CAAIE,CAAC,CAAA,CAAIb,CAAAA,CACT,QACF,CAEA,GAAI,CAACM,CAAAA,CAAO,MAAQ/B,CAAAA,CAAcyB,CAAC,CAAA,CAAG,CACpCW,CAAAA,CAAIE,CAAC,CAAA,CAAIb,CAAAA,CACT,QACF,CAEA,IAAMY,CAAAA,CAAOJ,CAAAA,CAAKR,CAAAA,CAAGnB,CAAAA,CAAK,MAAA,CAAOgC,CAAC,CAAC,CAAA,CAC/BD,CAAAA,GAAS,MAAA,GAAWD,CAAAA,CAAIE,CAAC,CAAA,CAAID,CAAAA,EACnC,CAEA,GAAIN,CAAAA,CAAO,gBAAA,EAAoB,MAAA,CAAO,IAAA,CAAKK,CAAG,CAAA,CAAE,MAAA,GAAW,EAAG,CAC5DtC,CAAAA,CAAUiC,CAAAA,CAAQ,CAAE,IAAA,CAAM,cAAA,CAAgB,IAAA,CAAAzB,CAAAA,CAAM,QAAA,CAAU6B,CAAW,CAAC,CAAA,CACtE,MACF,CAEA,OAAOC,CACT,CAEA,OAAOD,CACT,CAEA,IAAMI,CAAAA,CAASN,CAAAA,CAAKH,CAAAA,CAAS,EAAE,CAAA,CAE/B,OAAIS,CAAAA,GAAW,MAAA,CACTvC,CAAAA,CAAc8B,CAAO,CAAA,CAAU,EAAC,CAChC,KAAA,CAAM,OAAA,CAAQA,CAAO,CAAA,CAAU,EAAC,CAC7BA,CAAAA,CAGFS,CACT,CAOO,IAAMC,CAAAA,CAAkB,CAACC,CAAAA,CAA+B,EAAC,GACvD,CAAIX,CAAAA,CAAYvC,CAAAA,CAA2B,EAAC,GACjDsC,CAAAA,CAAaC,CAAAA,CAAS,CAAE,GAAGW,CAAAA,CAAa,GAAGlD,CAAQ,CAAC,CAAA,CAG3CmD,CAAAA,CAAwB,MAAA,CAAO,MAAA,CAAOb,EAAc,CAC/D,IAAA,CAAM,CAACY,CAAAA,CAA+B,EAAC,GAAMD,CAAAA,CAAgBC,CAAW,CAC1E,CAAC,CAAA,CAEYE,CAAAA,CAAeD,CAAAA,CAAS,KCjO9B,SAASE,CAAAA,CACdC,EACAC,CAAAA,CACY,CACZ,IAAMV,CAAAA,CAAM,EAAC,CACb,IAAA,IAAWE,CAAAA,IAAKQ,CAAAA,CACVR,CAAAA,IAAKO,CAAAA,GAAKT,CAAAA,CAAIE,CAAC,CAAA,CAAIO,CAAAA,CAAIP,CAAC,GAE9B,OAAOF,CACT,CCTO,SAASW,CAAAA,CACdF,CAAAA,CACAC,CAAAA,CACY,CACZ,IAAMV,CAAAA,CAAM,CAAE,GAAGS,CAAI,CAAA,CACrB,IAAA,IAAWP,CAAAA,IAAKQ,CAAAA,CACd,OAAOV,CAAAA,CAAIE,CAAC,CAAA,CAEd,OAAOF,CACT,CCTO,SAASY,CAAAA,CAAQ5D,CAAAA,CAAyB,CAC/C,OAAI,KAAA,CAAM,OAAA,CAAQA,CAAK,CAAA,CAAUA,CAAAA,CAAM,SAAW,CAAA,CAC9CA,CAAAA,EAAS,OAAOA,CAAAA,EAAU,QAAA,CACrB,MAAA,CAAO,IAAA,CAAKA,CAAe,CAAA,CAAE,MAAA,GAAW,CAAA,CAE1C,KACT,CCNO,SAAS6D,CAAAA,CAAWC,CAAAA,CAAe,CACxC,OAAOA,CAAAA,CAAI,MAAA,CAAO,OAAO,CAC3B,CCSO,SAASC,CAAAA,CACdC,CAAAA,CACAC,CAAAA,CACY,CACZ,IAAMC,CAAAA,CAAiC,EAAC,CAClCC,CAAAA,CAAmC,EAAC,CACpCC,CAAAA,CAAwC,EAAC,CAE/C,IAAA,IAAW7B,CAAAA,IAAO,MAAA,CAAO,IAAA,CAAKyB,CAAM,CAAA,CAAG,CACrC,GAAI,EAAEzB,CAAAA,IAAO0B,CAAAA,CAAAA,CAAQ,CACnBE,CAAAA,CAAQ5B,CAAG,CAAA,CAAIyB,CAAAA,CAAOzB,CAAG,CAAA,CACzB,QACF,CAEK,MAAA,CAAO,EAAA,CAAGyB,CAAAA,CAAOzB,CAAG,CAAA,CAAG0B,CAAAA,CAAM1B,CAAG,CAAC,CAAA,GACpC6B,CAAAA,CAAQ7B,CAAG,CAAA,CAAI,CAAE,IAAA,CAAMyB,CAAAA,CAAOzB,CAAG,CAAA,CAAG,EAAA,CAAI0B,CAAAA,CAAM1B,CAAG,CAAE,CAAA,EAEvD,CAEA,IAAA,IAAWA,CAAAA,IAAO,MAAA,CAAO,IAAA,CAAK0B,CAAK,CAAA,CAC3B1B,CAAAA,IAAOyB,CAAAA,GACXE,CAAAA,CAAM3B,CAAG,CAAA,CAAI0B,CAAAA,CAAM1B,CAAG,CAAA,CAAA,CAI1B,OAAO,CAAE,KAAA,CAAA2B,CAAAA,CAAO,OAAA,CAAAC,CAAAA,CAAS,OAAA,CAAAC,CAAQ,CACnC,CCrCO,SAASC,CAAAA,CAAaC,CAAAA,CAAcC,CAAAA,CAA6B,CACtE,GAAI,CACF,OAAO,IAAA,CAAK,KAAA,CAAMD,CAAI,CACxB,CAAA,KAAQ,CACN,OAAOC,CACT,CACF","file":"index.js","sourcesContent":["export function createCircularTracker() {\n const seen = new WeakSet<object>();\n\n return {\n has(value: object) {\n return seen.has(value);\n },\n add(value: object) {\n seen.add(value);\n },\n };\n}\n","import type { KeyPath } from \"./path\";\n\nexport type DebugEventType =\n | \"normalize\"\n | \"drop\"\n | \"keep\"\n | \"empty-object\"\n | \"empty-array\"\n | \"circular-skip\";\n\nexport interface DebugEvent {\n type: DebugEventType;\n path: KeyPath;\n original: unknown;\n result?: unknown;\n}\n\nexport type DebugOptions = {\n debug?: boolean;\n logger?: (event: DebugEvent) => void;\n};\n\nlet globalDebugOptions: DebugOptions = {};\n\nexport function configureDebug(options: DebugOptions = {}): void {\n globalDebugOptions = { ...globalDebugOptions, ...options };\n}\n\nexport function getDebugOptions(): DebugOptions {\n return globalDebugOptions;\n}\n\nexport function resetDebug(): void {\n globalDebugOptions = {};\n}\n\nfunction resolveDebugOptions(opts: DebugOptions): Required<DebugOptions> {\n return {\n debug: opts.debug ?? globalDebugOptions.debug ?? false,\n logger: opts.logger ?? globalDebugOptions.logger ?? defaultLogger,\n };\n}\n\nfunction defaultLogger(event: DebugEvent): void {\n const c = (globalThis as { console?: { log: (...args: unknown[]) => void } })\n .console;\n c?.log(\"[payload-sanitizer]\", event);\n}\n\nexport function emitDebug(opts: DebugOptions, event: DebugEvent): void {\n const resolved = resolveDebugOptions(opts);\n if (!resolved.debug) return;\n resolved.logger(event);\n}\n","export function 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\nexport function assert(condition: unknown, message: string): asserts condition {\n if (!condition) {\n throw new Error(`[payload-sanitizer] ${message}`);\n }\n}\n","export type KeyPath = Array<string | number>;\n\nexport function 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\nexport function 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\nexport function hasPath(\n list: Array<string | KeyPath> | undefined,\n path: KeyPath,\n): 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","export type DropPreset =\n | \"null\"\n | \"undefined\"\n | \"emptyString\"\n | \"whitespaceString\"\n | \"dash\"\n | \"nan\";\n\nexport const DEFAULT_DROP: DropPreset[] = [\n \"undefined\",\n \"null\",\n \"emptyString\",\n \"whitespaceString\",\n];\n\nexport function 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 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","import { createCircularTracker } from \"./circular\";\nimport type { DebugOptions } from \"./debug\";\nimport { emitDebug } from \"./debug\";\nimport { assert, isPlainObject } from \"./guards\";\nimport { hasPath, type KeyPath } from \"./path\";\nimport { DEFAULT_DROP, type DropPreset, shouldDropPreset } from \"./presets\";\n\nexport type SanitizeOptions = DebugOptions & {\n deep?: boolean;\n trimStrings?: boolean;\n cleanArrays?: boolean;\n drop?: DropPreset[];\n keepKeys?: string[];\n dropKeys?: string[];\n dropValues?: unknown[];\n keepPaths?: Array<string | KeyPath>;\n dropPaths?: Array<string | KeyPath>;\n shouldDrop?: (value: unknown, keyPath: KeyPath) => boolean;\n dropEmptyObjects?: boolean;\n dropEmptyArrays?: boolean;\n strict?: boolean;\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 normalizeValue(value: unknown, trim: boolean): unknown {\n if (typeof value === \"string\" && trim) {\n return value.trim();\n }\n return value;\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 validateStrict(options: SanitizeOptions): void {\n assert(\n options.drop === undefined || Array.isArray(options.drop),\n \"`drop` must be an array\",\n );\n assert(\n options.keepKeys === undefined || Array.isArray(options.keepKeys),\n \"`keepKeys` must be an array\",\n );\n assert(\n options.dropKeys === undefined || Array.isArray(options.dropKeys),\n \"`dropKeys` must be an array\",\n );\n assert(\n options.dropValues === undefined || Array.isArray(options.dropValues),\n \"`dropValues` must be an array\",\n );\n assert(\n options.keepPaths === undefined || Array.isArray(options.keepPaths),\n \"`keepPaths` must be an array\",\n );\n assert(\n options.dropPaths === undefined || Array.isArray(options.dropPaths),\n \"`dropPaths` must be an array\",\n );\n assert(\n options.shouldDrop === undefined || typeof options.shouldDrop === \"function\",\n \"`shouldDrop` must be a function\",\n );\n}\n\nfunction sanitizeImpl<T>(payload: T, options: SanitizeOptions = {}): T {\n const merged = {\n ...DEFAULT_OPTIONS,\n ...options,\n drop: options.drop ?? DEFAULT_OPTIONS.drop,\n };\n\n if (merged.strict) {\n validateStrict(merged);\n }\n\n const circular = createCircularTracker();\n\n function walk(input: unknown, path: KeyPath): unknown {\n const normalized = normalizeValue(input, merged.trimStrings);\n\n emitDebug(merged, {\n type: \"normalize\",\n path,\n original: input,\n result: normalized,\n });\n\n if (hasPath(merged.dropPaths, path)) {\n emitDebug(merged, { type: \"drop\", path, original: normalized });\n return undefined;\n }\n\n const isKeptPath = hasPath(merged.keepPaths, path);\n\n if (isKeptPath) {\n emitDebug(merged, { type: \"keep\", path, original: normalized });\n } else {\n if (shouldDropPreset(normalized, merged.drop)) {\n emitDebug(merged, { type: \"drop\", path, original: normalized });\n return undefined;\n }\n\n if (shouldDropExact(normalized, merged.dropValues)) {\n emitDebug(merged, { type: \"drop\", path, original: normalized });\n return undefined;\n }\n\n if (merged.shouldDrop?.(normalized, path)) {\n emitDebug(merged, { type: \"drop\", path, original: normalized });\n return undefined;\n }\n }\n\n if (Array.isArray(normalized)) {\n if (circular.has(normalized)) {\n emitDebug(merged, { type: \"circular-skip\", path, original: normalized });\n return normalized;\n }\n circular.add(normalized);\n\n if (!merged.cleanArrays) return normalized.slice();\n\n const out: unknown[] = [];\n for (let i = 0; i < normalized.length; i++) {\n const next = walk(normalized[i], path.concat(i));\n if (next !== undefined) out.push(next);\n }\n\n if (merged.dropEmptyArrays && out.length === 0) {\n emitDebug(merged, { type: \"empty-array\", path, original: normalized });\n return undefined;\n }\n\n return out;\n }\n\n if (isPlainObject(normalized)) {\n if (circular.has(normalized)) {\n emitDebug(merged, { type: \"circular-skip\", path, original: normalized });\n return normalized;\n }\n circular.add(normalized);\n\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(normalized)) {\n if (hasKey(merged.dropKeys, k)) continue;\n\n if (hasKey(merged.keepKeys, k)) {\n emitDebug(merged, { type: \"keep\", path: path.concat(k), original: v });\n out[k] = v;\n continue;\n }\n\n if (!merged.deep && isPlainObject(v)) {\n out[k] = v;\n continue;\n }\n\n const next = walk(v, path.concat(k));\n if (next !== undefined) out[k] = next;\n }\n\n if (merged.dropEmptyObjects && Object.keys(out).length === 0) {\n emitDebug(merged, { type: \"empty-object\", path, original: normalized });\n return undefined;\n }\n\n return out;\n }\n\n return normalized;\n }\n\n const result = walk(payload, []);\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 sanitizeImpl(payload, { ...baseOptions, ...options });\n};\n\nexport const sanitize: SanitizerFn = Object.assign(sanitizeImpl, {\n with: (baseOptions: SanitizeOptions = {}) => createSanitizer(baseOptions),\n});\n\nexport const sanitizeWith = sanitize.with;\n","export function pick<T extends object, K extends keyof T>(\n obj: T,\n keys: K[],\n): Pick<T, K> {\n const out = {} as Pick<T, K>;\n for (const k of keys) {\n if (k in obj) out[k] = obj[k];\n }\n return out;\n}\n","export function omit<T extends object, K extends keyof T>(\n obj: T,\n keys: K[],\n): Omit<T, K> {\n const out = { ...obj };\n for (const k of keys) {\n delete out[k];\n }\n return out;\n}\n","export function isEmpty(value: unknown): boolean {\n if (Array.isArray(value)) return value.length === 0;\n if (value && typeof value === \"object\") {\n return Object.keys(value as object).length === 0;\n }\n return false;\n}\n","export function compact<T>(arr: T[]): T[] {\n return arr.filter(Boolean);\n}\n","export type ChangedEntry = {\n from: unknown;\n to: unknown;\n};\n\nexport type DiffResult = {\n added: Record<string, unknown>;\n removed: Record<string, unknown>;\n changed: Record<string, ChangedEntry>;\n};\n\nexport function diff(\n before: Record<string, unknown>,\n after: Record<string, unknown>,\n): DiffResult {\n const added: Record<string, unknown> = {};\n const removed: Record<string, unknown> = {};\n const changed: Record<string, ChangedEntry> = {};\n\n for (const key of Object.keys(before)) {\n if (!(key in after)) {\n removed[key] = before[key];\n continue;\n }\n\n if (!Object.is(before[key], after[key])) {\n changed[key] = { from: before[key], to: after[key] };\n }\n }\n\n for (const key of Object.keys(after)) {\n if (!(key in before)) {\n added[key] = after[key];\n }\n }\n\n return { added, removed, changed };\n}\n","export function safeParse<T>(json: string, fallback?: T): T | undefined {\n try {\n return JSON.parse(json) as T;\n } catch {\n return fallback;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payload-sanitizer",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Tiny zero-dependency payload sanitizer for JS/TS (frontend + backend).",
5
5
  "keywords": [
6
6
  "sanitize",
@@ -28,18 +28,10 @@
28
28
  "LICENSE"
29
29
  ],
30
30
  "sideEffects": false,
31
- "scripts": {
32
- "build": "tsup",
33
- "dev": "tsup --watch",
34
- "test": "vitest",
35
- "typecheck": "tsc -p tsconfig.json --noEmit",
36
- "lint": "eslint .",
37
- "prepublishOnly": "pnpm build && pnpm test && pnpm typecheck"
38
- },
39
- "packageManager": "pnpm@10.29.3",
40
31
  "devDependencies": {
41
32
  "tsup": "^8.5.1",
42
33
  "typescript": "^5.9.3",
34
+ "vitepress": "^1.6.4",
43
35
  "vitest": "^4.0.18"
44
36
  },
45
37
  "publishConfig": {
@@ -47,5 +39,15 @@
47
39
  },
48
40
  "engines": {
49
41
  "node": ">=18"
42
+ },
43
+ "scripts": {
44
+ "build": "tsup",
45
+ "dev": "tsup --watch",
46
+ "test": "vitest",
47
+ "typecheck": "tsc -p tsconfig.json --noEmit",
48
+ "lint": "tsc -p tsconfig.json --noEmit",
49
+ "docs:dev": "vitepress dev docs",
50
+ "docs:build": "vitepress build docs",
51
+ "docs:preview": "vitepress preview docs"
50
52
  }
51
- }
53
+ }