@oxog/kindof
Version:
Zero-dependency advanced type detection library with TypeScript support, plugin system, and 100% test coverage
410 lines (403 loc) • 12.1 kB
JavaScript
'use strict';
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);
}
exports.createValidator = createValidator;
exports.validateSchema = validateSchema;
//# sourceMappingURL=index.cjs.map