json-accelerator
Version:
Speed up JSON stringification by providing OpenAPI/TypeBox model
334 lines (332 loc) • 12.6 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
createAccelerator: () => createAccelerator,
default: () => index_default,
mergeObjectIntersection: () => mergeObjectIntersection
});
module.exports = __toCommonJS(index_exports);
var Kind = Symbol.for("TypeBox.Kind");
var OptionalKind = Symbol.for("TypeBox.Optional");
var isSpecialProperty = (name) => /(\ |-|\t|\n)/.test(name);
var joinProperty = (v1, v2) => {
if (typeof v2 === "number") return `${v1}[${v2}]`;
if (isSpecialProperty(v2)) return `${v1}["${v2}"]`;
return `${v1}.${v2}`;
};
var encodeProperty = (v) => `"${v}"`;
var isInteger = (schema) => {
if (!schema.anyOf || Kind in schema && schema[Kind] !== "Union")
return false;
let hasIntegerFormat = false;
let hasNumberType = false;
for (const type of schema.anyOf) {
if (type.type === "null" || type.type === "undefined") {
continue;
}
if (!hasIntegerFormat && type.type === "string" && type.format === "integer") {
hasIntegerFormat = true;
continue;
}
if (!hasNumberType && (type.type === "number" || type.type === "integer")) {
hasNumberType = true;
continue;
}
return false;
}
return hasIntegerFormat && hasNumberType;
};
var getMetadata = (schema) => {
let isNullable = false;
let isUndefinable = false;
let newSchema;
if (!schema.anyOf || Kind in schema && schema[Kind] !== "Union")
return {
schema,
isNullable,
isUndefinable
};
for (const type of schema.anyOf) {
if (type.type === "null") {
isNullable = true;
continue;
}
if (type.type === "undefined") {
isUndefinable = true;
continue;
}
if (!newSchema) {
newSchema = type;
continue;
}
return {
schema,
isNullable,
isUndefinable
};
}
return {
schema: newSchema,
isNullable,
isUndefinable
};
};
var mergeObjectIntersection = (schema) => {
if (!schema.allOf || Kind in schema && (schema[Kind] !== "Intersect" || schema.type !== "object"))
return schema;
const { allOf, ...newSchema } = schema;
newSchema.properties = {};
if (Kind in newSchema) newSchema[Kind] = "Object";
for (const type of allOf) {
if (type.type !== "object") continue;
const { properties, required, type: _, [Kind]: __, ...rest } = type;
if (required)
newSchema.required = newSchema.required ? newSchema.required.concat(required) : required;
Object.assign(newSchema, rest);
for (const property in type.properties)
newSchema.properties[property] = mergeObjectIntersection(
type.properties[property]
);
}
return newSchema;
};
var isDateType = (schema) => {
if (!schema.anyOf || Kind in schema && schema[Kind] !== "Union")
return false;
if (!schema.anyOf) return false;
let hasDateType = false;
let hasStringFormatDate = false;
let hasNumberType = false;
if (schema.anyOf)
for (const type of schema.anyOf) {
if (!hasDateType && type.type === "Date") hasDateType = true;
if (!hasStringFormatDate && type.type === "string" && (type.format === "date" || type.format === "date-time"))
hasStringFormatDate = true;
if (!hasNumberType && type.type === "number") hasNumberType = true;
}
return hasDateType;
};
var SANITIZE = {
auto: (property) => `re.test(${property})?JSON.stringify(${property}):\`"\${${property}}"\``,
manual: (property) => `${property}`,
throw: (property) => `re.test(${property})?(()=>{throw new Error("Property '${property}' contains invalid characters")})():${property}`
};
var joinStringArray = (p) => `"\${((p)=>{if(p.length===1)return p
let ars=''
for(let i=0;i<p.length;i++){if(i===0)ars+=p[i]
else ars+=\`","\${p[i]}\`}return ars})(${p})}"`;
var handleRecord = (schema, property, instruction) => {
const child = schema.patternProperties["^(.*)$"] ?? schema.patternProperties[Object.keys(schema.patternProperties)[0]];
if (!child) return property;
const i = instruction.array;
instruction.array++;
return `\${((ar${i}n)=>{const ar${i}s=Object.keys(ar${i}n);let ar${i}v='{';for(let i=0;i<ar${i}s.length;i++){const ar${i}p=ar${i}n[ar${i}s[i]];if(i!==0)ar${i}v+=',';ar${i}v+=\`"\${ar${i}s[i]}":${accelerate(child, `ar${i}p`, instruction)}\`}return ar${i}v + '}'})(${property})}`;
};
var accelerate = (schema, property, instruction) => {
if (!schema) return "";
if (Kind in schema && schema[Kind] === "Import" && schema.$ref in schema.$defs)
return accelerate(schema.$defs[schema.$ref], property, {
...instruction,
definitions: Object.assign(instruction.definitions, schema.$defs)
});
let v = "";
const isRoot = property === "v";
const { schema: newSchema, isNullable, isUndefinable } = getMetadata(schema);
schema = newSchema;
const nullableCondition = isNullable && isUndefinable ? `${property}===null||${property}===undefined` : isNullable ? `${property}===null` : isUndefinable ? `${property}===undefined` : "";
let sanitize = SANITIZE[instruction.sanitize];
switch (schema.type) {
case "string":
instruction.hasString = true;
if (instruction.sanitize === "auto" || // Elysia specific format, this implied that format might contain unescaped JSON string
schema.sanitize) {
sanitize = SANITIZE["auto"];
if (nullableCondition)
v = `\${${nullableCondition}?${schema.const !== void 0 ? `'${JSON.stringify(schema.const)}'` : schema.default !== void 0 ? `'${JSON.stringify(schema.default)}'` : `'null'`}:${sanitize(property)}}`;
else
v = `${schema.const !== void 0 ? `${JSON.stringify(schema.const)}` : `\${${sanitize(property)}}`}`;
} else {
if (nullableCondition)
v = `\${${nullableCondition}?${schema.const !== void 0 ? `'${JSON.stringify(schema.const)}'` : schema.default !== void 0 ? `'${JSON.stringify(schema.default)}'` : `'null'`}:\`\\"\${${sanitize(property)}}\\"\`}`;
else
v = `${schema.const !== void 0 ? `${JSON.stringify(schema.const)}` : `"\${${sanitize(property)}}"`}`;
}
break;
case "number":
case "boolean":
case "bigint":
if (nullableCondition)
v = `\${${property}??${schema.default !== void 0 ? schema.default : `'null'`}}`;
else v = `\${${property}}`;
break;
case "null":
v = `\${${property}}`;
break;
case "undefined":
break;
case "object":
if (nullableCondition) v += `\${${nullableCondition}?"null":\``;
v += "{";
if (schema.additionalProperties) {
v = `\${JSON.stringify(${property})}`;
break;
}
schema = mergeObjectIntersection(schema);
let init = true;
let hasOptional = false;
let op = `op${instruction.optional}`;
if (schema[Kind] === "Record") {
v = handleRecord(schema, property, instruction);
break;
}
if (!Object.keys(schema.properties).length && schema.patternProperties) {
v = `\${JSON.stringify(${property})}`;
break;
}
for (const key in schema.properties)
if (OptionalKind in schema.properties[key]) {
instruction.optional++;
hasOptional = true;
break;
}
for (const key in schema.properties) {
const isOptional = OptionalKind in schema.properties[key];
const name = joinProperty(property, key);
const hasShortName = schema.properties[key].type === "object" && !name.startsWith("ar");
const i2 = instruction.properties.length;
if (hasShortName) instruction.properties.push(name);
const k = encodeProperty(key);
const p = accelerate(
schema.properties[key],
hasShortName ? `s${i2}` : name,
instruction
);
const comma = `\${${op}?',':(${op}=true)&&''}`;
let defaultValue = schema.properties[key].default;
if (defaultValue !== void 0) {
if (typeof defaultValue === "string")
defaultValue = `"${defaultValue}"`;
defaultValue = `\`${comma}${k}:${defaultValue}\``;
} else defaultValue = '""';
v += isOptional ? `\${(${name}===undefined?${defaultValue}:\`${comma}${k}:${p}\`)}` : hasOptional ? `${!init ? "," : `\${(${op}=true)&&""}`}${k}:${p}` : `${!init ? "," : ""}${k}:${p}`;
init = false;
}
v += "}";
if (nullableCondition) v += `\`}`;
break;
case "array":
const i = instruction.array;
instruction.array++;
if (schema.items.type === "string") {
if (isRoot) v += "return `";
if (nullableCondition)
v += `\${${nullableCondition}?"null":${property}.length?\`[${joinStringArray(property)}]\`:"[]"}`;
else
v += `\${${property}.length?\`[${joinStringArray(property)}]\`:"[]"}`;
if (isRoot) v += "`";
break;
}
if (schema.items.type === "number" || schema.items.type === "boolean" || schema.items.type === "bigint" || isInteger(schema.items)) {
if (isRoot) v += "return `";
if (nullableCondition)
v += `\${${nullableCondition}?'"null"':${property}.length?\`[\${${property}.toString()}]\`:"[]"`;
else
v += `\${${property}.length?\`[\${${property}.toString()}]\`:"[]"}`;
if (isRoot) v += "`";
break;
}
if (isNullable || isUndefinable) v += `\${!${property}?'"null"':\``;
if (isRoot) v += `const ar${i}s=${property};`;
else v += `\${((ar${i}s)=>{`;
v += `let ar${i}v='[';for(let i=0;i<ar${i}s.length;i++){const ar${i}p=ar${i}s[i];if(i!==0){ar${i}v+=','}ar${i}v+=\`${accelerate(schema.items, `ar${i}p`, instruction)}\`}return ar${i}v+']'`;
if (!isRoot) v += `})(${property})}`;
if (isNullable || isUndefinable) v += `\`}`;
break;
default:
if (schema.$ref && schema.$ref in instruction.definitions)
return accelerate(
instruction.definitions[schema.$ref],
property,
instruction
);
if (isDateType(schema)) {
if (isNullable || isUndefinable)
v = `\${${nullableCondition}?${schema.default !== void 0 ? `'"${schema.default}"'` : "'null'"}:typeof ${property}==="object"?\`"\${${property}.toISOString()}"\`:${property}}`;
else {
v = `\${typeof ${property}==="object"?\`"\${${property}.toISOString()}"\`:${property}}`;
}
break;
}
if (isInteger(schema)) {
if (nullableCondition)
v = `\${${property}??${schema.default !== void 0 ? schema.default : `'null'`}}`;
else v = `\${${property}}`;
break;
}
v = `\${JSON.stringify(${property})}`;
break;
}
if (!isRoot) return v;
const isArray = schema.type === "array";
if (!isArray) v = `\`${v}\``;
let setup = "";
if (instruction.hasString)
setup += `const re=/[\\b\\f\\n\\r\\t\\\\\\\\/"]/
`;
if (instruction.optional) {
setup += "let ";
for (let i = 0; i < instruction.optional; i++) {
if (i !== 0) setup += ",";
setup += `op${i}=false`;
}
setup += "\n";
}
if (instruction.properties.length) {
setup += "const ";
for (let i = 0; i < instruction.properties.length; i++) {
if (i !== 0) setup += ",";
setup += `s${i}=${instruction.properties[i]}`;
}
setup += "\n";
}
if (isArray) return setup + "\n" + v;
return setup + "return " + v;
};
var createAccelerator = (schema, {
sanitize = "auto",
definitions = {}
} = {}) => {
const f = accelerate(schema, "v", {
array: 0,
optional: 0,
properties: [],
hasString: false,
sanitize,
definitions
});
return Function("v", f);
};
var index_default = createAccelerator;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
createAccelerator,
mergeObjectIntersection
});