feat: add schema marking and validation skip mechanism

This commit is contained in:
dswbx
2025-09-23 13:43:37 +02:00
parent 5e62e681e7
commit 54eee8cd34
3 changed files with 52 additions and 7 deletions

View File

@@ -12,6 +12,7 @@ export {
getMcpServer,
stdioTransport,
McpClient,
logLevels as mcpLogLevels,
type McpClientConfig,
type ToolAnnotation,
type ToolHandlerCtx,
@@ -21,8 +22,35 @@ export { secret, SecretSchema } from "./secret";
export { s };
export const stripMark = <O extends object>(o: O): O => o;
export const mark = <O extends object>(o: O): O => o;
const symbol = Symbol("bknd-validation-mark");
export function stripMark<O = any>(obj: O) {
const newObj = structuredClone(obj);
mark(newObj, false);
return newObj as O;
}
export function mark(obj: any, validated = true) {
try {
if (typeof obj === "object" && obj !== null && !Array.isArray(obj)) {
if (validated) {
obj[symbol] = true;
} else {
delete obj[symbol];
}
for (const key in obj) {
if (typeof obj[key] === "object" && obj[key] !== null) {
mark(obj[key], validated);
}
}
}
} catch (e) {}
}
export function isMarked(obj: any) {
if (typeof obj !== "object" || obj === null) return false;
return obj[symbol] === true;
}
export const stringIdentifier = s.string({
pattern: "^[a-zA-Z_][a-zA-Z0-9_]*$",
@@ -74,6 +102,10 @@ export function parse<S extends s.Schema, Options extends ParseOptions = ParseOp
v: unknown,
opts?: Options,
): Options extends { coerce: true } ? s.StaticCoerced<S> : s.Static<S> {
if (!opts?.forceParse && !opts?.coerce && isMarked(v)) {
return v as any;
}
const schema = (opts?.clone ? cloneSchema(_schema as any) : _schema) as s.Schema;
let value =
opts?.coerce !== false

View File

@@ -1,4 +1,12 @@
import { objectEach, transformObject, McpServer, type s, SecretSchema, setPath } from "bknd/utils";
import {
objectEach,
transformObject,
McpServer,
type s,
SecretSchema,
setPath,
mark,
} from "bknd/utils";
import { DebugLogger } from "core/utils/DebugLogger";
import { Guard } from "auth/authorize/Guard";
import { env } from "core/env";
@@ -65,7 +73,7 @@ export type ModuleManagerOptions = {
// callback after server was created
onServerInit?: (server: Hono<ServerEnv>) => void;
// doesn't perform validity checks for given/fetched config
trustFetched?: boolean;
skipValidation?: boolean;
// runs when initial config provided on a fresh database
seed?: (ctx: ModuleBuildContext) => Promise<void>;
// called right after modules are built, before finish
@@ -124,7 +132,12 @@ export class ModuleManager {
this.emgr = new EventManager({ ...ModuleManagerEvents });
this.logger = new DebugLogger(debug_modules);
this.createModules(options?.initial ?? {});
const config = options?.initial ?? {};
if (options?.skipValidation) {
mark(config, true);
}
this.createModules(config);
}
protected onModuleConfigUpdated(key: string, config: any) {}

View File

@@ -380,8 +380,8 @@ export class DbModuleManager extends ModuleManager {
}
}
if (this.options?.trustFetched === true) {
this.logger.log("trusting fetched config (mark)");
if (this.options?.skipValidation === true) {
this.logger.log("skipping validation (mark)");
mark(result.configs.json);
}