DEV Community

Zura Japoshvili
Zura Japoshvili

Posted on

duckkit update β€” validate, encode, and new object utils πŸ¦†

duckkit update β€” validate, encode, and new object utils πŸ¦†

quick update on what I shipped this week in duckkit.

if you haven't seen it before β€” duckkit is a TypeScript utility library. zero dependencies, tree-shakeable, everything properly typed. the kind of helpers you keep rewriting in every project.


what's new

validate module

the one I kept putting off. finally wrote it properly.

import { isEmail, isUrl, isUUID, isIP, isCreditCard, isPhone } from 'duckkit/validate'
import { isString, isNumber, isObject, isNullOrUndefined, isEnum } from 'duckkit/validate'
Enter fullscreen mode Exit fullscreen mode

the type guards actually narrow the type:

function process(value: unknown) {
  if (isString(value)) {
    value.toUpperCase()  // TypeScript knows it's a string βœ…
  }
}
Enter fullscreen mode Exit fullscreen mode

isNaN and isFinite fix the native JS gotchas:

// native β€” broken
isNaN('hello')    // true 😬
isFinite('42')    // true 😬

// duckkit β€” uses Number.isNaN / Number.isFinite
isNaN('hello')    // false βœ…
isFinite('42')    // false βœ…
Enter fullscreen mode Exit fullscreen mode

isEnum checks values, not keys:

enum Direction { Up = 'UP', Down = 'DOWN' }

isEnum('UP', Direction)   // true βœ…
isEnum('Up', Direction)   // false β€” 'Up' is a key, not a value
isEnum('LEFT', Direction) // false
Enter fullscreen mode Exit fullscreen mode

encode module

import { encodeBase64, decodeBase64, encodeHex, decodeHex, encodeUri, decodeUri, encodeJson, decodeJson } from 'duckkit/encode'
Enter fullscreen mode Exit fullscreen mode

all decode functions return null on failure instead of throwing:

decodeBase64('!!!invalid')  // null β€” no try/catch needed βœ…
decodeJson('not json')      // null βœ…
decodeUri('%zz')            // null βœ…
Enter fullscreen mode Exit fullscreen mode

Base64URL for tokens and URLs β€” no +, /, or =:

encodeBase64Url('hello world')  // "aGVsbG8gd29ybGQ" β€” safe in URLs βœ…
Enter fullscreen mode Exit fullscreen mode

new object functions

deepFreeze β€” returns DeepReadonly<T>, mutations caught at compile time:

const config = deepFreeze({ db: { host: 'localhost', port: 5432 } })
config.db.port = 9999  // TypeScript error ❌
Enter fullscreen mode Exit fullscreen mode

objectDiff β€” what changed between two objects:

objectDiff({ a: 1, b: 2 }, { a: 1, b: 3, c: 4 })
// ['b', 'c'] β€” b changed, c was added
Enter fullscreen mode Exit fullscreen mode

mergeIf β€” merge but skip null/undefined values:

mergeIf({ name: 'Zura', role: 'admin' }, { role: undefined, age: 25 })
// { name: 'Zura', role: 'admin', age: 25 } β€” role preserved βœ…
Enter fullscreen mode Exit fullscreen mode

hasOwn β€” safe typed Object.hasOwn:

const obj = { hasOwnProperty: () => false, a: 1 }
obj.hasOwnProperty('a')  // false ❌ β€” overridden
hasOwn(obj, 'a')         // true  βœ… β€” safe
Enter fullscreen mode Exit fullscreen mode

install

npm install duckkit
Enter fullscreen mode Exit fullscreen mode

npm Β· GitHub Β· Docs


what utility do you keep rewriting that should be in here? πŸ‘‡

Top comments (0)