UNPKG

@sidekick-coder/db

Version:

Cli Tool to manipulate data from diferent sources

1,804 lines (1,770 loc) 76.7 kB
'use strict'; var valibot = require('valibot'); var path = require('path'); var fs3 = require('fs'); require('url'); var yaml = require('yaml'); var qs = require('qs'); var inquirer = require('@inquirer/prompts'); var lodashEs = require('lodash-es'); var sift = require('sift'); var cp = require('child_process'); var os = require('os'); var dateFns = require('date-fns'); var crypto2 = require('crypto'); var omit4 = require('lodash-es/omit.js'); var pick2 = require('lodash-es/pick.js'); var ms = require('ms'); 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 valibot__namespace = /*#__PURE__*/_interopNamespace(valibot); var path__default = /*#__PURE__*/_interopDefault(path); var fs3__default = /*#__PURE__*/_interopDefault(fs3); var qs__default = /*#__PURE__*/_interopDefault(qs); var inquirer__namespace = /*#__PURE__*/_interopNamespace(inquirer); var sift__default = /*#__PURE__*/_interopDefault(sift); var cp__default = /*#__PURE__*/_interopDefault(cp); var os__default = /*#__PURE__*/_interopDefault(os); var crypto2__default = /*#__PURE__*/_interopDefault(crypto2); var omit4__default = /*#__PURE__*/_interopDefault(omit4); var pick2__default = /*#__PURE__*/_interopDefault(pick2); var ms__default = /*#__PURE__*/_interopDefault(ms); var fg__default = /*#__PURE__*/_interopDefault(fg); // src/core/validator/valibot.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 = yaml.parse; var stringify = yaml.stringify; var YAML = { parse, stringify }; function readFileSync(path4) { const [content, error] = tryCatch.sync(() => fs3__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 schema12 = typeof cb === "function" ? cb(v2) : cb; const { output, issues, success } = v2.safeParse(schema12, 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 schema12; if (typeof cb === "function") { schema12 = cb(v2); } else { schema12 = cb; } const { output, issues, success } = await v2.safeParseAsync(schema12, 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)); // src/core/database/list.ts var schema3 = vWithExtras.objectWithRest( { where: vWithExtras.optional(schema2), limit: vWithExtras.optional(vWithExtras.number()), page: vWithExtras.optional(vWithExtras.number()), cursor: vWithExtras.optional(vWithExtras.string()), include: vWithExtras.optional(vWithExtras.extras.stringList), exclude: vWithExtras.optional(vWithExtras.extras.stringList), sortBy: vWithExtras.optional(vWithExtras.extras.array(vWithExtras.string())), sortDesc: vWithExtras.optional(vWithExtras.extras.array(vWithExtras.boolean())) }, vWithExtras.any() ); async function list(provider5, payload) { const options = validate(schema3, payload); if (!provider5.list) { throw new Error(`Provider does not support listing`); } return await provider5.list(options); } // src/core/database/find.ts var schema4 = vWithExtras.objectWithRest( { where: vWithExtras.optional(schema2), include: vWithExtras.optional(vWithExtras.extras.stringList), exclude: vWithExtras.optional(vWithExtras.extras.stringList) }, vWithExtras.any() ); async function find(provider5, payload) { const options = validate(schema4, payload); if (!provider5.find) { throw new Error(`Provider does not support find`); } return await provider5.find(options); } // src/core/database/data.ts var schema5 = () => v2.pipe( v2.record(v2.string(), v2.any()), v2.transform((value) => { const result = {}; Object.entries(value).forEach(([k, v3]) => { if (v3 === "$undefined") { v3 = void 0; } result[k] = v3; }); return result; }) ); // src/core/database/create.ts var schema6 = vWithExtras.objectWithRest( { data: schema5() }, vWithExtras.any() ); async function create(provider5, payload) { if (!provider5.create) { throw new Error(`Provider does not support creating`); } const options = validate(schema6, payload); return provider5.create(options); } // src/core/database/update.ts var schema7 = vWithExtras.objectWithRest( { data: schema5(), where: vWithExtras.optional(schema2), limit: vWithExtras.optional(vWithExtras.number()) }, vWithExtras.any() ); async function update(provider5, payload) { const options = validate(schema7, payload); if (!provider5.update) { throw new Error(`Provider does not support updating`); } return await provider5.update(options); } // src/core/database/destroy.ts var schema8 = vWithExtras.object({ where: vWithExtras.optional(schema2), limit: vWithExtras.optional(vWithExtras.number()) }); async function destroy(provider5, payload) { if (!provider5.destroy) { throw new Error(`Provider does not support destroying`); } const options = validate(schema8, payload); return await provider5.destroy(options); } // src/core/provider/defineProvider.ts function defineProvider(payload) { return payload; } // 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) }); var operatorMap = { eq: "$eq", ne: "$ne", gt: "$gt", gte: "$gte", lt: "$lt", lte: "$lte", in: "$in", exists: "$exists" }; function parseCondition2(condition) { const { field, operator, value } = condition; if (!field || !operator) { console.error("Invalid condition:", condition); return {}; } const siftOperator = operatorMap[operator]; if (!siftOperator) { throw new Error(`Unsupported operator: ${operator}`); } return { [field]: { [siftOperator]: value } }; } function parseGroup(group) { const { and, or, ...rest } = group; const parsed = {}; if ((rest == null ? void 0 : rest.field) && (rest == null ? void 0 : rest.operator)) { return parseCondition2(rest); } if (and == null ? void 0 : and.length) { parsed.$and = and.map(parseGroup); } if (or == null ? void 0 : or.length) { parsed.$or = or.map(parseGroup); } return parsed; } function parseWhere(payload) { var _a, _b; if (!payload) return {}; const transformed = transformWhere(payload); const parsed = parseGroup(transformed); if (!((_a = parsed.$and) == null ? void 0 : _a.length)) { delete parsed.$and; } if (!((_b = parsed.$or) == null ? void 0 : _b.length)) { delete parsed.$or; } return parsed; } function query(data, options) { var _a; const { where, include, exclude } = options; const limit = options.limit; const offset = options.offset || 0; const siftQuery = parseWhere(where); let items = data.filter(sift__default.default(siftQuery)); if (include == null ? void 0 : include.length) { items = items.map((item) => lodashEs.pick(item, options.include)); } if ((exclude == null ? void 0 : exclude.length) && !(include == null ? void 0 : include.length)) { items = items.map((item) => lodashEs.omit(item, exclude)); } if ((_a = options.sortBy) == null ? void 0 : _a.length) { const sort = options.sortBy.map((f, i) => ({ field: f, order: options.sortDesc && options.sortDesc[i] ? "desc" : "asc" })); items = lodashEs.orderBy( items, sort.map((s) => s.field), sort.map((s) => s.order) ); } items = items.slice(offset, limit ? offset + limit : void 0); return items; } function count(data, options) { const items = query(data, { where: options.where }); return items.length; } // src/providers/file/list.ts async function list2(payload) { const { filesystem: filesystem2, root, options, parser } = payload; const resolve5 = (...args) => filesystem2.path.resolve(root, ...args); const where = (options == null ? void 0 : options.where) || {}; const exclude = options == null ? void 0 : options.exclude; const include = options == null ? void 0 : options.include; const limit = options == null ? void 0 : options.limit; const page = (options == null ? void 0 : options.page) || 1; const sortBy = options == null ? void 0 : options.sortBy; const sortDesc = options == null ? void 0 : options.sortDesc; const files = filesystem2.readdirSync(root).filter((f) => ![".db"].includes(f)); const result = []; for (const file of files) { const filename = resolve5(file); const content = filesystem2.readSync.text(filename); const item = { id: file.replace(`.${parser.ext}`, ""), filename, raw: content }; Object.assign(item, parser.parse(content)); result.push(item); } const items = query(result, { where, exclude, include, limit, offset: page > 1 ? (page - 1) * limit : 0, sortBy, sortDesc }); const meta = { total: count(result, { where }), limit, total_pages: limit ? Math.ceil(result.length / limit) : 1 }; return { meta, data: items }; } // src/providers/file/update.ts async function update2(payload) { const { filesystem: filesystem2, root, options, parser } = payload; const resolve5 = (...args) => filesystem2.path.resolve(root, ...args); const data = options.data; const { data: items } = await list2({ filesystem: filesystem2, root, parser, options: { where: options.where, limit: options.limit } }); const hideKeys = ["id", "folder", "raw", "lock"]; for (const item of items) { const filename = resolve5(`${item.id}.${parser.ext}`); const properties = lodashEs.omit({ ...item, ...data }, hideKeys); const content = parser.stringify(properties); filesystem2.writeSync.text(filename, content); } return { count: items.length }; } function createFsNode() { const exists = async (path4) => { const [, error] = await tryCatch(() => fs3__default.default.promises.access(path4)); return error ? false : true; }; const existsSync = (path4) => { const [, error] = tryCatch.sync(() => fs3__default.default.accessSync(path4)); return error ? false : true; }; const read = async (path4) => { const [content, error] = await tryCatch(() => fs3__default.default.promises.readFile(path4)); if (error) { return null; } return new Uint8Array(content); }; const readSync = (path4) => { const [content, error] = tryCatch.sync(() => fs3__default.default.readFileSync(path4)); if (error) { return null; } return new Uint8Array(content); }; const readdir = async (path4) => { const [files, error] = await tryCatch(() => fs3__default.default.promises.readdir(path4)); return error ? [] : files; }; const readdirSync = (path4) => { const [files, error] = tryCatch.sync(() => fs3__default.default.readdirSync(path4)); return error ? [] : files; }; const write = async (path4, content) => { const [, error] = await tryCatch(() => fs3__default.default.promises.writeFile(path4, content)); if (error) { throw error; } }; const writeSync = (path4, content) => { const [, error] = tryCatch.sync(() => fs3__default.default.writeFileSync(path4, content)); if (error) { throw error; } }; const mkdir2 = async (path4) => { const [, error] = await tryCatch(() => fs3__default.default.promises.mkdir(path4)); if (error) { throw error; } }; const mkdirSync = (path4) => { const [, error] = tryCatch.sync(() => fs3__default.default.mkdirSync(path4)); if (error) { throw error; } }; const remove = async (path4) => { const [, error] = await tryCatch(() => fs3__default.default.promises.rm(path4, { recursive: true })); if (error) { throw error; } }; const removeSync = (path4) => { const [, error] = tryCatch.sync(() => fs3__default.default.rmSync(path4, { recursive: true })); if (error) { throw error; } }; const removeAt = async (path4, milliseconds) => { if (os__default.default.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 = path.join(os__default.default.tmpdir(), `db-delete-file-${key}.vbs`); fs3__default.default.writeFileSync(tempScriptPath, script); const child = cp__default.default.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 }; } // src/core/filesystem/createFilesystem.ts function createFilesystem(options = {}) { const fs4 = options.fs || createFsNode(); const path4 = options.path || createPathNode(); const locks2 = /* @__PURE__ */ new Set(); function awaitLock2(filepath, timeout = 1e3) { return new Promise((resolve5, reject) => { const interval = setInterval(() => { if (!locks2.has(filepath)) { clearInterval(interval); resolve5(); } }, 100); setTimeout(() => { clearInterval(interval); reject(new Error("Timeout")); }, timeout); }); } async function read(filepath) { return fs4.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 [json2, error] = await tryCatch(() => JSON.parse(content, options2 == null ? void 0 : options2.reviver)); if (options2 == null ? void 0 : options2.schema) { return validate(options2.schema, json2); } return error ? (options2 == null ? void 0 : options2.default) || null : json2; }; 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 fs4.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 [json2, error] = tryCatch.sync(() => JSON.parse(content, options2 == null ? void 0 : options2.reviver)); if (options2 == null ? void 0 : options2.schema) { return validate(options2.schema, json2); } return error ? (options2 == null ? void 0 : options2.default) || null : json2; }; 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 fs4.readdir(filepath); } function readdirSync(filepath) { return fs4.readdirSync(filepath); } async function mkdir2(filepath, options2) { if (await fs4.exists(filepath)) return; if (options2 == null ? void 0 : options2.recursive) { const parent = path4.dirname(filepath); await mkdir2(parent, options2); } await fs4.mkdir(filepath); } function mkdirSync(filepath, options2) { if (fs4.existsSync(filepath)) return; if (options2 == null ? void 0 : options2.recursive) { const parent = path4.dirname(filepath); mkdirSync(parent, options2); } fs4.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(() => fs4.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 }); } fs4.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 fs4.remove(filepath); } function removeSync(filepath) { return fs4.removeSync(filepath); } function removeAt(filepath, miliseconds) { return fs4.removeAt(filepath, miliseconds); } return { path: path4, fs: fs4, exists: fs4.exists, existsSync: fs4.existsSync, read, readSync, readdir, readdirSync, write, writeSync, mkdir: mkdir2, mkdirSync, remove, removeSync, removeAt }; } // src/providers/file/create.ts async function create2(payload) { const { filesystem: filesystem2, root, options, makeId, parser } = payload; const resolve5 = (...args) => filesystem2.path.resolve(root, ...args); const data = options.data; const id = data.id || await makeId(); const filename = resolve5(`${id}.${parser.ext}`); if (filesystem2.existsSync(filename)) { throw new Error(`Item with id "${id}" already exists`); } const raw = parser.stringify(data); filesystem2.writeSync.text(filename, raw); const item = { id, raw, filename, ...data }; return item; } // src/providers/file/find.ts async function find2(payload) { const { filesystem: filesystem2, options, root, parser } = payload; const { data: items } = await list2({ filesystem: filesystem2, root, parser, options: { ...options, limit: 1 } }); return items[0] || null; } // src/providers/file/destroy.ts async function destroy2(payload) { const { filesystem: filesystem2, root, parser, options } = payload; const { data: items } = await list2({ filesystem: filesystem2, root, parser, options: { where: options.where, limit: options.limit } }); for (const item of items) { filesystem2.removeSync(item.filename); } return { count: items.length }; } function createDate() { return { name: "date", async create(config2) { const pattern = config2.pattern || "yyyy-MM-dd"; return dateFns.format(/* @__PURE__ */ new Date(), pattern); } }; } // src/core/idStrategy/createIncremental.ts function pad(value, size) { return value.toString().padStart(size, "0"); } function createIncremental(filesystem2, filename) { async function create5(payload = {}) { const options = validate( (v3) => v3.object({ padding: v3.optional(v3.number(), 2) }), payload ); if (!filesystem2.existsSync(filename)) { filesystem2.writeSync.json(filename, { last_id: 0 }, { recursive: true }); } const content = filesystem2.readSync.json(filename, { schema: (v3) => v3.object({ last_id: v3.number() }) }); const id = content.last_id + 1; filesystem2.writeSync.json(filename, { last_id: id }); return pad(id, options.padding); } return { name: "incremental", create: create5 }; } function createUuid() { return { name: "uuid", async create() { return crypto2__default.default.randomUUID(); } }; } // src/core/idStrategy/createStrategies.ts function createStrategies(options) { const { filesystem: filesystem2, root } = options; return [ createIncremental(filesystem2, filesystem2.path.resolve(root, ".db", "incremental.json")), createDate(), createUuid() ]; } var provider = defineProvider((config2, instanceOptions) => { const filesystem2 = createFilesystem({ fs: instanceOptions.fs, path: instanceOptions.path }); const schema12 = v2.object({ path: v2.extras.path(instanceOptions.root, filesystem2.path), format: v2.optional(v2.picklist(["markdown", "json", "yaml"]), "markdown"), id_strategy: v2.optional( v2.object({ name: v2.string(), options: v2.optional(v2.any()) }), { name: "incremental" } ) }); const { path: path4, format: format2, id_strategy } = validate(schema12, config2); const root = path4; const strategies = createStrategies({ filesystem: filesystem2, root }); const parser = parsers.find((p) => p.name === format2); const strategy = strategies.find((s) => s.name === id_strategy.name); if (!parser) { throw new Error(`Parser for format "${format2}" not found`); } if (!strategy) { throw new Error(`Strategy for id "${id_strategy}" not found`); } const makeId = () => strategy.create(id_strategy.options); async function open(payload) { const options = validate( (v3) => v3.object({ where: v3.optional(schema2), editor: v3.optional(v3.string()) }), payload ); const item = await find2({ root, filesystem: filesystem2, parser, options }); if (!item) { throw new Error("Item not found"); } let bin; if (process.env.EDITOR) { bin = process.env.EDITOR; } if (payload.editor) { bin = payload.editor; } if (!bin) { throw new Error( "No editor found, please set the EDITOR environment variable or pass the editor option" ); } cp__default.default.spawn(bin, [item.filename], { stdio: "inherit", env: process.env }); } return { open, list: (options = {}) => list2({ root, filesystem: filesystem2, parser, options }), find: (options) => find2({ root, filesystem: filesystem2, parser, options }), create: (options) => create2({ root, filesystem: filesystem2, parser, makeId, options }), update: (options) => update2({ root, filesystem: filesystem2, options, parser }), destroy: (options) => destroy2({ root, filesystem: filesystem2, parser, options }) }; }); // src/providers/folder/list.ts async function list3(payload) { const { filesystem: filesystem2, root, options, parser } = payload; const resolve5 = (...args) => filesystem2.path.resolve(root, ...args); const where = (options == null ? void 0 : options.where) || {}; const exclude = (options == null ? void 0 : options.exclude) || []; const include = (options == null ? void 0 : options.include) || []; const limit = options == null ? void 0 : options.limit; const page = (options == null ? void 0 : options.page) || 1; const sortBy = options == null ? void 0 : options.sortBy; const sortDesc = options == null ? void 0 : options.sortDesc; let files = filesystem2.readdirSync(root); const excludeDirs = [".db"]; files = files.filter((file) => !excludeDirs.includes(file)); const result = []; for (const id of files) { const filename = resolve5(id, `index.${parser.ext}`); const folder = resolve5(id); if (!filesystem2.existsSync(filename)) { throw new Error(`File "${filename}" not found`); } const raw = filesystem2.readSync.text(resolve5(id, `index.${parser.ext}`)); const item = { id, folder, raw }; Object.assign(item, parser.parse(raw)); result.push(item); } const items = query(result, { where, exclude, include, limit, offset: page > 1 ? (page - 1) * limit : 0, sortBy, sortDesc }); const meta = { total: count(result, { where }), limit, total_pages: limit ? Math.ceil(result.length / limit) : 1 }; return { meta, data: items }; } // src/providers/folder/update.ts async function update3(payload) { const { filesystem: filesystem2, root, options, parser } = payload; const resolve5 = (...args) => filesystem2.path.resolve(root, ...args); const data = options.data; const { data: items } = await list3({ filesystem: filesystem2, root, parser, options: { where: options.where, limit: options.limit } }); const hideKeys = ["id", "folder", "raw", "lock"]; for (const item of items) { const filename = resolve5(item.id, `index.${parser.ext}`); const properties = lodashEs.omit({ ...item, ...data }, hideKeys); const content = parser.stringify(properties); filesystem2.writeSync.text(filename, content); } return { count: items.length }; } // src/providers/folder/create.ts async function create3(payload) { const { filesystem: filesystem2, root, options, makeId, parser } = payload; const resolve5 = (...args) => filesystem2.path.resolve(root, ...args); const data = options.data; const id = data.id || await makeId(); const folder = resolve5(id); const filename = resolve5(id, `index.${parser.ext}`); if (filesystem2.existsSync(folder)) { throw new Error(`Item with id "${id}" already exists`); } const raw = parser.stringify(data); filesystem2.mkdirSync(resolve5(id)); filesystem2.writeSync.text(filename, raw); const item = { id, raw, folder, ...data }; return item; } // src/providers/folder/find.ts async function find3(payload) { const { filesystem: filesystem2, options, root, parser } = payload; const { data: items } = await list3({ filesystem: filesystem2, root, parser, options: { ...options, limit: 1 } }); return items[0] || null; } // src/providers/folder/destroy.ts async function destroy3(payload) { const { filesystem: filesystem2, root, parser, options } = payload; const { data: items } = await list3({ filesystem: filesystem2, root, parser, options: { where: options.where, limit: options.limit } }); for (const item of items) { filesystem2.removeSync(item.folder); } return { count: items.length }; } var provider2 = defineProvider((config2, instanceConfig) => { const filesystem2 = createFilesystem({ fs: instanceConfig.fs, path: instanceConfig.path }); const schema12 = v2.object({ path: v2.extras.path(instanceConfig.root, filesystem2.path), format: v2.optional(v2.picklist(["markdown", "json", "yaml"]), "markdown"), id_strategy: v2.optional( v2.object({ name: v2.string(), options: v2.optional(v2.any()) }), { name: "incremental" } ) }); const { path: path4, format: format2, id_strategy } = validate(schema12, config2); const root = path4; const strategies = createStrategies({ filesystem: filesystem2, root }); const parser = parsers.find((p) => p.name === format2); const strategy = strategies.find((s) => s.name === id_strategy.name); if (!parser) { throw new Error(`Parser for format "${format2}" not found`); } if (!strategy) { throw new Error(`Strategy for id "${id_strategy}" not found`); } const makeId = () => strategy.create(id_strategy.options); async function open(payload) { const options = validate( (v3) => v3.object({ where: v3.optional(schema2), editor: v3.optional(v3.string()) }), payload ); const item = await find3({ root, filesystem: filesystem2, parser, options }); if (!item) { throw new Error("Item not found"); } let bin; if (process.env.EDITOR) { bin = process.env.EDITOR; } if (payload.editor) { bin = payload.editor; } if (!bin) { throw new Error( "No editor found, please set the EDITOR environment variable or pass the editor option" ); } const filename = filesystem2.path.join(item.folder, `index.${parser.ext}`); cp__default.default.spawn(bin, [filename], { stdio: "inherit" }); } return { open, list: (options = {}) => list3({ root, filesystem: filesystem2, parser, options }), find: (options) => find3({ root, filesystem: filesystem2, parser, options }), create: (options) => create3({ root, filesystem: filesystem2, parser, makeId, options }), update: (options) => update3({ root, filesystem: filesystem2, options, parser }), destroy: (options) => destroy3({ filesystem: filesystem2, root, parser, options }) }; }); function getOne(value, keys) { for (const key of keys) { if (lodashEs.has(value, key)) { return lodashEs.get(value, key); } } } function toDataItem(notionObject) { const result = {}; const entries = Object.entries(notionObject.properties); for (const [key, value] of entries) { if (value.id === "title") { result[key] = value.title.map((t) => t.plain_text).join(""); continue; } if (value.type === "status") { result[key] = lodashEs.get(value, "status.name"); continue; } if (value.type === "rich_text") { const text = value.rich_text.map((t) => t.plain_text).join(""); result[key] = text; continue; } if (value.type === "number") { result[key] = value.number; continue; } if (value.type === "formula") { result[key] = getOne(value.formula, ["number", "string"]); continue; } if (value.type === "select") { result[key] = lodashEs.get(value, "select.name"); continue; } if (value.type === "multi_select") { result[key] = value.multi_select.map((s) => s == null ? void 0 : s.name); continue; } if (value.type === "url") { result[key] = value.url; continue; } if (value.type === "files") { result[key] = value.files; continue; } if (value.type === "last_edited_time") { result[key] = value.last_edited_time; continue; } if (value.type === "created_time") { result[key] = value.created_time; continue; } } return result; } function toNotionObject(itemData, properties) { const result = { properties: {} }; for (const [key, value] of Object.entries(itemData)) { const property = properties[key]; if (!property) continue; if (property.type === "title") { result.properties[key] = { title: [ { type: "text", text: { content: value } } ] }; continue; } if (property.type === "rich_text") { result.properties[key] = { rich_text: [ { type: "text", text: { content: String(value) } } ] }; continue; } if (property.type === "status") { const options = lodashEs.get(property, "status.options", []); const status = options.find((o) => { if (o.name === value) return o; if (o.id === value) return o; }); if (!status) continue; result.properties[key] = { status }; continue; } if (property.type === "multi_select") { const options = lodashEs.get(property, "multi_select.options", []); const multiSelect = value.map((v3) => { const option = options.find((o) => { if (o.name === v3) return o; if (o.id === v3) return o; }); return option; }); result.properties[key] = { multi_select: multiSelect }; continue; } if (property.type === "number") { result.properties[key] = { number: Number(value) }; continue; } if (property.type === "formula") { result.properties[key] = { formula: value }; continue; } if (property.type === "select") { result.properties[key] = { select: { name: value } }; continue; } if (property.type === "multi_select") { result.properties[key] = { multi_select: value.map((s) => ({ name: s })) }; continue; } if (property.type === "url") { result.properties[key] = { url: value }; continue; } if (property.type === "files") { result.properties[key] = { files: value }; continue; } if (property.type === "last_edited_time") { result.properties[key] = { last_edited_time: value }; continue; } if (property.type === "created_time") { result.properties[key] = { created_time: value }; continue; } if (property.type === "date") { result.properties[key] = { date: value }; continue; } } return result; } // src/providers/notion/parseWhere.ts var dateOperatorsMap = { eq: "equals", ne: "does_not_equal", gt: "after", gte: "on_or_after", lt: "before", lte: "on_or_before" }; function parseWhere2(where, properties) { const { and, or, ...rest } = where; const property = properties[rest.field]; const result = { and: [], or: [] }; if (rest.operator === "in") { const or2 = rest.value.map((v3) => ({ property: rest.field, [property.type]: { equals: v3 } })); return { or: or2 }; } if (rest.operator === "exists") { return { property: rest.field, [property.type]: { is_not_empty: rest.value ? true : void 0, is_empty: rest.value ? void 0 : true } }; } if ((property == null ? void 0 : property.type) === "formula") { return { property: rest.field, [property.type]: { string: { contains: rest.value } } }; } if (property && (rest == null ? void 0 : rest.field) && (rest == null ? void 0 : rest.operator)) { const notionOperator = dateOperatorsMap[rest.operator] || "equals"; return { property: rest.field, [property.type]: { [notionOperator]: rest.value } }; } if (and == null ? void 0 : and.length) { and.forEach((w) => { result.and.push(parseWhere2(w, properties)); }); } if (or == null ? void 0 : or.length) { or.forEach((w) => { result.or.push(parseWhere2(w, properties)); }); } if (result.and.length === 1 && !result.or.length) { return result.and[0]; } if (!result.or.length) { delete result.or; } if (!result.and.length) { delete result.and; } return result; } // src/providers/notion/provider.ts var provider3 = defineProvider((config2) => { const schema12 = v2.object({ secret_token: v2.string(), database_id: v2.string() }); const { secret_token, database_id } = validate(schema12, config2); const api = async (path4, options = {}) => { const defaultOptions = { headers: { "Authorization": `Bearer ${secret_token}`, "Notion-Version": "2022-06-28", "Content-Type": "application/json" } }; const [response, error] = await tryCatch( async () => fetch(`https://api.notion.com/v1/${path4}`, lodashEs.merge(defaultOptions, options)) ); if (error) { throw error; } const body = await response.json(); if (!response.ok) { console.error(body); throw new Error("Notion API error"); } if (body.object === "error") { console.error(body); throw new Error("Notion API error"); } return body; }; async function findProperties() { const response = await api(`databases/${database_id}`); return response.properties; } const list5 = async (options) => { const where = options == null ? void 0 : options.where; const exclude = options == null ? void 0 : options.exclude; const include = options == null ? void 0 : options.include; const limit = (options == null ? void 0 : options.limit) || 100; const cursor = options == null ? void 0 : options.cursor; const properties = await findProperties(); const body = { page_size: limit, start_cursor: cursor }; if (where && Object.keys(where).length) { body.filter = parseWhere2(where, properties); } const { results, has_more, next_cursor, request_id } = await api( `databases/${database_id}/query`, { method: "POST", body: JSON.stringify(body) } ); let items = []; for (const notionObject of results) { const item = toDataItem(notionObject); item._raw = notionObject; item._id = notionObject.id; items.push(item); } if (include == null ? void 0 : include.length) { items = items.map((item) => pick2__default.default(item, include)); } if ((exclude == null ? void 0 : exclude.length) && !(include == null ? void 0 : include.length)) { items = items.map((item) => omit4__default.default(item, exclude)); } if (items.length && !include && !exclude) { const keys = Object.keys(items[0]).filter((k) => k !== "_id" && k.startsWith("_")); items = items.map((item) => omit4__default.default(item, keys)); } return { meta: { has_more, next_cursor, request_id }, data: items }; }; const find5 = async (options) => { const { data: items } = await list5({ ...options, limit: 1 }); return items[0] || null; }; const create5 = async (options) => { const { data } = options; const properties = await findProperties(); const notionObject = toNotionObject(data, properties); notionObject.parent = { database_id }; const response = await api(`pages`, { method: "POST", body: JSON.stringify(notionObject) }); return toDataItem(response); }; const update5 = async (options) => { const { data, where } = options; const page = await list5({ where, exclude: [] }); const items = page.data; const properties = await findProperties(); let count2 = 0; for await (const item of items) { count2++; const notionObject = toNotionObject(data, properties); await api(`pages/${item._id}`, { method: "PATCH", body: JSON.stringify(notionObject) }); } return { count: count2 }; }; const destroy5 = async (options) => { const { where } = options; const { data } = await list5({ where, exclude: [] }); let count2 = 0; for await (const item of data) { count2++; await api(`pages/${item._id}`, { method: "PATCH", body: JSON.stringify({ archived: true }) }); } return { count: count2 }; }; return { list: list5, find: find5, create: create5, update: update5, destroy: destroy5, findProperties }; }); // src/providers/vault/config.ts var schema9 = (dirname5, path4 = createPathNode()) => v2.object({ format: v2.optional(v2.string(), "markdown"), path: v2.extras.path(dirname5, path4), id_strategy: v2.optional( v2.object({ name: v2.string(), options: v2.optional(v2.any()) }), { name: "incremental" } ) }); var schema10 = v2.object({ value: v2.union([v2.string(), v2.extras.uint8()]), salt: v2.optional(v2.string(), crypto2__default.default.randomBytes(16).toString("hex")), iv: v2.optional(v2.string(), crypto2__default.default.randomBytes(16).toString("hex")), password: v2.string() }); function encrypt(payload) { const options = validate(schema10, payload); const salt = Buffer.from(options.salt, "hex"); const iv = Buffer.from(options.iv, "hex"); const key = crypto2__default.default.scryptSync(options.password, salt, 32); const cipher = crypto2__default.default.createCipheriv("aes-256-ctr", key, iv); if (options.value instanceof Uint8Array) { c