@edge.app/x-cleaners 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,14 @@
1
+ # @edge.app/x-cleaners
2
+
3
+ A helpful utility library of cleaners.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ yarn add @edge.app/x-cleaners
9
+ bun i @edge.app/x-cleaners # or use any other package manager that supports NPM
10
+ ```
11
+
12
+ ## Documentation
13
+
14
+ All exported cleaners are documented in the [`src/cleaners/`](./src/cleaners/) directory. Each cleaner includes TypeScript type definitions and JSDoc comments explaining its usage.
@@ -0,0 +1,96 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __moduleCache = /* @__PURE__ */ new WeakMap;
6
+ var __toCommonJS = (from) => {
7
+ var entry = __moduleCache.get(from), desc;
8
+ if (entry)
9
+ return entry;
10
+ entry = __defProp({}, "__esModule", { value: true });
11
+ if (from && typeof from === "object" || typeof from === "function")
12
+ __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
13
+ get: () => from[key],
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ }));
16
+ __moduleCache.set(from, entry);
17
+ return entry;
18
+ };
19
+ var __export = (target, all) => {
20
+ for (var name in all)
21
+ __defProp(target, name, {
22
+ get: all[name],
23
+ enumerable: true,
24
+ configurable: true,
25
+ set: (newValue) => all[name] = () => newValue
26
+ });
27
+ };
28
+
29
+ // src/index.ts
30
+ var exports_src = {};
31
+ __export(exports_src, {
32
+ asHealingTuple: () => asHealingTuple,
33
+ asHealingObject: () => asHealingObject,
34
+ asHealingArray: () => asHealingArray
35
+ });
36
+ module.exports = __toCommonJS(exports_src);
37
+
38
+ // src/cleaners/asHealingArray.ts
39
+ function asHealingArray(cleaner) {
40
+ return function asHealingArrayCleaner(raw) {
41
+ if (!Array.isArray(raw))
42
+ return [];
43
+ const out = [];
44
+ for (const item of raw) {
45
+ try {
46
+ out.push(cleaner(item));
47
+ } catch (error) {}
48
+ }
49
+ return out;
50
+ };
51
+ }
52
+ // src/cleaners/asHealingObject.ts
53
+ var import_cleaners = require("cleaners");
54
+ function asHealingObject(shape, fallback) {
55
+ if (typeof shape === "function") {
56
+ return function asMaybeObject(raw) {
57
+ if (typeof raw !== "object" || raw == null)
58
+ return {};
59
+ const out = {};
60
+ const keys = fallback == null ? Object.keys(raw) : Object.keys({ ...raw, ...fallback });
61
+ for (let i = 0;i < keys.length; ++i) {
62
+ const key = keys[i];
63
+ if (key === "__proto__")
64
+ continue;
65
+ if (fallback?.[key] == null) {
66
+ try {
67
+ out[key] = shape(raw[key]);
68
+ } catch (error) {}
69
+ } else {
70
+ out[key] = import_cleaners.asMaybe(shape, fallback?.[key])(raw[key]);
71
+ }
72
+ }
73
+ return out;
74
+ };
75
+ }
76
+ const safeShape = { ...shape };
77
+ for (const key of Object.keys(shape)) {
78
+ safeShape[key] = import_cleaners.asMaybe(shape[key], fallback[key]);
79
+ }
80
+ return import_cleaners.asMaybe(import_cleaners.asObject(safeShape), fallback);
81
+ }
82
+ // src/cleaners/asHealingTuple.ts
83
+ var import_cleaners2 = require("cleaners");
84
+ function asHealingTuple(cleaner, fallback) {
85
+ return function asMaybeTuple(raw) {
86
+ if (!Array.isArray(raw)) {
87
+ return [...fallback];
88
+ }
89
+ const out = [];
90
+ const length = fallback.length;
91
+ for (let i = 0;i < length; ++i) {
92
+ out.push(import_cleaners2.asMaybe(cleaner, fallback[i])(raw[i]));
93
+ }
94
+ return out;
95
+ };
96
+ }
@@ -0,0 +1 @@
1
+ { "type": "commonjs" }
@@ -0,0 +1,25 @@
1
+ import type { Cleaner } from 'cleaners';
2
+ /**
3
+ * Cleans an array that may contain errors.
4
+ *
5
+ * Invalid entries are removed from the resulting array (the healing process).
6
+ *
7
+ * @param cleaner - The cleaner function to apply to each array element
8
+ * @returns A cleaner function that returns an array of cleaned values
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { asHealingArray } from '@edge.app/x-cleaners'
13
+ * import { asNumber } from 'cleaners'
14
+ *
15
+ * const cleaner = asHealingArray(asNumber)
16
+ * const result = cleaner([1, 'invalid', 3, null, 5])
17
+ * // Returns: [1, 3, 5]
18
+ * // - Invalid entries are removed
19
+ *
20
+ * // Non-array input returns empty array
21
+ * const result2 = cleaner('not-an-array')
22
+ * // Returns: []
23
+ * ```
24
+ */
25
+ export declare function asHealingArray<T>(cleaner: Cleaner<T>): Cleaner<T[]>;
@@ -0,0 +1,58 @@
1
+ import type { Cleaner, CleanerShape } from 'cleaners';
2
+ /**
3
+ * Cleans an object that may contain errors.
4
+ *
5
+ * Tries to fix individual properties when possible.
6
+ * For key-value objects, this will drop invalid entries unless an optional
7
+ * fallback is provided. For shape objects, this will use the matching property
8
+ * on the required fallback object.
9
+ *
10
+ * @param cleaner - The cleaner function to apply to each object value (for key-value objects)
11
+ * @param fallback - Optional fallback object providing default values for keys that fail validation
12
+ * @returns A cleaner function that returns a cleaned key-value object
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { asHealingObject } from '@edge.app/x-cleaners'
17
+ * import { asNumber } from 'cleaners'
18
+ *
19
+ * // Key-value object with fallback
20
+ * const cleaner = asHealingObject(asNumber, { age: 0, count: 0 })
21
+ * const result = cleaner({ age: '25', count: 'invalid', extra: '10' })
22
+ * // Returns: { age: 25, count: 0, extra: 10 }
23
+ * ```
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * // Key-value object without fallback (invalid entries are dropped)
28
+ * const cleaner = asHealingObject(asNumber)
29
+ * const result = cleaner({ valid: '42', invalid: 'not-a-number' })
30
+ * // Returns: { valid: 42 }
31
+ * ```
32
+ */
33
+ export declare function asHealingObject<T>(cleaner: Cleaner<T>, fallback?: Record<string, T>): Cleaner<Record<string, T>>;
34
+ /**
35
+ * Cleans a shape object that may contain errors.
36
+ *
37
+ * @param shape - A shape object with cleaner functions as values
38
+ * @param fallback - Required fallback object providing default values for all properties
39
+ * @returns A cleaner function that returns a cleaned shape object
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * import { asHealingObject } from '@edge.app/x-cleaners'
44
+ * import { asString, asNumber } from 'cleaners'
45
+ *
46
+ * // Shape object with fallback
47
+ * const shapeCleaner = asHealingObject(
48
+ * { name: asString, age: asNumber },
49
+ * { name: 'Unknown', age: 0 }
50
+ * )
51
+ * const result = shapeCleaner({ name: 'John', age: 'invalid' })
52
+ * // Returns: { name: 'John', age: 0 }
53
+ *
54
+ * const result2 = shapeCleaner({ name: 'John' })
55
+ * // Returns: { name: 'John', age: 0 }
56
+ * ```
57
+ */
58
+ export declare function asHealingObject<T extends object>(shape: CleanerShape<T>, fallback: T): Cleaner<T>;
@@ -0,0 +1,40 @@
1
+ import { type Cleaner } from 'cleaners';
2
+ /**
3
+ * Cleans a tuple that may contain errors.
4
+ *
5
+ * Tries to fix individual elements when possible.
6
+ * The fallback array determines the tuple length and provides default values
7
+ * for invalid entries.
8
+ *
9
+ * @param cleaner - The cleaner function to apply to each tuple element
10
+ * @param fallback - Array of fallback values that defines the tuple length.
11
+ * If an element at index `i` fails validation, `fallback[i]` will be used.
12
+ * @returns A cleaner function that returns a tuple of cleaned values with fixed length
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { asHealingTuple } from '@edge.app/x-cleaners'
17
+ * import { asNumber } from 'cleaners'
18
+ *
19
+ * // Tuple with fallback (invalid entries use fallback at same index)
20
+ * const cleaner = asHealingTuple(asNumber, [0, 0, 0] as const)
21
+ * const result = cleaner([1, 'invalid', 3])
22
+ * // Returns: [1, 0, 3] with type [number, number, number]
23
+ * // - Index 0: 1 is valid number (kept)
24
+ * // - Index 1: 'invalid' fails validation, uses fallback[1] = 0
25
+ * // - Index 2: 3 is valid number (kept)
26
+ *
27
+ * // Tuple size is fixed to fallback length
28
+ * const cleaner2 = asHealingTuple(asNumber, [10, 20] as const)
29
+ * const result2 = cleaner2([1, 2, 3, 4, 5])
30
+ * // Returns: [1, 2] with type [number, number]
31
+ * // - Only first 2 elements are kept (fallback length = 2)
32
+ *
33
+ * // Non-array input returns fallback tuple
34
+ * const result3 = cleaner2('not-an-array')
35
+ * // Returns: [10, 20]
36
+ * ```
37
+ */
38
+ export declare function asHealingTuple<T, const F extends readonly T[]>(cleaner: Cleaner<T>, fallback: F): Cleaner<{
39
+ -readonly [K in keyof F]: T;
40
+ }>;
@@ -0,0 +1,64 @@
1
+ // src/cleaners/asHealingArray.ts
2
+ function asHealingArray(cleaner) {
3
+ return function asHealingArrayCleaner(raw) {
4
+ if (!Array.isArray(raw))
5
+ return [];
6
+ const out = [];
7
+ for (const item of raw) {
8
+ try {
9
+ out.push(cleaner(item));
10
+ } catch (error) {}
11
+ }
12
+ return out;
13
+ };
14
+ }
15
+ // src/cleaners/asHealingObject.ts
16
+ import { asMaybe, asObject } from "cleaners";
17
+ function asHealingObject(shape, fallback) {
18
+ if (typeof shape === "function") {
19
+ return function asMaybeObject(raw) {
20
+ if (typeof raw !== "object" || raw == null)
21
+ return {};
22
+ const out = {};
23
+ const keys = fallback == null ? Object.keys(raw) : Object.keys({ ...raw, ...fallback });
24
+ for (let i = 0;i < keys.length; ++i) {
25
+ const key = keys[i];
26
+ if (key === "__proto__")
27
+ continue;
28
+ if (fallback?.[key] == null) {
29
+ try {
30
+ out[key] = shape(raw[key]);
31
+ } catch (error) {}
32
+ } else {
33
+ out[key] = asMaybe(shape, fallback?.[key])(raw[key]);
34
+ }
35
+ }
36
+ return out;
37
+ };
38
+ }
39
+ const safeShape = { ...shape };
40
+ for (const key of Object.keys(shape)) {
41
+ safeShape[key] = asMaybe(shape[key], fallback[key]);
42
+ }
43
+ return asMaybe(asObject(safeShape), fallback);
44
+ }
45
+ // src/cleaners/asHealingTuple.ts
46
+ import { asMaybe as asMaybe2 } from "cleaners";
47
+ function asHealingTuple(cleaner, fallback) {
48
+ return function asMaybeTuple(raw) {
49
+ if (!Array.isArray(raw)) {
50
+ return [...fallback];
51
+ }
52
+ const out = [];
53
+ const length = fallback.length;
54
+ for (let i = 0;i < length; ++i) {
55
+ out.push(asMaybe2(cleaner, fallback[i])(raw[i]));
56
+ }
57
+ return out;
58
+ };
59
+ }
60
+ export {
61
+ asHealingTuple,
62
+ asHealingObject,
63
+ asHealingArray
64
+ };
@@ -0,0 +1,3 @@
1
+ export * from './cleaners/asHealingArray';
2
+ export * from './cleaners/asHealingObject';
3
+ export * from './cleaners/asHealingTuple';
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@edge.app/x-cleaners",
3
+ "version": "0.1.0",
4
+ "repository": "https://github.com/EdgeApp/x-cleaners",
5
+ "author": "Edge (Airbitz Inc.)",
6
+ "contributors": [
7
+ "Sam Holmes <sam@edge.app>"
8
+ ],
9
+ "license": "MIT",
10
+ "type": "module",
11
+ "source": "./src/index.ts",
12
+ "main": "./dist/cjs/index.js",
13
+ "module": "./dist/esm/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "import": "./dist/esm/index.js",
18
+ "require": "./dist/cjs/index.js",
19
+ "types": "./dist/index.d.ts"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "scripts": {
26
+ "clean": "bun -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true});\"",
27
+ "build:esm": "bun build ./src/index.ts --outdir ./dist/esm --format esm --packages external",
28
+ "build:cjs": "bun build ./src/index.ts --outdir ./dist/cjs --format cjs --packages external && cp cjs-package.json ./dist/cjs/package.json",
29
+ "build:types": "bunx tsc --project tsconfig.build.json",
30
+ "build:copy-types": "bun -e \"const fs=require('fs');fs.mkdirSync('dist',{recursive:true});fs.copyFileSync('src/pouchdb-selector-core.d.ts','dist/pouchdb-selector-core.d.ts');\"",
31
+ "build": "bun run clean && bun run build:esm && bun run build:cjs && bun run build:types && bun run build:copy-types",
32
+ "precommit": "eslint --fix . && bun run test && bun run build",
33
+ "prepare": "(husky || true) && bun run build",
34
+ "release": "bun run prepare && bunx jsr publish && npm publish",
35
+ "test": "bun test",
36
+ "format": "prettier --write ."
37
+ },
38
+ "devDependencies": {
39
+ "@types/bun": "latest",
40
+ "eslint": "^9.39.2",
41
+ "eslint-config-standard-kit": "^1.0.0",
42
+ "husky": "^9.0.11",
43
+ "prettier": "^3.6.2",
44
+ "typescript": "^5.6.3"
45
+ },
46
+ "peerDependencies": {
47
+ "typescript": "^5"
48
+ },
49
+ "dependencies": {
50
+ "cleaners": "^0.3.17"
51
+ }
52
+ }