shiro-orm
Version:
Access your RONIN database via TypeScript.
541 lines (539 loc) • 15 kB
JavaScript
// ../shiro-syntax/dist/chunk-YC3ZPL7O.js
var getPathSegments = (path) => {
const segments = path.replace(/\\\./g, "\u200B").split(/[[.]/g).map((s) => s.replace(/\u200B/g, ".")).filter((x) => !!x.trim()).map((x) => x.replaceAll("\\.", "."));
return segments;
};
var setPropertyViaPathSegments = (obj, pathSegments, value) => {
let current = obj;
for (let i = 0; i < pathSegments.length; i++) {
const key = pathSegments[i];
const isLastKey = i === pathSegments.length - 1;
if (isLastKey) {
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];
}
}
};
var setProperty = (obj, path, value) => {
if (path === ".") {
Object.assign(obj, value);
} else {
const segments = getPathSegments(path);
setPropertyViaPathSegments(obj, segments, value);
}
return obj;
};
var getProperty = (obj, path) => {
const pathSegments = getPathSegments(path);
let current = obj;
for (const key of pathSegments) {
if (current[key] === null || current[key] === void 0) return void 0;
current = current[key];
}
return current;
};
var isPlainObject = (value) => {
return Object.prototype.toString.call(value) === "[object Object]";
};
var 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);
};
var DML_QUERY_TYPES_READ = ["get", "count"];
var DML_QUERY_TYPES_WRITE = ["set", "add", "remove"];
var DML_QUERY_TYPES = [
...DML_QUERY_TYPES_READ,
...DML_QUERY_TYPES_WRITE
];
var DDL_QUERY_TYPES = ["list", "create", "alter", "drop"];
var QUERY_TYPES = [...DML_QUERY_TYPES, ...DDL_QUERY_TYPES];
var QUERY_SYMBOLS = {
// Represents a sub query.
QUERY: "__RONIN_QUERY",
// Represents an expression that should be evaluated.
EXPRESSION: "__RONIN_EXPRESSION",
// Represents the value of a field in the model.
FIELD: "__RONIN_FIELD_",
// Represents the value of a field in the model of a parent query.
FIELD_PARENT: "__RONIN_FIELD_PARENT_",
// Represents a value provided to a query preset.
VALUE: "__RONIN_VALUE"
};
var RONIN_MODEL_FIELD_REGEX = new RegExp(
`${QUERY_SYMBOLS.FIELD}[_a-zA-Z0-9.]+`,
"g"
);
var CURRENT_TIME_EXPRESSION = {
[QUERY_SYMBOLS.EXPRESSION]: `strftime('%Y-%m-%dT%H:%M:%f', 'now') || 'Z'`
};
var SINGLE_QUOTE_REGEX = /'/g;
var DOUBLE_QUOTE_REGEX = /"/g;
var AMPERSAND_REGEX = /\s*&+\s*/g;
var SPECIAL_CHARACTERS_REGEX = /[^\w\s-]+/g;
var SPLIT_REGEX = /(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|[\s.\-_]+/;
var sanitize = (str) => {
if (!str || str.length === 0) return "";
return str.replace(SINGLE_QUOTE_REGEX, "").replace(DOUBLE_QUOTE_REGEX, "").replace(AMPERSAND_REGEX, " and ").replace(SPECIAL_CHARACTERS_REGEX, " ").trim();
};
var convertToSnakeCase = (str) => {
if (!str || str.length === 0) return "";
return sanitize(str).split(SPLIT_REGEX).map((part) => part.toLowerCase()).join("_");
};
var isObject = (value) => value != null && typeof value === "object" && Array.isArray(value) === false;
var getQuerySymbol = (value) => {
if (!isObject(value)) return null;
const objectValue = value;
if (QUERY_SYMBOLS.QUERY in objectValue) {
return {
type: "query",
value: objectValue[QUERY_SYMBOLS.QUERY]
};
}
if (QUERY_SYMBOLS.EXPRESSION in objectValue) {
return {
type: "expression",
value: objectValue[QUERY_SYMBOLS.EXPRESSION]
};
}
return null;
};
var omit = (obj, properties) => Object.fromEntries(
Object.entries(obj).filter(([key]) => !properties.includes(key))
);
var conjunctions = [
"for",
"and",
"nor",
"but",
"or",
"yet",
"so"
];
var articles = [
"a",
"an",
"the"
];
var prepositions = [
"aboard",
"about",
"above",
"across",
"after",
"against",
"along",
"amid",
"among",
"anti",
"around",
"as",
"at",
"before",
"behind",
"below",
"beneath",
"beside",
"besides",
"between",
"beyond",
"but",
"by",
"concerning",
"considering",
"despite",
"down",
"during",
"except",
"excepting",
"excluding",
"following",
"for",
"from",
"in",
"inside",
"into",
"like",
"minus",
"near",
"of",
"off",
"on",
"onto",
"opposite",
"over",
"past",
"per",
"plus",
"regarding",
"round",
"save",
"since",
"than",
"through",
"to",
"toward",
"towards",
"under",
"underneath",
"unlike",
"until",
"up",
"upon",
"versus",
"via",
"with",
"within",
"without"
];
var lowerCase = /* @__PURE__ */ new Set([
...conjunctions,
...articles,
...prepositions
]);
var specials = [
"ZEIT",
"ZEIT Inc.",
"Vercel",
"Vercel Inc.",
"CLI",
"API",
"HTTP",
"HTTPS",
"JSX",
"DNS",
"URL",
"now.sh",
"now.json",
"vercel.app",
"vercel.json",
"CI",
"CD",
"CDN",
"package.json",
"package.lock",
"yarn.lock",
"GitHub",
"GitLab",
"CSS",
"Sass",
"JS",
"JavaScript",
"TypeScript",
"HTML",
"WordPress",
"Next.js",
"Node.js",
"Webpack",
"Docker",
"Bash",
"Kubernetes",
"SWR",
"TinaCMS",
"UI",
"UX",
"TS",
"TSX",
"iPhone",
"iPad",
"watchOS",
"iOS",
"iPadOS",
"macOS",
"PHP",
"composer.json",
"composer.lock",
"CMS",
"SQL",
"C",
"C#",
"GraphQL",
"GraphiQL",
"JWT",
"JWTs"
];
var word = `[^\\s'\u2019\\(\\)!?;:"-]`;
var regex = new RegExp(`(?:(?:(\\s?(?:^|[.\\(\\)!?;:"-])\\s*)(${word}))|(${word}))(${word}*[\u2019']*${word}*)`, "g");
var convertToRegExp = (specials2) => specials2.map((s) => [new RegExp(`\\b${s}\\b`, "gi"), s]);
function parseMatch(match) {
const firstCharacter = match[0];
if (/\s/.test(firstCharacter)) {
return match.slice(1);
}
if (/[\(\)]/.test(firstCharacter)) {
return null;
}
return match;
}
var src_default = (str, options = {}) => {
str = str.toLowerCase().replace(regex, (m, lead = "", forced, lower, rest, offset, string) => {
const isLastWord = m.length + offset >= string.length;
const parsedMatch = parseMatch(m);
if (!parsedMatch) {
return m;
}
if (!forced) {
const fullLower = lower + rest;
if (lowerCase.has(fullLower) && !isLastWord) {
return parsedMatch;
}
}
return lead + (lower || forced).toUpperCase() + rest;
});
const customSpecials = options.special || [];
const replace = [...specials, ...customSpecials];
const replaceRegExp = convertToRegExp(replace);
replaceRegExp.forEach(([pattern, s]) => {
str = str.replace(pattern, s);
});
return str;
};
var slugToName = (slug) => {
const name = slug.replace(/([a-z])([A-Z])/g, "$1 $2");
return src_default(name);
};
var VOWELS = ["a", "e", "i", "o", "u"];
var pluralize = (word2) => {
const lastLetter = word2.slice(-1).toLowerCase();
const secondLastLetter = word2.slice(-2, -1).toLowerCase();
if (lastLetter === "y" && !VOWELS.includes(secondLastLetter)) {
return `${word2.slice(0, -1)}ies`;
}
if (lastLetter === "s" || word2.slice(-2).toLowerCase() === "ch" || word2.slice(-2).toLowerCase() === "sh" || word2.slice(-2).toLowerCase() === "ex") {
return `${word2}es`;
}
return `${word2}s`;
};
var modelAttributes = [
["pluralSlug", "slug", pluralize, true],
["name", "slug", slugToName, false],
["pluralName", "pluralSlug", slugToName, false],
["idPrefix", "slug", (slug) => slug.slice(0, 3).toLowerCase(), false],
["table", "pluralSlug", convertToSnakeCase, true]
];
var getRecordIdentifier = (prefix) => {
return `${prefix}_${Array.from(crypto.getRandomValues(new Uint8Array(12))).map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, 16).toLowerCase()}`;
};
var addDefaultModelAttributes = (model, isNew) => {
const copiedModel = { ...model };
if (isNew && !copiedModel.id) copiedModel.id = getRecordIdentifier("mod");
for (const [setting, base, generator, mustRegenerate] of modelAttributes) {
if (!(isNew || mustRegenerate)) continue;
if (copiedModel[setting] || !copiedModel[base]) continue;
copiedModel[setting] = generator(copiedModel[base]);
}
const newFields = copiedModel.fields || [];
if (isNew || Object.keys(newFields).length > 0) {
if (!copiedModel.identifiers) copiedModel.identifiers = {};
if (!copiedModel.identifiers.name) {
const suitableField = Object.entries(newFields).find(
([fieldSlug, field]) => field.type === "string" && field.required === true && ["name"].includes(fieldSlug)
);
copiedModel.identifiers.name = suitableField?.[0] || "id";
}
if (!copiedModel.identifiers.slug) {
const suitableField = Object.entries(newFields).find(
([fieldSlug, field]) => field.type === "string" && field.unique === true && field.required === true && ["slug", "handle"].includes(fieldSlug)
);
copiedModel.identifiers.slug = suitableField?.[0] || "id";
}
}
return copiedModel;
};
var ROOT_MODEL = {
slug: "roninModel",
identifiers: {
name: "name",
slug: "slug"
},
// The default ID prefix would be `ron_` based on the slug, but we want `mod_`.
idPrefix: "mod",
// This name mimics the `sqlite_schema` table in SQLite.
table: "ronin_schema",
// Indicates that the model was automatically generated by RONIN.
system: { model: "root" },
fields: {
name: { type: "string" },
pluralName: { type: "string" },
slug: { type: "string" },
pluralSlug: { type: "string" },
idPrefix: { type: "string" },
table: { type: "string" },
"identifiers.name": { type: "string" },
"identifiers.slug": { type: "string" },
// Providing an empty object as a default value allows us to use `json_insert`
// without needing to fall back to an empty object in the insertion statement,
// which makes the statement shorter.
fields: { type: "json", defaultValue: {} },
indexes: { type: "json", defaultValue: {} },
presets: { type: "json", defaultValue: {} }
}
};
var ROOT_MODEL_WITH_ATTRIBUTES = addDefaultModelAttributes(ROOT_MODEL, true);
var PLURAL_MODEL_ENTITIES = {
field: "fields",
index: "indexes",
preset: "presets"
};
var PLURAL_MODEL_ENTITIES_VALUES = Object.values(PLURAL_MODEL_ENTITIES);
var CLEAN_ROOT_MODEL = omit(ROOT_MODEL, ["system"]);
var MULTILINE_SQL_COMMENTS = /\/\*[\s\S]*?\*\//g;
var SINGLELINE_SQL_COMMENTS = /--[^\n]*\n/g;
var IN_SQL_BATCH = false;
var getSyntaxProxySQL = (options) => {
return (strings, ...values) => {
let text = "";
const params = [];
strings.forEach((string, i) => {
const processedString = string.replace(MULTILINE_SQL_COMMENTS, "").replace(SINGLELINE_SQL_COMMENTS, "\n");
text += processedString;
if (i < values.length) {
text += `$${i + 1}`;
params.push(values[i]);
}
});
const statement = {
// Collapse whitespace and newlines into single spaces, then trim leading or
// trailing spaces.
statement: text.replace(/\s+/g, " ").trim(),
params
};
if (IN_SQL_BATCH) return statement;
return options.callback(statement);
};
};
var getBatchProxySQL = (operations) => {
let statements = [];
IN_SQL_BATCH = true;
statements = operations();
IN_SQL_BATCH = false;
return statements;
};
var 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 {
value = mutateStructure(value, (value2) => {
return serializeValue(value2, config?.replacer);
});
}
const structure = { ...targetProps };
const pathParts = config?.root ? [config.root, ...path] : path;
const pathJoined = pathParts.length > 0 ? pathParts.join(".") : ".";
setProperty(structure, pathJoined, value);
if (global.IN_SHIRO_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(target2, nextProp, receiver) {
if (Object.hasOwn(target2, nextProp)) {
return Reflect.get(target2, nextProp, receiver);
}
if (nextProp === "toJSON") return targetProps;
return createProxy(path.concat([nextProp]), targetProps);
}
});
};
return createProxy();
};
var getBatchProxy = (operations) => {
let queries = [];
global.IN_SHIRO_BATCH = true;
try {
queries = operations();
} finally {
global.IN_SHIRO_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;
});
};
var serializeValue = (defaultValue, replacer) => {
let value = defaultValue;
if (typeof value === "undefined") return value;
if (typeof value === "function") {
const ORIGINAL_IN_SHIRO_BATCH = global.IN_SHIRO_BATCH;
global.IN_SHIRO_BATCH = true;
const fieldProxy = new Proxy(
{},
{
get(_target, property) {
const name = property.toString();
return {
[QUERY_SYMBOLS.EXPRESSION]: `${QUERY_SYMBOLS.FIELD}${name}`
};
}
}
);
try {
value = value(fieldProxy);
} finally {
global.IN_SHIRO_BATCH = ORIGINAL_IN_SHIRO_BATCH;
}
}
if (replacer) {
const replacedValue = replacer(value);
if (typeof replacedValue !== "undefined") return replacedValue;
}
return JSON.parse(JSON.stringify(value));
};
export {
setProperty,
getProperty,
QUERY_SYMBOLS,
getQuerySymbol,
getSyntaxProxySQL,
getBatchProxySQL,
getSyntaxProxy,
getBatchProxy
};