@ai2070/l0
Version:
L0: The Missing Reliability Substrate for AI
185 lines • 7.1 kB
JavaScript
let jsonSchemaAdapter = null;
export function registerJSONSchemaAdapter(adapter) {
jsonSchemaAdapter = adapter;
}
export function unregisterJSONSchemaAdapter() {
jsonSchemaAdapter = null;
}
export function hasJSONSchemaAdapter() {
return jsonSchemaAdapter !== null;
}
export function getJSONSchemaAdapter() {
if (!jsonSchemaAdapter) {
throw new Error("JSON Schema adapter not registered. Call registerJSONSchemaAdapter() first.");
}
return jsonSchemaAdapter;
}
export 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);
}
export 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) };
}
}
export 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);
},
};
}
export 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 !== undefined && 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 !== undefined && value.length < s.minLength) {
errors.push({
path,
message: `String must be at least ${s.minLength} characters`,
keyword: "minLength",
});
}
if (s.maxLength !== undefined && 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 !== undefined && value < s.minimum) {
errors.push({
path,
message: `Number must be >= ${s.minimum}`,
keyword: "minimum",
});
}
if (s.maximum !== undefined && 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: 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;
}
//# sourceMappingURL=jsonSchemaCompat.js.map