UNPKG

@sidekick-coder/db

Version:

Cli Tool to manipulate data from diferent sources

537 lines (524 loc) 14.9 kB
'use strict'; var path = require('path'); var fs = require('fs'); var yaml = require('yaml'); var valibot = require('valibot'); require('url'); var qs = require('qs'); var inquirer = require('@inquirer/prompts'); var lodashEs = require('lodash-es'); var fg = require('fast-glob'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var path__default = /*#__PURE__*/_interopDefault(path); var fs__default = /*#__PURE__*/_interopDefault(fs); var valibot__namespace = /*#__PURE__*/_interopNamespace(valibot); var qs__default = /*#__PURE__*/_interopDefault(qs); var inquirer__namespace = /*#__PURE__*/_interopNamespace(inquirer); var fg__default = /*#__PURE__*/_interopDefault(fg); // src/core/drive/index.ts var drive = { exists: async (path4) => { try { await fs__default.default.promises.stat(path4); return true; } catch (error) { return false; } }, existsSync: (path4) => { try { fs__default.default.statSync(path4); return true; } catch (error) { return false; } }, list: async (path4, options) => { if (!await drive.exists(path4)) { return []; } const all = await fs__default.default.promises.readdir(path4, { withFileTypes: true }); if (options == null ? void 0 : options.onlyFiles) { return all.filter((item) => item.isFile()).map((item) => item.name); } if (options == null ? void 0 : options.onlyDirs) { return all.filter((item) => item.isDirectory()).map((item) => item.name); } return all.map((item) => item.name); }, listSync: (path4, options) => { if (!drive.existsSync(path4)) { return []; } const all = fs__default.default.readdirSync(path4, { withFileTypes: true }); if (options == null ? void 0 : options.onlyFiles) { return all.filter((item) => item.isFile()).map((item) => item.name); } if (options == null ? void 0 : options.onlyDirs) { return all.filter((item) => item.isDirectory()).map((item) => item.name); } return all.map((item) => item.name); }, read: async (path4) => { if (!await drive.exists(path4)) { throw new Error(`entry not found ${path4}`); } const stat = await fs__default.default.promises.stat(path4); if (!stat.isFile()) { throw new Error(`entry not file ${path4}`); } return fs__default.default.promises.readFile(path4, "utf-8"); }, readSync: (path4) => { if (!drive.existsSync(path4)) { throw new Error(`entry not found ${path4}`); } const stat = fs__default.default.statSync(path4); if (!stat.isFile()) { throw new Error(`entry not file ${path4}`); } return fs__default.default.readFileSync(path4, "utf-8"); }, write: async (path4, content, options) => { if (options == null ? void 0 : options.recursive) { await drive.mkdir(path.dirname(path4), { recursive: true }); } return fs__default.default.promises.writeFile(path4, content, "utf-8"); }, writeSync: (path4, content, options) => { if (options == null ? void 0 : options.recursive) { const parent = path.dirname(path4); drive.mkdirSync(parent, { recursive: true }); } return fs__default.default.writeFileSync(path4, content, "utf-8"); }, mkdir: async (path4, options) => { if (await drive.exists(path4)) return; if (options == null ? void 0 : options.recursive) { await drive.mkdir(path.dirname(path4)); } return fs__default.default.promises.mkdir(path4); }, mkdirSync: (path4, options) => { if (drive.existsSync(path4)) return; if (options == null ? void 0 : options.recursive) { const parent = path.dirname(path4); drive.mkdirSync(parent, { recursive: true }); } return fs__default.default.mkdirSync(path4); }, destroy: (path4) => { return fs__default.default.promises.rm(path4, { recursive: true }); }, destroySync: (path4) => { return fs__default.default.rmSync(path4, { recursive: true }); } }; var parse = yaml.parse; var stringify = yaml.stringify; var YAML = { parse, stringify }; // src/core/parsers/markdown.ts function parse2(contents) { let body = contents; const result = {}; if (contents.startsWith("---")) { const [, frontmatter, rest] = contents.split("---"); Object.assign(result, YAML.parse(frontmatter)); body = rest.trim(); } result["body"] = body || ""; return result; } function stringify2(data) { const { body, ...properties } = data; let result = ""; if (properties && Object.keys(properties).length) { result += `--- ${YAML.stringify(properties)}--- `; } if (body) { result += body; } return result; } var MD = { parse: parse2, stringify: stringify2 }; // src/core/parsers/all.ts var parsers = []; var json = { name: "json", ext: "json", parse: JSON.parse, stringify: (contents) => JSON.stringify(contents, null, 4) }; var markdown = { name: "markdown", ext: "md", parse: MD.parse, stringify: (contents) => MD.stringify(contents) }; parsers.push(json, markdown); parsers.push({ name: "yaml", ext: "yaml", parse: YAML.parse, stringify: (contents) => YAML.stringify(contents, null, 4) }); parsers.push({ name: "yml", ext: "yml", parse: YAML.parse, stringify: (contents) => YAML.stringify(contents, null, 4) }); // 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]; } }; function readFileSync(path4) { const [content, error] = tryCatch.sync(() => fs__default.default.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 [json2, error] = tryCatch.sync(() => JSON.parse(content, options == null ? void 0 : options.reviver)); return error ? (options == null ? void 0 : options.default) || null : json2; }; 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 path.resolve(path.dirname(folder), value); } return value; }; } var schema = valibot__namespace.optional( valibot__namespace.pipe( valibot__namespace.any(), valibot__namespace.transform((value) => { if (typeof value == "object") { return value; } if (/\.yml$/.test(value)) { const file = value.replace(/^@/, ""); const folder = path.dirname(file); return filesystem.readSync.yaml(value.replace(/^@/, ""), { reviver: createReviver(folder) }); } if (typeof value == "string" && value.includes("=")) { const result = qs__default.default.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__namespace.record(valibot__namespace.string(), valibot__namespace.any()) ) ); function createPathNode() { return { resolve: (...args) => path__default.default.resolve(...args), join: (...args) => path__default.default.join(...args), dirname: (args) => path__default.default.dirname(args), basename: (args) => path__default.default.basename(args) }; } // src/core/validator/valibot.ts var stringList = valibot__namespace.pipe( valibot__namespace.any(), valibot__namespace.transform((value) => { if (typeof value === "string") { return value.split(","); } if (Array.isArray(value)) { return value; } }), valibot__namespace.array(valibot__namespace.string()) ); function array2(s) { return valibot__namespace.pipe( v2.union([v2.array(s), s]), valibot__namespace.transform((value) => Array.isArray(value) ? value : [value]), valibot__namespace.array(s) ); } function path3(dirname5, path4 = createPathNode()) { return valibot__namespace.pipe( valibot__namespace.string(), valibot__namespace.transform((value) => path4.resolve(dirname5, value)) ); } function uint8() { return valibot__namespace.pipe( valibot__namespace.any(), valibot__namespace.check((value) => value instanceof Uint8Array), valibot__namespace.transform((value) => value) ); } var prompts = { password: (options) => valibot__namespace.optionalAsync(valibot__namespace.string(), () => { return inquirer__namespace.password({ message: "Enter password", ...options }); }) }; var extras = { array: array2, vars: schema, stringList, path: path3, uint8, number: valibot__namespace.pipe( valibot__namespace.any(), valibot__namespace.transform(Number), valibot__namespace.check((n) => !isNaN(n)), valibot__namespace.number() ) }; var vWithExtras = { ...valibot__namespace, 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; }; var definition = (root) => v2.object({ dirs: v2.optional(v2.extras.array(v2.extras.path(root)), []), files: v2.optional(v2.extras.array(v2.extras.path(root)), []), patterns: v2.optional(v2.extras.array(v2.extras.path(root)), []), items: v2.optional(v2.array(v2.any()), []) }); var schema2 = (root) => v2.pipe( definition(root), v2.transform((value) => { const items = []; const files = []; for (const item of value.items || []) { items.push({ data: item }); } for (const file of value.files || []) { files.push(path.resolve(root, file)); } for (const dir of value.dirs || []) { const entries = drive.listSync(dir); entries.forEach((entry) => { files.push(path.resolve(dir, entry)); }); } for (const pattern of value.patterns || []) { const path4 = fg__default.default.convertPathToPattern(pattern); const entries = fg__default.default.sync(path4); entries.forEach((entry) => { files.push(path.resolve(entry)); }); } const unique = files.filter((value2, index, self) => self.indexOf(value2) === index); for (const file of unique) { const content = drive.readSync(file); const view2 = YAML.parse(content); items.push({ filename: file, dirname: path.dirname(file), data: view2 }); } return items; }) ); // src/core/config/schemas.ts var viewSources = (root) => v2.pipe( schema2(root), v2.transform((items) => items.map((i) => i.data)), v2.transform((items) => { return items.map((i) => { if (i.extend) { const parent = items.find((x) => x.name === i.extend); return lodashEs.merge({}, parent, i); } return i; }); }) ); var view = (root) => v2.object({ default: v2.optional(v2.string()), sources: v2.optional(viewSources(root)) }); var database = (root) => v2.objectWithRest( { name: v2.string(), provider: v2.object({ name: v2.string(), config: v2.any() }), view: v2.optional(view(root)) }, v2.record(v2.string(), v2.any()) ); var config = (root) => v2.object({ databases: v2.object({ default: v2.optional(v2.string()), sources: v2.pipe( schema2(root), v2.transform((i) => { return i.map((i2) => { if ("dirname" in i2) { return { filename: i2.filename, dirname: i2.dirname, data: validate(database(i2.dirname), i2.data) }; } return { data: validate(database(root), i2.data) }; }); }) ) }) }); function resolve4(filename) { const root = path.dirname(filename); const schema3 = v2.pipe( v2.union([config(root), database(root)]), v2.transform((value) => { if ("databases" in value) { return value; } const item = { filename, dirname: path.dirname(filename), data: value }; return { databases: { default: value.name, sources: [item] } }; }) ); const text = drive.readSync(filename); const yml = YAML.parse(text); const result = validate(schema3, yml); return { dirname: root, filename, ...result }; } exports.resolve = resolve4;