Skip to content

Commit

Permalink
feat: added isObject utility + fixed stringify/parse
Browse files Browse the repository at this point in the history
  • Loading branch information
oliver-oloughlin committed Nov 26, 2023
1 parent 2c7bb65 commit 532842c
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 6 deletions.
88 changes: 82 additions & 6 deletions json.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isObject } from "./object.ts"

export type JSONError = {
message: string
name: string
Expand All @@ -6,6 +8,7 @@ export type JSONError = {
}

export enum TypeKey {
Undefined = "__undefined__",
BigInt = "__bigint__",
KvU64 = "__kvu64__",
Int8Array = "__int8array__",
Expand All @@ -29,12 +32,47 @@ export enum TypeKey {
NaN = "__nan__",
}

/**
* Serialize a value to Uint8Array.
*
* @param value . Value to be serialized.
* @returns Serialized value.
*/
export function serialize(value: unknown) {
const str = stringify(value)
return new TextEncoder().encode(str)
}

/**
* Deserialize a value encoded as Uint8Array.
*
* @param value - Value to be deserialize.
* @returns Deserialized value.
*/
export function deserialize<T>(value: Uint8Array) {
const str = new TextDecoder().decode(value)
return parse<T>(str)
}

/**
* Convert a value to a JSON string.
*
* @param value - Value to be stringified.
* @param space
* @returns
*/
export function stringify(value: unknown, space?: number | string) {
return JSON.stringify(value, replacer, space)
return JSON.stringify(_replacer(value), replacer, space)
}

/**
* Parse a value from a JSON string.
*
* @param value - JSON string to be parsed.
* @returns
*/
export function parse<T>(value: string) {
return JSON.parse(value, reviver) as T
return postReviver(JSON.parse(value, reviver)) as T
}

/**
Expand All @@ -44,7 +82,7 @@ export function parse<T>(value: string) {
* @param value
* @returns
*/
export function replacer(_key: string, value: unknown) {
function replacer(_key: string, value: unknown) {
return _replacer(value)
}

Expand All @@ -55,7 +93,7 @@ export function replacer(_key: string, value: unknown) {
* @param value
* @returns
*/
export function reviver(_key: string, value: unknown) {
function reviver(_key: string, value: unknown) {
return _reviver(value)
}

Expand All @@ -69,7 +107,6 @@ function _replacer(value: unknown): unknown {
// Return value if primitive, function or symbol
if (
value === null ||
value === undefined ||
typeof value === "string" ||
typeof value === "number" ||
typeof value === "function" ||
Expand All @@ -78,6 +115,13 @@ function _replacer(value: unknown): unknown {
return value
}

// Undefined
if (value === undefined) {
return {
[TypeKey.Undefined]: false,
}
}

// NaN
if (Number.isNaN(value)) {
return {
Expand Down Expand Up @@ -238,7 +282,7 @@ function _replacer(value: unknown): unknown {
}

// Clone value to handle special cases
const clone = structuredClone(value)
const clone = structuredClone(value) as Record<string, unknown>
for (const [k, v] of Object.entries(value)) {
if (v instanceof Date) {
clone[k] = _replacer(v)
Expand Down Expand Up @@ -385,6 +429,38 @@ function _reviver(value: unknown): unknown {
return value
}

/**
* Reviver post-parse.
*
* @param value
* @returns
*/
function postReviver(value: unknown): unknown {
if (
value === undefined ||
value === null ||
typeof value !== "object"
) {
return value
}

if (TypeKey.Undefined in value) {
return undefined
}

if (Array.isArray(value)) {
return value.map((v) => postReviver(v))
}

if (isObject(value)) {
return Object.fromEntries(
Object.entries(value).map(([k, v]) => [k, postReviver(v)]),
)
}

return value
}

/**
* Map from special type entry to value.
*
Expand Down
55 changes: 55 additions & 0 deletions object.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/**
* Remove null and undefined entries from an object.
*
* If input value is null or undefined, the value is returned as is.
*
* @param value
* @returns
*/
export function removeNullish<T>(value: T): NonNullable<T> {
if (typeof value !== "object") {
return value!
Expand All @@ -15,3 +23,50 @@ export function removeNullish<T>(value: T): NonNullable<T> {
.map(([key, val]) => [key, removeNullish(val)]),
) as NonNullable<T>
}

/**
* Check if value is a basic JS object.
*
* @param value
* @returns
*/
export function isObject(value: unknown) {
// If value is null or undefined, return false
if (value === null || value === undefined) {
return false
}

// If value is not an object, return false
if (typeof value !== "object") {
return false
}

// If value is an instance of other KvValue objects, return false
if (
value instanceof Deno.KvU64 ||
value instanceof Array ||
value instanceof Int8Array ||
value instanceof Int16Array ||
value instanceof Int32Array ||
value instanceof BigInt64Array ||
value instanceof Uint8Array ||
value instanceof Uint16Array ||
value instanceof Uint32Array ||
value instanceof BigUint64Array ||
value instanceof Uint8ClampedArray ||
value instanceof Float32Array ||
value instanceof Float64Array ||
value instanceof ArrayBuffer ||
value instanceof Date ||
value instanceof Set ||
value instanceof Map ||
value instanceof RegExp ||
value instanceof DataView ||
value instanceof Error
) {
return false
}

// Return true after performing all checks
return true
}

0 comments on commit 532842c

Please sign in to comment.