






















@@ -0,0 +1,142 @@
1+import { createHash } from "node:crypto";
2+import AjvPkg, { type AnySchema, type ValidateFunction } from "ajv";
3+4+const installedSymbol = Symbol.for("openclaw.lobster.ajv-compile-cache.installed");
5+const cacheSymbol = Symbol.for("openclaw.lobster.ajv-compile-cache.entries");
6+const maxEntries = 512;
7+8+type AjvInstance = import("ajv").default;
9+10+type CompileCacheEntry = {
11+schema: AnySchema;
12+validate: ValidateFunction;
13+};
14+15+const AjvCtor = AjvPkg as unknown as {
16+new (opts?: object): AjvInstance;
17+prototype: AjvInstance;
18+};
19+20+type AjvWithCompileCache = AjvInstance & {
21+[cacheSymbol]?: Map<string, CompileCacheEntry>;
22+};
23+24+type AjvPrototypePatch = {
25+[installedSymbol]?: boolean;
26+compile: (schema: AnySchema) => ValidateFunction;
27+removeSchema: (schemaKeyRef?: Parameters<AjvInstance["removeSchema"]>[0]) => AjvInstance;
28+};
29+30+type JsonLike = null | boolean | number | string | JsonLike[] | { [key: string]: JsonLike };
31+32+function stableJsonStringify(value: unknown, seen = new WeakSet<object>()): string {
33+if (value === null || typeof value !== "object") {
34+return JSON.stringify(value);
35+}
36+if (seen.has(value)) {
37+throw new TypeError("Cannot cache cyclic JSON schema");
38+}
39+seen.add(value);
40+if (Array.isArray(value)) {
41+const items = value.map((entry) => stableJsonStringify(entry, seen));
42+seen.delete(value);
43+return `[${items.join(",")}]`;
44+}
45+const record = value as Record<string, unknown>;
46+const keys = Object.keys(record).toSorted();
47+const properties = keys
48+.filter((key) => record[key] !== undefined)
49+.map((key) => `${JSON.stringify(key)}:${stableJsonStringify(record[key], seen)}`);
50+seen.delete(value);
51+return `{${properties.join(",")}}`;
52+}
53+54+function compileCacheKey(schema: unknown): string | null {
55+try {
56+return createHash("sha256").update(stableJsonStringify(schema)).digest("hex");
57+} catch {
58+return null;
59+}
60+}
61+62+function readCompileCache(instance: AjvWithCompileCache): Map<string, CompileCacheEntry> {
63+let cache = instance[cacheSymbol];
64+if (!cache) {
65+cache = new Map<string, CompileCacheEntry>();
66+Object.defineProperty(instance, cacheSymbol, {
67+value: cache,
68+configurable: true,
69+});
70+}
71+return cache;
72+}
73+74+function rememberCompiledValidator(params: {
75+cache: Map<string, CompileCacheEntry>;
76+instance: AjvWithCompileCache;
77+key: string;
78+removeSchema: AjvPrototypePatch["removeSchema"];
79+schema: AnySchema;
80+validate: ValidateFunction;
81+}) {
82+const { cache, instance, key, removeSchema, schema, validate } = params;
83+if (!cache.has(key) && cache.size >= maxEntries) {
84+const oldest = cache.keys().next().value;
85+if (oldest !== undefined) {
86+const evicted = cache.get(oldest);
87+cache.delete(oldest);
88+if (evicted) {
89+removeSchema.call(instance, evicted.schema);
90+}
91+}
92+}
93+cache.set(key, { schema, validate });
94+}
95+96+export function installLobsterAjvCompileCache() {
97+const proto = AjvCtor.prototype as unknown as AjvPrototypePatch;
98+if (proto[installedSymbol]) {
99+return;
100+}
101+102+const originalCompile = proto.compile;
103+const originalRemoveSchema = proto.removeSchema;
104+105+Object.defineProperty(proto, installedSymbol, {
106+value: true,
107+configurable: true,
108+});
109+110+proto.compile = function compileWithContentCache(
111+this: AjvWithCompileCache,
112+schema: AnySchema,
113+): ValidateFunction<JsonLike> {
114+const key = compileCacheKey(schema);
115+if (!key) {
116+return originalCompile.call(this, schema) as ValidateFunction<JsonLike>;
117+}
118+const cache = readCompileCache(this);
119+const cached = cache.get(key);
120+if (cached) {
121+return cached.validate as ValidateFunction<JsonLike>;
122+}
123+const validate = originalCompile.call(this, schema) as ValidateFunction<JsonLike>;
124+rememberCompiledValidator({
125+ cache,
126+instance: this,
127+ key,
128+removeSchema: originalRemoveSchema,
129+ schema,
130+ validate,
131+});
132+return validate;
133+};
134+135+proto.removeSchema = function removeSchemaAndClearContentCache(
136+this: AjvWithCompileCache,
137+schemaKeyRef?: Parameters<AjvInstance["removeSchema"]>[0],
138+) {
139+this[cacheSymbol]?.clear();
140+return originalRemoveSchema.call(this, schemaKeyRef);
141+};
142+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。