42 lines
2.6 KiB
TypeScript
42 lines
2.6 KiB
TypeScript
import {types} from 'node:util';
|
|
|
|
export const valid_plugin_name = (value: unknown): value is string =>
|
|
typeof value === 'string' && /^[a-zA-Z0-9_-]+$/.test(value);
|
|
export const valid_plugin_path = (value: unknown): value is string =>
|
|
typeof value === 'string' && /^[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_-]+)*$/.test(value);
|
|
|
|
export function plain_plugin_object(value: unknown): value is Record<string, unknown> {
|
|
if (!value || typeof value !== 'object' || types.isProxy(value)) return false;
|
|
const prototype = Object.getPrototypeOf(value);
|
|
if (!prototype || types.isProxy(prototype)) return false;
|
|
const constructor = Object.getOwnPropertyDescriptor(prototype, 'constructor');
|
|
// Accept ordinary objects from other realms, but never custom prototypes.
|
|
if (Object.getPrototypeOf(prototype) !== null || !constructor
|
|
|| typeof constructor.value !== 'function'
|
|
|| types.isProxy(constructor.value) || constructor.value.prototype !== prototype
|
|
|| Function.prototype.toString.call(constructor.value) !== 'function Object() { [native code] }') return false;
|
|
return Reflect.ownKeys(value).every(key => typeof key === 'string'
|
|
&& Object.getOwnPropertyDescriptor(value, key)?.enumerable
|
|
&& Object.hasOwn(Object.getOwnPropertyDescriptor(value, key)!, 'value'));
|
|
}
|
|
|
|
export function valid_plugin_config(value: unknown): value is Record<string, unknown> {
|
|
if (!plain_plugin_object(value)) return false;
|
|
return Object.values(value).every(item => {
|
|
if (item === null || typeof item === 'string' || typeof item === 'boolean') return true;
|
|
if (typeof item === 'number') return Number.isFinite(item);
|
|
if (!Array.isArray(item) || types.isProxy(item)) return false;
|
|
const prototype = Object.getPrototypeOf(item);
|
|
if (!prototype || types.isProxy(prototype)) return false;
|
|
const constructor = Object.getOwnPropertyDescriptor(prototype, 'constructor');
|
|
if (!constructor || typeof constructor.value !== 'function'
|
|
|| types.isProxy(constructor.value) || constructor.value.prototype !== prototype
|
|
|| Function.prototype.toString.call(constructor.value) !== 'function Array() { [native code] }') return false;
|
|
const keys = Reflect.ownKeys(item);
|
|
return keys.length === item.length + 1 && keys.every(key => key === 'length'
|
|
|| typeof key === 'string' && /^(0|[1-9]\d*)$/.test(key)
|
|
&& Object.hasOwn(Object.getOwnPropertyDescriptor(item, key)!, 'value'))
|
|
&& Array.from(item).every(value => typeof value === 'string' || typeof value === 'number' && Number.isFinite(value));
|
|
});
|
|
}
|