apiful
Version:
Extensible, typed API tooling
206 lines (200 loc) • 5.85 kB
JavaScript
import { C as CODE_HEADER_DIRECTIVES } from '../shared/apiful.B_nvMJ_g.mjs';
async function jsonToTypeDefinition(data, options = {}) {
const { compile } = await import('json-schema-to-typescript-lite').catch(() => {
throw new Error('Missing dependency "json-schema-to-typescript-lite", please install it');
});
const resolvedOptions = resolveOptions(options);
const schema = createJsonSchema(data, resolvedOptions);
const output = await compile(schema, resolvedOptions.typeName);
return `
${CODE_HEADER_DIRECTIVES}
${output}
`.trimStart();
}
function createJsonSchema(data, options) {
if (Array.isArray(data)) {
if (data.length === 0) {
return {
type: "array"
};
}
const itemSchemas = data.map((item) => createJsonSchema(item, options));
return {
type: "array",
items: mergeSchemas(itemSchemas)
};
} else if (typeof data === "object" && data !== null) {
const properties = Object.fromEntries(
Object.entries(data).map(([key, value]) => [
key,
createJsonSchema(value, options)
])
);
return {
type: "object",
properties,
required: options.strictProperties ? Object.keys(properties) : false,
additionalProperties: Object.keys(data).length === 0
};
} else if (data == null) {
return {
type: "any"
};
} else {
return {
type: typeof data
};
}
}
function mergeSchemas(schemas, options) {
if (schemas.length === 0)
return {};
if (schemas.length === 1)
return schemas[0];
const types = new Set(schemas.map((schema) => schema.type));
if (types.size !== 1) {
return {
anyOf: schemas.map((schema) => ({
...schema,
additionalProperties: schema.type === "object" ? schema.additionalProperties : void 0
}))
};
}
const type = schemas[0].type;
if (type === "object") {
const propertySchemas = /* @__PURE__ */ new Map();
const requiredProperties = /* @__PURE__ */ new Set();
for (const schema of schemas) {
if (!schema.properties)
continue;
for (const [key, value] of Object.entries(schema.properties)) {
if (!propertySchemas.has(key))
propertySchemas.set(key, []);
propertySchemas.get(key).push(value);
}
if (Array.isArray(schema.required)) {
for (const key of schema.required)
requiredProperties.add(key);
}
}
return {
type: "object",
properties: Object.fromEntries(
Array.from(propertySchemas.entries()).map(([key, propertySchemas2]) => [
key,
mergeSchemas(propertySchemas2)
])
),
required: requiredProperties.size > 0 ? Array.from(requiredProperties) : void 0,
additionalProperties: propertySchemas.size === 0
};
} else if (type === "array") {
const itemSchemas = schemas.map((schema) => schema.items).filter((items) => Boolean(items));
return {
type: "array",
items: mergeSchemas(itemSchemas)
};
} else {
return schemas[0];
}
}
function resolveOptions(options) {
return {
typeName: options.typeName || "Root",
strictProperties: options.strictProperties ?? false
};
}
const validatorSymbol = Symbol.for("apiful.validator");
class TypeValidationError extends Error {
value;
cause;
constructor({
value,
cause
}) {
super(
`Type validation failed with value: ${JSON.stringify(value)}
Error message: ${getErrorMessage(cause)}`
);
this.value = value;
this.cause = cause;
}
}
function validator(validate) {
return { [validatorSymbol]: true, validate };
}
function validateTypes({
value,
schema: inputSchema
}) {
const result = safeValidateTypes({ value, schema: inputSchema });
if (!result.success) {
throw new TypeValidationError({ value, cause: result.error });
}
return result.value;
}
function safeValidateTypes({
value,
schema
}) {
try {
if (schema.validate == null) {
return { success: true, value };
}
const result = schema.validate(value);
if (result.success) {
return result;
}
return {
success: false,
error: new TypeValidationError({ value, cause: result.error })
};
} catch (error) {
return {
success: false,
error: new TypeValidationError({ value, cause: error })
};
}
}
function isValidator(value) {
return typeof value === "object" && value !== null && validatorSymbol in value && value[validatorSymbol] === true && "validate" in value;
}
function getErrorMessage(error) {
if (error == null) {
return "unknown error";
}
if (typeof error === "string") {
return error;
}
if (error instanceof Error) {
return error.message;
}
return JSON.stringify(error);
}
const schemaSymbol = Symbol.for("apiful.schema");
function jsonSchema(jsonSchema2, { validate } = {}) {
return {
[schemaSymbol]: true,
_type: void 0,
// Should never be used directly
[validatorSymbol]: true,
jsonSchema: jsonSchema2,
validate
};
}
function isSchema(value) {
return typeof value === "object" && value !== null && schemaSymbol in value && value[schemaSymbol] === true && "jsonSchema" in value && "validate" in value;
}
function base64ToUint8Array(base64String) {
const base64Url = base64String.replaceAll("-", "+").replaceAll("_", "/");
const latin1String = globalThis.atob(base64Url);
return Uint8Array.from(latin1String, (byte) => byte.charCodeAt(0));
}
function uint8ArrayToBase64(array) {
let latin1String = "";
for (const byte of array) {
latin1String += String.fromCharCode(byte);
}
return globalThis.btoa(latin1String);
}
export { TypeValidationError, base64ToUint8Array, isSchema, isValidator, jsonSchema, jsonToTypeDefinition, safeValidateTypes, uint8ArrayToBase64, validateTypes, validator, validatorSymbol };