@sidekick-coder/db
Version:
Cli Tool to manipulate data from diferent sources
649 lines (642 loc) • 18.2 kB
JavaScript
import { parse as parse$1, stringify as stringify$1 } from 'yaml';
import cp from 'child_process';
import fs from 'fs';
import os from 'os';
import path2, { dirname, resolve, basename, join } from 'path';
import * as valibot from 'valibot';
import 'url';
import qs from 'qs';
import * as inquirer from '@inquirer/prompts';
// 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$1;
var stringify = stringify$1;
var YAML = {
parse,
stringify
};
function createFsNode() {
const exists = async (path4) => {
const [, error] = await tryCatch(() => fs.promises.access(path4));
return error ? false : true;
};
const existsSync = (path4) => {
const [, error] = tryCatch.sync(() => fs.accessSync(path4));
return error ? false : true;
};
const read = async (path4) => {
const [content, error] = await tryCatch(() => fs.promises.readFile(path4));
if (error) {
return null;
}
return new Uint8Array(content);
};
const readSync = (path4) => {
const [content, error] = tryCatch.sync(() => fs.readFileSync(path4));
if (error) {
return null;
}
return new Uint8Array(content);
};
const readdir = async (path4) => {
const [files, error] = await tryCatch(() => fs.promises.readdir(path4));
return error ? [] : files;
};
const readdirSync = (path4) => {
const [files, error] = tryCatch.sync(() => fs.readdirSync(path4));
return error ? [] : files;
};
const write = async (path4, content) => {
const [, error] = await tryCatch(() => fs.promises.writeFile(path4, content));
if (error) {
throw error;
}
};
const writeSync = (path4, content) => {
const [, error] = tryCatch.sync(() => fs.writeFileSync(path4, content));
if (error) {
throw error;
}
};
const mkdir2 = async (path4) => {
const [, error] = await tryCatch(() => fs.promises.mkdir(path4));
if (error) {
throw error;
}
};
const mkdirSync = (path4) => {
const [, error] = tryCatch.sync(() => fs.mkdirSync(path4));
if (error) {
throw error;
}
};
const remove = async (path4) => {
const [, error] = await tryCatch(() => fs.promises.rm(path4, { recursive: true }));
if (error) {
throw error;
}
};
const removeSync = (path4) => {
const [, error] = tryCatch.sync(() => fs.rmSync(path4, { recursive: true }));
if (error) {
throw error;
}
};
const removeAt = async (path4, milliseconds) => {
if (os.platform() === "win32") {
const script = `
Set objShell = CreateObject("WScript.Shell")
objShell.Run "cmd /c timeout /t ${milliseconds / 1e3} && del /f /q ${path4}", 0, True
`;
const key = Math.random().toString(36).substring(7);
const tempScriptPath = join(os.tmpdir(), `db-delete-file-${key}.vbs`);
fs.writeFileSync(tempScriptPath, script);
const child = cp.spawn("cscript.exe", [tempScriptPath], {
detached: true,
stdio: "ignore"
});
child.unref();
return true;
}
return false;
};
return {
exists,
existsSync,
read,
readSync,
readdir,
readdirSync,
write,
writeSync,
mkdir: mkdir2,
mkdirSync,
remove,
removeSync,
removeAt
};
}
function createPathNode() {
return {
resolve: (...args) => path2.resolve(...args),
join: (...args) => path2.join(...args),
dirname: (args) => path2.dirname(args),
basename: (args) => path2.basename(args)
};
}
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())
)
);
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(dirname3, path4 = createPathNode()) {
return valibot.pipe(
valibot.string(),
valibot.transform((value) => path4.resolve(dirname3, 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 schema2 = typeof cb === "function" ? cb(v2) : cb;
const { output, issues, success } = v2.safeParse(schema2, 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 schema2;
if (typeof cb === "function") {
schema2 = cb(v2);
} else {
schema2 = cb;
}
const { output, issues, success } = await v2.safeParseAsync(schema2, 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/filesystem/createFilesystem.ts
function createFilesystem(options = {}) {
const fs3 = options.fs || createFsNode();
const path4 = options.path || createPathNode();
const locks2 = /* @__PURE__ */ new Set();
function awaitLock2(filepath, timeout = 1e3) {
return new Promise((resolve3, reject) => {
const interval = setInterval(() => {
if (!locks2.has(filepath)) {
clearInterval(interval);
resolve3();
}
}, 100);
setTimeout(() => {
clearInterval(interval);
reject(new Error("Timeout"));
}, timeout);
});
}
async function read(filepath) {
return fs3.read(filepath);
}
read.text = async function(filepath, options2) {
const content = await read(filepath);
if (!content) {
return (options2 == null ? void 0 : options2.default) || "";
}
return new TextDecoder().decode(content);
};
read.json = async function(filepath, options2) {
const content = await read.text(filepath);
if (!content) {
return (options2 == null ? void 0 : options2.default) || null;
}
const [json, error] = await tryCatch(() => JSON.parse(content, options2 == null ? void 0 : options2.reviver));
if (options2 == null ? void 0 : options2.schema) {
return validate(options2.schema, json);
}
return error ? (options2 == null ? void 0 : options2.default) || null : json;
};
read.yaml = async function(filepath, options2) {
const content = await read.text(filepath);
if (!content) {
return (options2 == null ? void 0 : options2.default) || null;
}
const [yml, error] = await tryCatch(() => YAML.parse(content, options2 == null ? void 0 : options2.reviver));
return error ? (options2 == null ? void 0 : options2.default) || null : yml;
};
function readSync(filepath) {
return fs3.readSync(filepath);
}
readSync.text = function(filepath, defaultValue = "") {
const content = readSync(filepath);
if (!content) {
return defaultValue;
}
return new TextDecoder().decode(content);
};
readSync.json = function(filepath, options2) {
const content = readSync.text(filepath);
if (!content) {
return (options2 == null ? void 0 : options2.default) || null;
}
const [json, error] = tryCatch.sync(() => JSON.parse(content, options2 == null ? void 0 : options2.reviver));
if (options2 == null ? void 0 : options2.schema) {
return validate(options2.schema, json);
}
return error ? (options2 == null ? void 0 : options2.default) || null : json;
};
readSync.yaml = function(filepath, options2) {
const content = readSync.text(filepath);
if (!content) {
return (options2 == null ? void 0 : options2.default) || null;
}
const [yml, error] = tryCatch.sync(() => YAML.parse(content, options2 == null ? void 0 : options2.parseOptions));
return error ? (options2 == null ? void 0 : options2.default) || null : yml;
};
async function readdir(filepath) {
return fs3.readdir(filepath);
}
function readdirSync(filepath) {
return fs3.readdirSync(filepath);
}
async function mkdir2(filepath, options2) {
if (await fs3.exists(filepath)) return;
if (options2 == null ? void 0 : options2.recursive) {
const parent = path4.dirname(filepath);
await mkdir2(parent, options2);
}
await fs3.mkdir(filepath);
}
function mkdirSync(filepath, options2) {
if (fs3.existsSync(filepath)) return;
if (options2 == null ? void 0 : options2.recursive) {
const parent = path4.dirname(filepath);
mkdirSync(parent, options2);
}
fs3.mkdirSync(filepath);
}
async function write(filename, content, options2) {
if (locks2.has(filename)) {
await awaitLock2(filename);
}
locks2.add(filename);
if (options2 == null ? void 0 : options2.recursive) {
const parent = path4.dirname(filename);
await mkdir2(parent, { recursive: true });
}
const [, error] = await tryCatch(() => fs3.write(filename, content));
locks2.delete(filename);
if (error) {
throw error;
}
}
write.text = async function(filename, content, options2) {
await write(filename, new TextEncoder().encode(content), options2);
};
write.json = async function(filename, content, options2) {
await write.text(filename, JSON.stringify(content, null, 2), options2);
};
function writeSync(filename, content, options2) {
if (options2 == null ? void 0 : options2.recursive) {
const parent = path4.dirname(filename);
mkdirSync(parent, { recursive: true });
}
fs3.writeSync(filename, content);
}
writeSync.text = function(filename, content, options2) {
writeSync(filename, new TextEncoder().encode(content), options2);
};
writeSync.json = function(filename, content, options2) {
writeSync.text(filename, JSON.stringify(content, null, 2), options2);
};
function remove(filepath) {
return fs3.remove(filepath);
}
function removeSync(filepath) {
return fs3.removeSync(filepath);
}
function removeAt(filepath, miliseconds) {
return fs3.removeAt(filepath, miliseconds);
}
return {
path: path4,
fs: fs3,
exists: fs3.exists,
existsSync: fs3.existsSync,
read,
readSync,
readdir,
readdirSync,
write,
writeSync,
mkdir: mkdir2,
mkdirSync,
remove,
removeSync,
removeAt
};
}
function createFsFake() {
const entries = /* @__PURE__ */ new Map();
entries.set("/", {
name: "/",
type: "directory",
path: "/"
});
const existsSync = (path4) => {
if (path4 === "/") return true;
const result = entries.has(path4);
return result;
};
const exists = async (path4) => {
return existsSync(path4);
};
const read = async (path4) => {
const entry = entries.get(path4);
if (!entry || entry.type !== "file") {
return null;
}
return entry.content;
};
const readSync = (path4) => {
const entry = entries.get(path4);
if (!entry || entry.type !== "file") {
return null;
}
return entry.content;
};
const readdirSync = (path4) => {
const directory = entries.get(path4);
if (!directory || directory.type !== "directory") {
return [];
}
const result = Array.from(entries.values()).filter((entry) => dirname(entry.path) === directory.path).map((entry) => entry.name);
return result;
};
const readdir = async (path4) => {
return readdirSync(path4);
};
const writeSync = (path4, content) => {
const directory = entries.get(dirname(path4));
if (!directory || directory.type !== "directory") {
throw new Error(`Directory ${dirname(path4)} does not exist`);
}
entries.set(path4, {
name: basename(path4),
path: path4,
type: "file",
content
});
};
const write = async (path4, content) => {
return writeSync(path4, content);
};
const mkdirSync = (path4) => {
const directory = entries.get(dirname(path4));
if (!directory || directory.type !== "directory") {
throw new Error(`Directory ${dirname(path4)} does not exist`);
}
entries.set(path4, {
name: basename(path4),
path: path4,
type: "directory"
});
};
const mkdir2 = async (path4) => {
entries.set(path4, {
name: basename(path4),
path: path4,
type: "directory"
});
};
const removeSync = (path4) => {
const entry = entries.get(path4);
if (!entry) return;
const keys = [path4];
if ((entry == null ? void 0 : entry.type) === "directory") {
Array.from(entries.keys()).filter((key) => key.startsWith(path4)).forEach((key) => keys.push(key));
}
keys.forEach((key) => entries.delete(key));
};
const remove = async (path4) => {
removeSync(path4);
};
const removeAt = async (filepath, milliseconds) => {
setTimeout(() => removeSync(filepath), milliseconds);
return true;
};
return {
entries,
exists,
existsSync,
read,
readSync,
readdir,
readdirSync,
write,
writeSync,
mkdir: mkdir2,
mkdirSync,
remove,
removeSync,
removeAt
};
}
// src/core/filesystem/createPathFake.ts
function createPathFake() {
function parts(...args) {
const result = args.map((a) => a.replace(/\/\//g, "/")).map((a) => a.split("/")).flat().filter((a) => a !== "").filter(Boolean);
return result;
}
function resolve3(...args) {
const normalized = parts(...args).reduce((acc, segment) => {
if (segment === "." || segment === ".") {
return acc;
}
if (segment === "..") {
acc.pop();
return acc;
}
acc.push(segment);
return acc;
}, []).join("/");
let result = normalized;
if (!result.startsWith("/")) {
result = "/" + normalized;
}
return result;
}
function join2(...args) {
return parts(...args).join("/");
}
function dirname3(args) {
if (args === "/") {
return "/";
}
const partsArray = parts(args);
if (partsArray.length < 1) {
return "/";
}
let result = partsArray.slice(0, -1).join("/");
if (!result.startsWith("/")) {
result = "/" + result;
}
return result;
}
function basename2(args) {
return parts(args).slice(-1)[0];
}
return {
resolve: resolve3,
join: join2,
dirname: dirname3,
basename: basename2
};
}
// src/core/filesystem/createFilesystemFake.ts
function createFilesystemFake() {
return createFilesystem({
fs: createFsFake(),
path: createPathFake()
});
}
export { createFilesystemFake };