UNPKG

@oxog/kindof

Version:

Zero-dependency advanced type detection library with TypeScript support, plugin system, and 100% test coverage

407 lines (401 loc) 12 kB
const TYPE_TAG_MAP = { "[object Arguments]": "arguments", "[object Array]": "array", "[object ArrayBuffer]": "arraybuffer", "[object AsyncFunction]": "asyncfunction", "[object AsyncGeneratorFunction]": "asyncgeneratorfunction", "[object BigInt]": "bigint", "[object BigInt64Array]": "bigint64array", "[object BigUint64Array]": "biguint64array", "[object Boolean]": "boolean", "[object DataView]": "dataview", "[object Date]": "date", "[object Error]": "error", "[object Float32Array]": "float32array", "[object Float64Array]": "float64array", "[object Function]": "function", "[object GeneratorFunction]": "generatorfunction", "[object Int8Array]": "int8array", "[object Int16Array]": "int16array", "[object Int32Array]": "int32array", "[object Map]": "map", "[object Number]": "number", "[object Object]": "object", "[object Promise]": "promise", "[object Proxy]": "proxy", "[object RegExp]": "regexp", "[object Set]": "set", "[object SharedArrayBuffer]": "sharedarraybuffer", "[object String]": "string", "[object Symbol]": "symbol", "[object Uint8Array]": "uint8array", "[object Uint8ClampedArray]": "uint8clampedarray", "[object Uint16Array]": "uint16array", "[object Uint32Array]": "uint32array", "[object WeakMap]": "weakmap", "[object WeakSet]": "weakset", "[object Window]": "window", "[object HTMLDocument]": "document", "[object Document]": "document", "[object Null]": "null", "[object Undefined]": "undefined" }; const toString = Object.prototype.toString; const toStringTag = Symbol.toStringTag; function getNativeType(value) { if (value === void 0) return "undefined"; if (value === null) return "null"; const primitiveType = typeof value; switch (primitiveType) { case "boolean": return "boolean"; case "number": return "number"; case "string": return "string"; case "symbol": return "symbol"; case "bigint": return "bigint"; case "function": return getFunctionType(value); case "object": return getObjectType(value); default: return null; } } function getFunctionType(fn) { const fnString = fn.toString(); if (/^class\s/.test(fnString)) { return "function"; } if (/^async\s*function\s*\*/.test(fnString) || /^async\s*\*/.test(fnString)) { return "asyncgeneratorfunction"; } if (/^async\s/.test(fnString)) { return "asyncfunction"; } if (/^function\s*\*/.test(fnString) || /^\*/.test(fnString)) { return "generatorfunction"; } const ctorName = fn.constructor?.name; if (ctorName === "AsyncFunction") return "asyncfunction"; if (ctorName === "GeneratorFunction") return "generatorfunction"; if (ctorName === "AsyncGeneratorFunction") return "asyncgeneratorfunction"; return "function"; } function getObjectType(obj) { const tag = toString.call(obj); const mappedType = TYPE_TAG_MAP[tag]; if (mappedType) { return mappedType; } if (typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(obj)) { return "buffer"; } if (typeof obj === "object" && "callee" in obj && typeof obj.callee === "function") { return "arguments"; } if (isStream(obj)) { return "stream"; } if (isEventEmitter(obj)) { return "eventemitter"; } if (tag === "[object Object]") { const customTag = obj[toStringTag]; if (typeof customTag === "string") { return customTag.toLowerCase(); } const ctor = obj.constructor; if (ctor && ctor !== Object) { const ctorName = ctor.name; if (ctorName && ctorName !== "Object") { return ctorName.toLowerCase(); } } } if (isDOMElement(obj)) { return "element"; } if (isDOMNode(obj)) { return "node"; } if (isGlobal(obj)) { return "global"; } return "object"; } function isStream(obj) { return obj !== null && typeof obj === "object" && typeof obj.pipe === "function"; } function isEventEmitter(obj) { return obj !== null && typeof obj === "object" && typeof obj.on === "function" && typeof obj.emit === "function" && typeof obj.removeListener === "function"; } function isDOMElement(obj) { if (typeof HTMLElement === "undefined") return false; return obj instanceof HTMLElement; } function isDOMNode(obj) { if (typeof Node === "undefined") return false; return obj instanceof Node; } function isGlobal(obj) { if (typeof globalThis !== "undefined" && obj === globalThis) return true; if (typeof global !== "undefined" && obj === global) return true; if (typeof window !== "undefined" && obj === window) return true; if (typeof self !== "undefined" && obj === self) return true; return false; } const WeakMapPrototype = WeakMap.prototype; const WeakSetPrototype = WeakSet.prototype; const MapPrototype = Map.prototype; const SetPrototype = Set.prototype; const ArrayBufferPrototype = ArrayBuffer.prototype; const DataViewPrototype = DataView.prototype; const SharedArrayBufferPrototype = typeof SharedArrayBuffer !== "undefined" ? SharedArrayBuffer.prototype : null; function checkModernType(value) { if (value === null || value === void 0) { return null; } try { if (value instanceof Promise || isThenable(value)) { return "promise"; } if (value instanceof Map || value.__proto__ === MapPrototype) { return "map"; } if (value instanceof Set || value.__proto__ === SetPrototype) { return "set"; } if (value instanceof WeakMap || value.__proto__ === WeakMapPrototype) { return "weakmap"; } if (value instanceof WeakSet || value.__proto__ === WeakSetPrototype) { return "weakset"; } if (value instanceof ArrayBuffer || value.__proto__ === ArrayBufferPrototype) { return "arraybuffer"; } if (SharedArrayBufferPrototype && (value instanceof SharedArrayBuffer || value.__proto__ === SharedArrayBufferPrototype)) { return "sharedarraybuffer"; } if (value instanceof DataView || value.__proto__ === DataViewPrototype) { return "dataview"; } if (isProxy(value)) { return "proxy"; } } catch { } return null; } function isThenable(value) { return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function"; } function isProxy(value) { try { if (typeof value !== "object" && typeof value !== "function") { return false; } new Proxy({}, value); const descriptor = Object.getOwnPropertyDescriptor(value, Symbol.toStringTag); if (descriptor && !descriptor.configurable && descriptor.value === "Proxy") { return true; } return false; } catch { return false; } } function getTypedArrayType(value) { if (!ArrayBuffer.isView(value)) { return null; } const proto = Object.getPrototypeOf(value); const ctorName = proto?.constructor?.name; switch (ctorName) { case "Int8Array": return "int8array"; case "Uint8Array": return "uint8array"; case "Uint8ClampedArray": return "uint8clampedarray"; case "Int16Array": return "int16array"; case "Uint16Array": return "uint16array"; case "Int32Array": return "int32array"; case "Uint32Array": return "uint32array"; case "Float32Array": return "float32array"; case "Float64Array": return "float64array"; case "BigInt64Array": return "bigint64array"; case "BigUint64Array": return "biguint64array"; default: return null; } } let typeCache = null; let cacheEnabled = true; function kindOfCore(value) { if (value === void 0) return "undefined"; if (value === null) return "null"; const primitiveType = getPrimitiveType(value); if (primitiveType) return primitiveType; if (typeof value === "object" && cacheEnabled && typeCache) { const cached = typeCache.get(value); if (cached) return cached; } let type = getNativeType(value); if (!type || type === "object") { const modernType = checkModernType(value); if (modernType) { type = modernType; } else { const arrayType = getTypedArrayType(value); if (arrayType) { type = arrayType; } else { type = getCustomType(value) || type || "object"; } } } if (typeof value === "object" && cacheEnabled && type) { if (!typeCache) typeCache = /* @__PURE__ */ new WeakMap(); typeCache.set(value, type); } return type || "object"; } function getPrimitiveType(value) { const type = typeof value; switch (type) { case "boolean": case "number": case "string": case "symbol": case "bigint": return type; default: return null; } } function getCustomType(value) { try { const toStringTag = value[Symbol.toStringTag]; if (typeof toStringTag === "string") { return toStringTag.toLowerCase(); } if (value.constructor && value.constructor !== Object) { const ctorName = value.constructor.name; if (ctorName && ctorName !== "Object") { return ctorName.toLowerCase(); } } } catch { } return null; } function validateSchema(value, schema, options = {}) { const errors = []; const context = { path: options.path || "", strict: options.strict ?? true, coerce: options.coerce ?? false, partial: options.partial ?? false }; validateValue(value, schema, context, errors); return { valid: errors.length === 0, errors }; } function validateValue(value, schema, context, errors) { if (typeof schema === "string") { const actualType = kindOfCore(value); if (actualType !== schema) { errors.push({ path: context.path || "root", expected: schema, actual: actualType, message: `Expected type "${schema}" but got "${actualType}"` }); } } else if (Array.isArray(schema)) { if (!Array.isArray(value)) { errors.push({ path: context.path || "root", expected: "array", actual: kindOfCore(value), message: `Expected array but got "${kindOfCore(value)}"` }); return; } const itemSchema = schema[0]; if (itemSchema) { value.forEach((item, index) => { validateValue( item, itemSchema, { ...context, path: `${context.path}[${index}]` }, errors ); }); } } else if (schema && typeof schema === "object") { if (typeof value !== "object" || value === null) { errors.push({ path: context.path || "root", expected: "object", actual: kindOfCore(value), message: `Expected object but got "${kindOfCore(value)}"` }); return; } const obj = value; const schemaObj = schema; if (!context.partial) { for (const key of Object.keys(schemaObj)) { if (!(key in obj)) { errors.push({ path: context.path ? `${context.path}.${key}` : key, expected: typeof schemaObj[key] === "string" ? schemaObj[key] : kindOfCore(schemaObj[key]), actual: "undefined", message: `Missing required property "${key}"` }); } } } for (const key of Object.keys(obj)) { if (key in schemaObj) { const schemaValue = schemaObj[key]; if (schemaValue !== void 0) { validateValue( obj[key], schemaValue, { ...context, path: context.path ? `${context.path}.${key}` : key }, errors ); } } else if (context.strict) { errors.push({ path: context.path ? `${context.path}.${key}` : key, expected: "undefined", actual: kindOfCore(obj[key]), message: `Unexpected property "${key}"` }); } } } } function createValidator(schema, options) { return (value) => validateSchema(value, schema, options); } export { createValidator, validateSchema }; //# sourceMappingURL=index.mjs.map