@directus/api
Version:
Directus is a real-time API and App dashboard for managing SQL database content
55 lines (53 loc) • 2.79 kB
JavaScript
import { useEnv } from "@directus/env";
import { InvalidQueryError } from "@directus/errors";
//#region src/database/helpers/fn/json/parse-function.ts
const env = useEnv();
const MAX_JSON_QUERY_DEPTH = Number(env["MAX_JSON_QUERY_DEPTH"]);
/**
* Calculates the depth of a JSON path by counting the number of property accesses and array accesses.
* @example .color → 1
* @example .settings.theme → 2
* @example .items[0].name → 3
* @example [0] → 1
*/
function calculateJsonPathDepth(path) {
let depth = 0;
for (let i = 0; i < path.length; i++) if (path[i] === "." || path[i] === "[") depth++;
return depth;
}
/**
* Validates and normalizes a bare JSON path string (e.g. "color" or "settings.theme").
* Adds a leading dot if absent. Throws on unsupported expressions or depth overflow.
*/
function parseJsonPath(path) {
const normalized = path.startsWith("[") ? path : `.${path}`;
if (/\[\]|\.\.|[*?@$]|\[-/.test(normalized)) throw new InvalidQueryError({ reason: "Invalid JSON path: unsupported path expression" });
if (!/^[\p{L}\p{N}\p{Extended_Pictographic}\w.[\]]+$/u.test(normalized)) throw new InvalidQueryError({ reason: "Invalid JSON path: unsupported path expression" });
const depth = calculateJsonPathDepth(normalized);
if (depth > MAX_JSON_QUERY_DEPTH) throw new InvalidQueryError({ reason: `JSON path depth (${depth}) exceeds allowed maximum of ${MAX_JSON_QUERY_DEPTH}` });
return normalized;
}
/**
* Parses a json function selection into its field and path components.
* Expects relational prefixes to have already been extracted by parseFilterFunctionPath,
* so the field should always be a simple column name.
* @example json(metadata, color) → { field: 'metadata', path: '.color' }
* @example json(data, items[0].name) → { field: 'data', path: '.items[0].name' }
*/
function parseJsonFunction(functionString) {
if (!functionString.startsWith("json(") || !functionString.endsWith(")")) throw new InvalidQueryError({ reason: "Invalid json() syntax" });
const content = functionString.substring(5, functionString.length - 1).trim();
if (!content) throw new InvalidQueryError({ reason: "Invalid json() syntax" });
const commaIndex = content.indexOf(",");
if (commaIndex === -1) throw new InvalidQueryError({ reason: "Invalid json() syntax: requires json(field, path) format" });
if (commaIndex === 0) throw new InvalidQueryError({ reason: "Invalid json() syntax: missing field name" });
const field = content.substring(0, commaIndex).trim();
const pathContent = content.substring(commaIndex + 1).trim();
if (!pathContent) throw new InvalidQueryError({ reason: "Invalid json() syntax: missing path" });
return {
field,
path: parseJsonPath(pathContent)
};
}
//#endregion
export { calculateJsonPathDepth, parseJsonFunction, parseJsonPath };