blade
Version:
React at the edge.
220 lines (218 loc) • 7.25 kB
JavaScript
import { r as QUERY_SYMBOLS } from "./dist-C7NzDGd9.js";
//#region ../blade-syntax/dist/queries-CX5Cbnwt.js
/**
* Splits the given path into an array of so-called segments. This is done by
* splitting the path on the `.` character, but only if it's not preceded by a
* `\` character. This is done to allow for setting values on nested records,
* such as `invoice.companyName`.
*
* @param path - Dot-separated path to split into segments.
*
* @returns Array of path segments.
*/
const getPathSegments = (path) => {
return path.replace(/\\\./g, "").split(/[[.]/g).map((s) => s.replace(/\u200B/g, ".")).filter((x) => !!x.trim()).map((x) => x.replaceAll("\\.", "."));
};
/**
* Set the property at the given path to the given value.
*
* @param obj - Object to set the property on.
* @param pathSegments - An array of property keys leading up to the final
* property to set.
* @param value - Value to set at the given path.
*
* @returns Object with the updated property.
*/
const setPropertyViaPathSegments = (obj, pathSegments, value) => {
let current = obj;
for (let i = 0; i < pathSegments.length; i++) {
const key = pathSegments[i];
if (i === pathSegments.length - 1) current[key] = typeof value === "function" ? value(current[key]) : value;
else {
if (!Object.prototype.hasOwnProperty.call(current, key) || typeof current[key] !== "object") current[key] = {};
current = current[key];
}
}
};
const setProperty = (obj, path, value) => {
if (path === ".") Object.assign(obj, value);
else setPropertyViaPathSegments(obj, getPathSegments(path), value);
return obj;
};
/**
* Determines whether an object is a plain object or not.
*
* @param value - The object to check.
*
* @returns A boolean indicating whether the object is plain, or not.
*/
const isPlainObject = (value) => {
return Object.prototype.toString.call(value) === "[object Object]";
};
/**
* Recursively iterates through an object and calls a mutation function for the value of
* every property in the object.
*
* @param obj - The object to mutate.
* @param callback - The function to call for every value in the object.
*
* @returns The mutated object.
*/
const mutateStructure = (obj, callback) => {
if (Array.isArray(obj)) {
obj.map((item) => mutateStructure(item, callback));
return obj;
}
if (isPlainObject(obj)) {
for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key)) {
if (obj[key] === void 0) {
delete obj[key];
continue;
}
if (isPlainObject(obj[key])) {
mutateStructure(obj[key], callback);
continue;
}
if (Array.isArray(obj[key])) {
obj[key].map((item) => mutateStructure(item, callback));
continue;
}
obj[key] = callback(obj[key]);
}
return obj;
}
return callback(obj);
};
/**
* A utility function that creates a proxy object to handle dynamic property access and
* function calls, which is used to compose the query and schema syntax.
*
* @param config - An object containing configuration for the composed structure.
*
* @returns A proxy object that intercepts property access and function calls.
*
* ### Usage
* ```typescript
* const getProxy = getSyntaxProxy<GetQuery>({
* root: `${QUERY_SYMBOLS.QUERY}.get`,
* // Execute the query and return the result
* callback: async (query) => {}
* });
*
* const result = await get.account();
*
* const result = await get.account.with.email('mike@gmail.com');
* ```
*/
const getSyntaxProxy = (config) => {
const propertyValue = typeof config?.propertyValue === "undefined" ? {} : config.propertyValue;
const shouldAllowChaining = config?.chaining ?? true;
const createProxy = (path = [], targetProps, assign) => {
let target;
if (assign) target = { ...targetProps };
else {
target = () => void 0;
target();
delete target.name;
}
return new Proxy(target, {
apply(_, __, args) {
let value = args[0];
const options = args[1];
if (typeof value === "undefined") value = propertyValue;
else {
const shouldUseParentSymbols = Boolean(globalThis.IN_RONIN_QUERY);
value = mutateStructure(value, (inner) => {
const serialized = serializeValue(inner, config?.replacer);
if (shouldUseParentSymbols && typeof serialized === "string") return serialized.replaceAll(QUERY_SYMBOLS.FIELD, QUERY_SYMBOLS.FIELD_PARENT);
return serialized;
});
}
const structure = { ...targetProps };
const pathParts = config?.root ? [config.root, ...path] : path;
setProperty(structure, pathParts.length > 0 ? pathParts.join(".") : ".", value);
if (globalThis.IN_RONIN_BATCH || !config?.callback) {
const newPath = path.slice(0, -1);
const details = { ...structure };
if (options) details.options = options;
return shouldAllowChaining ? createProxy(newPath, details, true) : details;
}
return config.callback(structure, options);
},
get(target$1, nextProp, receiver) {
if (Object.hasOwn(target$1, nextProp)) return Reflect.get(target$1, nextProp, receiver);
if (nextProp === "toJSON") return targetProps;
return createProxy(path.concat([nextProp]), targetProps);
}
});
};
return createProxy();
};
/**
* Obtains a list of queries from a function by wrapping the queries into a context.
*
* @param operations - A function that contains multiple query functions.
*
* @returns A list of queries and their respective options.
*
* ### Usage
* ```typescript
* const queries = getBatchProxy(() => [
* get.accounts(),
* get.account.with.email('mike@gmail.com')
* ]);
* ```
*/
const getBatchProxy = (operations) => {
let queries = [];
globalThis.IN_RONIN_BATCH = true;
try {
queries = operations();
} finally {
globalThis.IN_RONIN_BATCH = false;
}
return queries.map((details) => {
if (!isPlainObject(details)) return { structure: details };
const item = { structure: details[QUERY_SYMBOLS.QUERY] };
if ("options" in details) item.options = details.options;
return item;
});
};
/**
* Serializes a provided value to ensure that the final structure can be sent over the
* network and/or passed to the query compiler.
*
* For example, `Date` objects will be converted into ISO strings.
*
* @param defaultValue - The value to serialize.
* @param replacer - A function that should be used to serialize nested values.
*
* @returns The serialized value.
*/
const serializeValue = (defaultValue, replacer) => {
let value = defaultValue;
if (typeof value === "undefined") return value;
if (typeof value === "function") {
const ORIGINAL_IN_RONIN_BATCH = globalThis.IN_RONIN_BATCH;
globalThis.IN_RONIN_BATCH = true;
const fieldProxy = new Proxy({}, { get(_target, property) {
const name = property.toString();
return { [QUERY_SYMBOLS.EXPRESSION]: `${QUERY_SYMBOLS.FIELD}${name}` };
} });
try {
const ORIGINAL_IN_RONIN_QUERY = globalThis.IN_RONIN_QUERY;
globalThis.IN_RONIN_QUERY = true;
value = value(fieldProxy);
globalThis.IN_RONIN_QUERY = ORIGINAL_IN_RONIN_QUERY;
} finally {
globalThis.IN_RONIN_BATCH = ORIGINAL_IN_RONIN_BATCH;
}
}
if (replacer) {
const replacedValue = replacer(value);
if (typeof replacedValue !== "undefined") return replacedValue;
}
return JSON.parse(JSON.stringify(value));
};
//#endregion
export { getSyntaxProxy as n, getBatchProxy as t };