@sidekick-coder/db
Version:
Cli Tool to manipulate data from diferent sources
429 lines (422 loc) • 11.4 kB
JavaScript
import fs, { promises } from 'fs';
import * as valibot from 'valibot';
import path, { dirname, resolve } from 'path';
import { pathToFileURL } from 'url';
import { parse as parse$2, stringify as stringify$2 } from 'yaml';
import qs, { stringify as stringify$1, parse as parse$1 } from 'qs';
import * as inquirer from '@inquirer/prompts';
import { mergeWith, merge, omit } from 'lodash-es';
import fg from 'fast-glob';
import minimist from 'minimist';
// src/utils/vars.ts
// src/utils/tryCatch.ts
async function tryCatch(tryer) {
try {
const result = await tryer();
return [result, null];
} catch (error) {
return [null, error];
}
}
tryCatch.sync = function(tryer) {
try {
const result = tryer();
return [result, null];
} catch (error) {
return [null, error];
}
};
var parse = parse$2;
var stringify = stringify$2;
var YAML = {
parse,
stringify
};
function readFileSync(path4) {
const [content, error] = tryCatch.sync(() => fs.readFileSync(path4));
if (error) {
return null;
}
return new Uint8Array(content);
}
readFileSync.text = function(filepath, defaultValue = "") {
const content = readFileSync(filepath);
if (!content) {
return defaultValue;
}
return new TextDecoder().decode(content);
};
readFileSync.json = function(path4, options) {
const content = readFileSync.text(path4);
if (!content) {
return (options == null ? void 0 : options.default) || null;
}
const [json, error] = tryCatch.sync(() => JSON.parse(content, options == null ? void 0 : options.reviver));
return error ? (options == null ? void 0 : options.default) || null : json;
};
readFileSync.yaml = function(path4, options) {
const content = readFileSync.text(path4);
if (!content) {
return (options == null ? void 0 : options.default) || null;
}
const [yml, error] = tryCatch.sync(() => YAML.parse(content, options == null ? void 0 : options.parseOptions));
return error ? (options == null ? void 0 : options.default) || null : yml;
};
var filesystem = {
readSync: readFileSync};
function createReviver(folder) {
return (_, value) => {
if (typeof value == "string" && value.startsWith("./")) {
return resolve(dirname(folder), value);
}
return value;
};
}
var schema = valibot.optional(
valibot.pipe(
valibot.any(),
valibot.transform((value) => {
if (typeof value == "object") {
return value;
}
if (/\.yml$/.test(value)) {
const file = value.replace(/^@/, "");
const folder = dirname(file);
return filesystem.readSync.yaml(value.replace(/^@/, ""), {
reviver: createReviver(folder)
});
}
if (typeof value == "string" && value.includes("=")) {
const result = qs.parse(value, { allowEmptyArrays: true });
return result;
}
if (typeof value == "string" && value.startsWith("{")) {
return JSON.parse(value);
}
if (typeof value == "string" && value.startsWith("[")) {
return JSON.parse(value);
}
return value;
}),
valibot.record(valibot.string(), valibot.any())
)
);
function createPathNode() {
return {
resolve: (...args) => path.resolve(...args),
join: (...args) => path.join(...args),
dirname: (args) => path.dirname(args),
basename: (args) => path.basename(args)
};
}
// src/core/validator/valibot.ts
var stringList = valibot.pipe(
valibot.any(),
valibot.transform((value) => {
if (typeof value === "string") {
return value.split(",");
}
if (Array.isArray(value)) {
return value;
}
}),
valibot.array(valibot.string())
);
function array2(s) {
return valibot.pipe(
v2.union([v2.array(s), s]),
valibot.transform((value) => Array.isArray(value) ? value : [value]),
valibot.array(s)
);
}
function path3(dirname2, path4 = createPathNode()) {
return valibot.pipe(
valibot.string(),
valibot.transform((value) => path4.resolve(dirname2, value))
);
}
function uint8() {
return valibot.pipe(
valibot.any(),
valibot.check((value) => value instanceof Uint8Array),
valibot.transform((value) => value)
);
}
var prompts = {
password: (options) => valibot.optionalAsync(valibot.string(), () => {
return inquirer.password({
message: "Enter password",
...options
});
})
};
var extras = {
array: array2,
vars: schema,
stringList,
path: path3,
uint8,
number: valibot.pipe(
valibot.any(),
valibot.transform(Number),
valibot.check((n) => !isNaN(n)),
valibot.number()
)
};
var vWithExtras = {
...valibot,
extras,
prompts
};
var v2 = vWithExtras;
// src/core/validator/validate.ts
function validate(cb, payload) {
const schema3 = typeof cb === "function" ? cb(v2) : cb;
const { output, issues, success } = v2.safeParse(schema3, payload);
if (!success) {
const flatten = v2.flatten(issues);
const messages = [];
if (flatten.root) {
messages.push(...flatten.root);
}
if (flatten.nested) {
Object.entries(flatten.nested).forEach((entry) => {
const [key, value] = entry;
messages.push(...value.map((v3) => `${key}: ${v3}`));
});
}
const message = messages.length ? messages.join(", ") : "Validation failed";
const error = new Error(message);
error.name = "ValidationError";
Object.assign(error, {
messages
});
throw error;
}
return output;
}
validate.async = async function(cb, payload) {
let schema3;
if (typeof cb === "function") {
schema3 = cb(v2);
} else {
schema3 = cb;
}
const { output, issues, success } = await v2.safeParseAsync(schema3, payload);
if (!success) {
const error = new Error("Validation failed");
const flatten = v2.flatten(issues);
const details = {
...flatten.root,
...flatten.nested
};
Object.assign(error, {
details
});
throw error;
}
return output;
};
// src/core/database/where.ts
function parseCondition(condition) {
if (condition.value === "$true") {
return {
or: [],
and: [
{
field: condition.field,
operator: condition.operator,
value: true
}
]
};
}
if (condition.value === "$false") {
return {
or: [],
and: [
{
field: condition.field,
operator: condition.operator,
value: false
}
]
};
}
if (condition.value === "$exists") {
return {
or: [],
and: [
{
field: condition.field,
operator: "exists",
value: true
}
]
};
}
if (typeof condition.value == "string" && condition.value.startsWith("$in")) {
const values = condition.value.slice(4, -1).split(",");
return {
or: [],
and: [
{
field: condition.field,
operator: "in",
value: values
}
]
};
}
return {
and: [condition],
or: []
};
}
function transformWhere(where) {
const { and, or, ...rest } = where;
const result = {
and: [],
or: []
};
if ((rest == null ? void 0 : rest.field) && (rest == null ? void 0 : rest.operator)) {
return {
field: rest.field,
operator: rest.operator,
value: rest.value
};
}
for (const [key, value] of Object.entries(rest)) {
const { and: and2, or: or2 } = parseCondition({
field: (value == null ? void 0 : value.field) || key,
operator: (value == null ? void 0 : value.operator) || "eq",
value: (value == null ? void 0 : value.value) || value
});
if (and2) {
result.and.push(...and2);
}
if (or2) {
result.or.push(...or2);
}
}
if (and == null ? void 0 : and.length) {
and.forEach((w) => {
result.and.push(transformWhere(w));
});
}
if (or == null ? void 0 : or.length) {
or.forEach((w) => {
result.or.push(transformWhere(w));
});
}
if (!result.or.length) {
delete result.or;
}
if (!result.and.length) {
delete result.and;
}
return result;
}
var schema2 = v2.pipe(v2.any(), v2.transform(transformWhere));
function parseFile(filename) {
const extensions = [".yml", ".yaml", ".json"];
if (extensions.some((ext) => filename.endsWith(`*${ext}`))) {
const pattern = fg.convertPathToPattern(filename);
const files = fg.sync(pattern);
return files.map((file) => parseFile(file));
}
const contents = fs.readFileSync(filename, "utf-8");
const reviver = (key, value) => {
if (typeof value == "string" && extensions.some((ext) => value.endsWith(ext))) {
return parseFile(value);
}
return value;
};
if (filename.endsWith(".json")) {
return JSON.parse(contents, reviver);
}
if (filename.endsWith(".yml") || filename.endsWith(".yaml")) {
return YAML.parse(contents, reviver);
}
return contents;
}
function parseVars(payload) {
const items = Array.isArray(payload) ? payload : [payload];
if (!items.length) {
return {};
}
let result = {};
items.forEach((item) => {
const vars = validate((v3) => v3.extras.vars, item);
result = mergeWith(result, vars, (objValue, srcValue) => {
if (Array.isArray(objValue)) {
return objValue.concat(srcValue);
}
});
});
return result;
}
function parseWhere(payload) {
const record2 = parseVars(payload);
return validate(() => schema2, record2);
}
var parse2 = parse$2;
var stringify2 = stringify$2;
var QS = {
parse: parse$1,
stringify: stringify$1
};
async function importAll(path4, options) {
const files = await promises.readdir(path4);
const result = {};
for await (const file of files) {
if ((options == null ? void 0 : options.exclude) && options.exclude.includes(file)) {
continue;
}
const url = pathToFileURL(resolve(path4, file));
const module = await import(url.href);
result[file] = module;
}
return result;
}
function parseOptions(args, options) {
var _a, _b, _c;
const flags = minimist(args, {
string: ["where", "view", "data", "render", "id"],
alias: {
where: "w",
view: "v",
data: "d",
render: "r",
render_options: "ro",
sortBy: ["sort-by"],
sortDesc: ["sort-desc"]
}
});
const result = validate(
(v3) => {
var _a2, _b2;
return v3.objectWithRest(
{
view: v3.optional(v3.string(), (_b2 = (_a2 = options == null ? void 0 : options.databaseDefinition) == null ? void 0 : _a2.view) == null ? void 0 : _b2.default),
render: v3.optional(v3.string()),
data: v3.optional(v3.extras.vars),
where: v3.optional(v3.extras.vars),
include: v3.optional(v3.extras.stringList),
exclude: v3.optional(v3.extras.stringList),
render_options: v3.optional(v3.extras.vars, {}),
sortBy: v3.optional(v3.extras.stringList),
sortDesc: v3.optional(v3.extras.array(v3.boolean()))
},
v3.any()
);
},
flags
);
if (result.view && (options == null ? void 0 : options.databaseDefinition)) {
const view = (_c = (_b = (_a = options.databaseDefinition) == null ? void 0 : _a.view) == null ? void 0 : _b.sources) == null ? void 0 : _c.find((v3) => v3.name === result.view);
merge(result, omit(view, "name"));
}
return result;
}
export { QS, importAll, parse2 as parse, parseFile, parseOptions, parseVars, parseWhere, stringify2 as stringify };