@ai2070/l0
Version:
L0: The Missing Reliability Substrate for AI
189 lines (188 loc) • 5.75 kB
JavaScript
let jsonSchemaAdapter = null;
function registerJSONSchemaAdapter(adapter) {
jsonSchemaAdapter = adapter;
}
function unregisterJSONSchemaAdapter() {
jsonSchemaAdapter = null;
}
function hasJSONSchemaAdapter() {
return jsonSchemaAdapter !== null;
}
function getJSONSchemaAdapter() {
if (!jsonSchemaAdapter) {
throw new Error(
"JSON Schema adapter not registered. Call registerJSONSchemaAdapter() first."
);
}
return jsonSchemaAdapter;
}
function isJSONSchema(value) {
if (!value || typeof value !== "object") return false;
const schema = value;
return "$schema" in schema || "type" in schema || "properties" in schema || "$ref" in schema || "allOf" in schema || "anyOf" in schema || "oneOf" in schema;
}
function validateJSONSchema(schema, data) {
const adapter = getJSONSchemaAdapter();
const result = adapter.validate(schema, data);
if (result.valid) {
return { success: true, data: result.data };
} else {
const message = adapter.formatErrors(result.errors);
return { success: false, error: new Error(message) };
}
}
function wrapJSONSchema(schema) {
return {
_tag: "jsonschema",
parse(data) {
const result = validateJSONSchema(schema, data);
if (result.success) {
return result.data;
}
throw result.error;
},
safeParse(data) {
return validateJSONSchema(schema, data);
}
};
}
function createSimpleJSONSchemaAdapter() {
return {
validate: (schema, data) => {
const errors = [];
function validateValue(s, value, path) {
if (s.type) {
const types = Array.isArray(s.type) ? s.type : [s.type];
const actualType = getJSONType(value);
const typeMatches = types.some((t) => {
if (t === actualType) return true;
if (t === "integer" && actualType === "number") {
return Number.isInteger(value);
}
return false;
});
if (!typeMatches) {
errors.push({
path,
message: `Expected ${types.join(" or ")}, got ${actualType}`,
keyword: "type"
});
return;
}
}
if (s.enum && !s.enum.includes(value)) {
errors.push({
path,
message: `Value must be one of: ${s.enum.join(", ")}`,
keyword: "enum"
});
}
if (s.const !== void 0 && value !== s.const) {
errors.push({
path,
message: `Value must be ${JSON.stringify(s.const)}`,
keyword: "const"
});
}
if (s.type === "object" && typeof value === "object" && value !== null) {
const obj = value;
if (s.required) {
for (const prop of s.required) {
if (!(prop in obj)) {
errors.push({
path: `${path}/${prop}`,
message: `Missing required property: ${prop}`,
keyword: "required"
});
}
}
}
if (s.properties) {
for (const [key, propSchema] of Object.entries(s.properties)) {
if (key in obj) {
validateValue(propSchema, obj[key], `${path}/${key}`);
}
}
}
}
if (s.type === "array" && Array.isArray(value)) {
if (s.items && !Array.isArray(s.items)) {
value.forEach((item, index) => {
validateValue(
s.items,
item,
`${path}/${index}`
);
});
}
}
if (s.type === "string" && typeof value === "string") {
if (s.minLength !== void 0 && value.length < s.minLength) {
errors.push({
path,
message: `String must be at least ${s.minLength} characters`,
keyword: "minLength"
});
}
if (s.maxLength !== void 0 && value.length > s.maxLength) {
errors.push({
path,
message: `String must be at most ${s.maxLength} characters`,
keyword: "maxLength"
});
}
if (s.pattern) {
const regex = new RegExp(s.pattern);
if (!regex.test(value)) {
errors.push({
path,
message: `String must match pattern: ${s.pattern}`,
keyword: "pattern"
});
}
}
}
if (s.type === "number" && typeof value === "number") {
if (s.minimum !== void 0 && value < s.minimum) {
errors.push({
path,
message: `Number must be >= ${s.minimum}`,
keyword: "minimum"
});
}
if (s.maximum !== void 0 && value > s.maximum) {
errors.push({
path,
message: `Number must be <= ${s.maximum}`,
keyword: "maximum"
});
}
}
}
validateValue(schema, data, "");
if (errors.length === 0) {
return { valid: true, data };
}
return { valid: false, errors };
},
formatErrors: (errors) => {
return errors.map((e) => `${e.path || "/"}: ${e.message}`).join("; ");
}
};
}
function getJSONType(value) {
if (value === null) return "null";
if (Array.isArray(value)) return "array";
return typeof value;
}
export {
createSimpleJSONSchemaAdapter,
getJSONSchemaAdapter,
hasJSONSchemaAdapter,
isJSONSchema,
registerJSONSchemaAdapter,
unregisterJSONSchemaAdapter,
validateJSONSchema,
wrapJSONSchema
};
//# sourceMappingURL=jsonSchemaCompat.js.map