egg
Version:
A web application framework for Node.js
117 lines (116 loc) • 4.06 kB
JavaScript
import util from "node:util";
import { isAsyncFunction, isClass, isFunction, isGeneratorFunction, isPrimitive, isRegExp, isSymbol } from "is-type-of";
//#region src/lib/core/utils.ts
function convertObject(obj, ignore = []) {
if (!Array.isArray(ignore)) ignore = [ignore];
for (const key of Object.keys(obj)) obj[key] = convertValue(key, obj[key], ignore);
return obj;
}
function convertValue(key, value, ignore) {
if (value === null || value === void 0) return value;
let hit = false;
for (const matchKey of ignore) if (typeof matchKey === "string" && matchKey === key) {
hit = true;
break;
} else if (isRegExp(matchKey) && matchKey.test(key)) {
hit = true;
break;
}
if (!hit) {
if (isSymbol(value) || isRegExp(value) || value instanceof URL) return value.toString();
if (isPrimitive(value) || Array.isArray(value)) return value;
}
if (Object.getPrototypeOf(value) === Object.prototype) return convertObject(value, ignore);
const name = value.name || "anonymous";
if (isClass(value)) return `<Class ${name}>`;
if (isFunction(value)) {
if (isGeneratorFunction(value)) return `<GeneratorFunction ${name}>`;
if (isAsyncFunction(value)) return `<AsyncFunction ${name}>`;
return `<Function ${name}>`;
}
const typeName = value.constructor.name;
if (typeName) {
if (Buffer.isBuffer(value) || typeof value === "string") return `<${typeName} len: ${value.length}>`;
return `<${typeName}>`;
}
return util.format(value);
}
function safeParseURL(url) {
try {
return new URL(url);
} catch {
return null;
}
}
/**
* Create a Proxy that behaves like the real object, but remains transparent to
* monkeypatch libraries (e.g. defineProperty-based overrides like egg-mock's `mm()`).
*
* - Lazily creates the real object on first access.
* - Allows overriding properties on the proxy target (overlay) — e.g. via `Object.defineProperty`.
* - Delegates everything else to the real object.
*
* This is used to defer HttpClient construction so plugins can modify
* `config.httpclient.lookup` after the first access to `app.httpClient` but
* before any actual HTTP request is made.
*/
function createTransparentProxy(options) {
const { createReal, bindFunctions = true } = options;
if (typeof createReal !== "function") throw new TypeError("createReal must be a function");
let real;
let cachedError;
const boundFnCache = /* @__PURE__ */ new WeakMap();
const getReal = () => {
if (real) return real;
if (cachedError) throw cachedError;
try {
return real = createReal();
} catch (err) {
cachedError = err;
throw err;
}
};
const hasOwn = (obj, prop) => Reflect.getOwnPropertyDescriptor(obj, prop) !== void 0;
return new Proxy({}, {
get(target, prop, receiver) {
const r = getReal();
if (hasOwn(target, prop)) return Reflect.get(target, prop, receiver);
const value = Reflect.get(r, prop);
if (bindFunctions && typeof value === "function") {
let bound = boundFnCache.get(value);
if (!bound) {
bound = value.bind(r);
boundFnCache.set(value, bound);
}
return bound;
}
return value;
},
set(target, prop, value) {
const r = getReal();
if (hasOwn(target, prop)) return Reflect.set(target, prop, value);
return Reflect.set(r, prop, value);
},
has(target, prop) {
return Reflect.has(target, prop) || Reflect.has(getReal(), prop);
},
ownKeys(target) {
return [.../* @__PURE__ */ new Set([...Reflect.ownKeys(getReal()), ...Reflect.ownKeys(target)])];
},
getOwnPropertyDescriptor(target, prop) {
return Reflect.getOwnPropertyDescriptor(target, prop) ?? Reflect.getOwnPropertyDescriptor(getReal(), prop);
},
deleteProperty(target, prop) {
if (hasOwn(target, prop)) return Reflect.deleteProperty(target, prop);
return Reflect.deleteProperty(getReal(), prop);
},
getPrototypeOf() {
return Reflect.getPrototypeOf(getReal());
},
defineProperty(target, prop, descriptor) {
return Reflect.defineProperty(target, prop, descriptor);
}
});
}
//#endregion
export { convertObject, createTransparentProxy, safeParseURL };