import { parse } from 'yaml';
|
|
const SAFE_CODE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
const SAFE_PACKAGE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
const SAFE_SCENE = /^[a-z][a-z0-9]*(?:\.[a-z][a-z0-9]*)+$/u;
|
const GUID = /^asc\.\{[0-9A-F-]+\}$/u;
|
|
/** @typedef {{ autostart: boolean, bizModule: string, bizScene: string, code: string, devPort: number, directory: string, disabledPluginGuids: readonly string[], guid: string, packageName: string, version: '0.1.0' }} RegistryPlugin */
|
|
/** @param {unknown} value @param {string} message @returns {Record<string, unknown>} */
|
function record(value, message) {
|
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error(message);
|
return /** @type {Record<string, unknown>} */ (value);
|
}
|
|
/** @param {unknown} value @param {RegExp} pattern @param {string} message */
|
function text(value, pattern, message) {
|
if (typeof value !== 'string' || !pattern.test(value)) throw new Error(message);
|
return value;
|
}
|
|
/** @param {RegistryPlugin[]} plugins @param {keyof RegistryPlugin} property @param {string} message */
|
function unique(plugins, property, message) {
|
const values = plugins.map((plugin) => plugin[property]);
|
if (new Set(values).size !== values.length) throw new Error(message);
|
}
|
|
/** @param {string} source */
|
export function parseRegistry(source) {
|
const root = record(parse(source), '插件注册表无效');
|
if (root.version !== 1 || root.release !== '0.1.0-dev' || !Array.isArray(root.plugins) || root.plugins.length === 0) {
|
throw new Error('插件注册表无效');
|
}
|
/** @type {RegistryPlugin[]} */
|
const plugins = root.plugins.map((raw) => {
|
const plugin = record(raw, '插件定义无效');
|
if (plugin.version !== '0.1.0') throw new Error('插件版本必须为 0.1.0');
|
const devPort = plugin['dev-port'];
|
if (typeof devPort !== 'number' || !Number.isInteger(devPort) || devPort < 1024 || devPort > 65_535) throw new Error('插件开发端口无效');
|
const code = text(plugin.code, SAFE_CODE, '插件编码无效');
|
const directory = text(plugin.directory, SAFE_CODE, '插件目录无效');
|
const packageName = text(plugin.package, SAFE_PACKAGE, '插件包名无效');
|
const disabled = plugin['disabled-plugin-guids'] ?? [];
|
if (!Array.isArray(disabled) || disabled.some((guid) => typeof guid !== 'string' || !GUID.test(guid))) throw new Error('禁用插件 GUID 无效');
|
const disabledPluginGuids = /** @type {string[]} */ (disabled);
|
return Object.freeze({
|
autostart: plugin.autostart === true,
|
bizModule: text(plugin['biz-module'], SAFE_SCENE, '业务模块无效'),
|
bizScene: text(plugin['biz-scene'], SAFE_SCENE, '业务场景无效'),
|
code,
|
devPort,
|
directory,
|
disabledPluginGuids: Object.freeze([...disabledPluginGuids]),
|
guid: text(plugin.guid, GUID, '插件 GUID 无效'),
|
packageName,
|
version: '0.1.0',
|
});
|
});
|
unique(plugins, 'code', '插件编码重复');
|
unique(plugins, 'guid', '插件 GUID 重复');
|
unique(plugins, 'devPort', '插件开发端口重复');
|
unique(plugins, 'bizScene', '业务场景重复');
|
return Object.freeze({ plugins: Object.freeze(plugins), release: root.release, version: 1 });
|
}
|