UNPKG

@sidekick-coder/db

Version:

Cli Tool to manipulate data from diferent sources

649 lines (642 loc) 18.2 kB
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 };