From 634be9b01ddc8d84cc5a37b63e138d1ef34f7b57 Mon Sep 17 00:00:00 2001 From: Benjamin Toby Date: Mon, 2 Mar 2026 10:44:30 +0100 Subject: [PATCH] Updates --- .gitignore | 1 - .npmrc | 1 + dist/commands/index.d.ts | 6 + dist/commands/index.js | 27 + dist/commands/schema.d.ts | 2 + dist/commands/schema.js | 37 + dist/commands/typedef.d.ts | 2 + dist/commands/typedef.js | 28 + dist/data/app-data.d.ts | 3 + dist/data/app-data.js | 3 + dist/data/grab-dir-names.d.ts | 3 + dist/data/grab-dir-names.js | 7 + dist/functions/init.d.ts | 2 + dist/functions/init.js | 49 + dist/index.d.ts | 13 + dist/index.js | 13 + dist/lib/sqlite/db-delete.d.ts | 16 + dist/lib/sqlite/db-delete.js | 49 + dist/lib/sqlite/db-generate-type-defs.d.ts | 15 + dist/lib/sqlite/db-generate-type-defs.js | 61 + dist/lib/sqlite/db-insert.d.ts | 15 + dist/lib/sqlite/db-insert.js | 32 + dist/lib/sqlite/db-schema-manager.d.ts | 72 ++ dist/lib/sqlite/db-schema-manager.js | 456 ++++++++ dist/lib/sqlite/db-schema-to-typedef.d.ts | 6 + dist/lib/sqlite/db-schema-to-typedef.js | 44 + dist/lib/sqlite/db-select.d.ts | 17 + dist/lib/sqlite/db-select.js | 48 + dist/lib/sqlite/db-sql.d.ts | 11 + dist/lib/sqlite/db-sql.js | 33 + dist/lib/sqlite/db-update.d.ts | 17 + dist/lib/sqlite/db-update.js | 68 ++ dist/lib/sqlite/index.d.ts | 3 + dist/lib/sqlite/index.js | 17 + dist/lib/sqlite/schema-to-typedef.d.ts | 7 + dist/lib/sqlite/schema-to-typedef.js | 18 + dist/lib/sqlite/schema.d.ts | 2 + dist/lib/sqlite/schema.js | 5 + dist/types/index.d.ts | 1014 +++++++++++++++++ dist/types/index.js | 123 ++ .../append-default-fields-to-db-schema.d.ts | 6 + .../append-default-fields-to-db-schema.js | 12 + dist/utils/sql-equality-parser.d.ts | 2 + dist/utils/sql-equality-parser.js | 39 + dist/utils/sql-gen-operator-gen.d.ts | 20 + dist/utils/sql-gen-operator-gen.js | 127 +++ dist/utils/sql-generator.d.ts | 25 + dist/utils/sql-generator.js | 392 +++++++ dist/utils/sql-insert-generator.d.ts | 5 + dist/utils/sql-insert-generator.js | 56 + package.json | 1 + tsconfig.json | 5 +- 52 files changed, 3033 insertions(+), 3 deletions(-) create mode 100644 dist/commands/index.d.ts create mode 100644 dist/commands/index.js create mode 100644 dist/commands/schema.d.ts create mode 100644 dist/commands/schema.js create mode 100644 dist/commands/typedef.d.ts create mode 100644 dist/commands/typedef.js create mode 100644 dist/data/app-data.d.ts create mode 100644 dist/data/app-data.js create mode 100644 dist/data/grab-dir-names.d.ts create mode 100644 dist/data/grab-dir-names.js create mode 100644 dist/functions/init.d.ts create mode 100644 dist/functions/init.js create mode 100644 dist/index.d.ts create mode 100644 dist/index.js create mode 100644 dist/lib/sqlite/db-delete.d.ts create mode 100644 dist/lib/sqlite/db-delete.js create mode 100644 dist/lib/sqlite/db-generate-type-defs.d.ts create mode 100644 dist/lib/sqlite/db-generate-type-defs.js create mode 100644 dist/lib/sqlite/db-insert.d.ts create mode 100644 dist/lib/sqlite/db-insert.js create mode 100644 dist/lib/sqlite/db-schema-manager.d.ts create mode 100644 dist/lib/sqlite/db-schema-manager.js create mode 100644 dist/lib/sqlite/db-schema-to-typedef.d.ts create mode 100644 dist/lib/sqlite/db-schema-to-typedef.js create mode 100644 dist/lib/sqlite/db-select.d.ts create mode 100644 dist/lib/sqlite/db-select.js create mode 100644 dist/lib/sqlite/db-sql.d.ts create mode 100644 dist/lib/sqlite/db-sql.js create mode 100644 dist/lib/sqlite/db-update.d.ts create mode 100644 dist/lib/sqlite/db-update.js create mode 100644 dist/lib/sqlite/index.d.ts create mode 100644 dist/lib/sqlite/index.js create mode 100644 dist/lib/sqlite/schema-to-typedef.d.ts create mode 100644 dist/lib/sqlite/schema-to-typedef.js create mode 100644 dist/lib/sqlite/schema.d.ts create mode 100644 dist/lib/sqlite/schema.js create mode 100644 dist/types/index.d.ts create mode 100644 dist/types/index.js create mode 100644 dist/utils/append-default-fields-to-db-schema.d.ts create mode 100644 dist/utils/append-default-fields-to-db-schema.js create mode 100644 dist/utils/sql-equality-parser.d.ts create mode 100644 dist/utils/sql-equality-parser.js create mode 100644 dist/utils/sql-gen-operator-gen.d.ts create mode 100644 dist/utils/sql-gen-operator-gen.js create mode 100644 dist/utils/sql-generator.d.ts create mode 100644 dist/utils/sql-generator.js create mode 100644 dist/utils/sql-insert-generator.d.ts create mode 100644 dist/utils/sql-insert-generator.js diff --git a/.gitignore b/.gitignore index 15da839..2bd9555 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ node_modules # output out -dist *.tgz # code coverage diff --git a/.npmrc b/.npmrc index 1b4726a..6bc2539 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ @moduletrace:registry=https://git.tben.me/api/packages/moduletrace/npm/ +//git.tben.me/api/packages/moduletrace/npm/:_authToken=${GITBEN_NPM_TOKEN} diff --git a/dist/commands/index.d.ts b/dist/commands/index.d.ts new file mode 100644 index 0000000..4cb206d --- /dev/null +++ b/dist/commands/index.d.ts @@ -0,0 +1,6 @@ +#!/usr/bin/env bun +/** + * # Declare Global Variables + */ +declare global { } +export {}; diff --git a/dist/commands/index.js b/dist/commands/index.js new file mode 100644 index 0000000..5fe5e18 --- /dev/null +++ b/dist/commands/index.js @@ -0,0 +1,27 @@ +#!/usr/bin/env bun +import { program } from "commander"; +import schema from "./schema"; +import typedef from "./typedef"; +/** + * # Describe Program + */ +program + .name(`bun-sqlite`) + .description(`SQLite manager for Bun`) + .version(`1.0.0`); +/** + * # Declare Commands + */ +program.addCommand(schema()); +program.addCommand(typedef()); +/** + * # Handle Unavailable Commands + */ +program.on("command:*", () => { + console.error("Invalid command: %s\nSee --help for a list of available commands.", program.args.join(" ")); + process.exit(1); +}); +/** + * # Parse Arguments + */ +program.parse(Bun.argv); diff --git a/dist/commands/schema.d.ts b/dist/commands/schema.d.ts new file mode 100644 index 0000000..f202e7d --- /dev/null +++ b/dist/commands/schema.d.ts @@ -0,0 +1,2 @@ +import { Command } from "commander"; +export default function (): Command; diff --git a/dist/commands/schema.js b/dist/commands/schema.js new file mode 100644 index 0000000..377b758 --- /dev/null +++ b/dist/commands/schema.js @@ -0,0 +1,37 @@ +import { Command } from "commander"; +import { SQLiteSchemaManager } from "../lib/sqlite/db-schema-manager"; +import init from "../functions/init"; +import grabDirNames from "../data/grab-dir-names"; +import path from "path"; +import dbSchemaToTypeDef from "../lib/sqlite/schema-to-typedef"; +import _ from "lodash"; +import { DefaultFields } from "../types"; +import appendDefaultFieldsToDbSchema from "../utils/append-default-fields-to-db-schema"; +export default function () { + return new Command("schema") + .description("Build DB From Schema") + .option("-v, --vector", "Recreate Vector Tables. This will drop and rebuild all vector tables") + .option("-t, --typedef", "Generate typescript type definitions") + .action(async (opts) => { + console.log(`Starting process ...`); + const { config, dbSchema } = await init(); + const { ROOT_DIR } = grabDirNames(); + const isVector = Boolean(opts.vector || opts.v); + const isTypeDef = Boolean(opts.typedef || opts.t); + const finaldbSchema = appendDefaultFieldsToDbSchema({ dbSchema }); + const manager = new SQLiteSchemaManager({ + schema: finaldbSchema, + recreate_vector_table: isVector, + }); + await manager.syncSchema(); + manager.close(); + if (isTypeDef && config.typedef_file_path) { + const out_file = path.resolve(ROOT_DIR, config.typedef_file_path); + dbSchemaToTypeDef({ + dbSchema: finaldbSchema, + dst_file: out_file, + }); + } + process.exit(); + }); +} diff --git a/dist/commands/typedef.d.ts b/dist/commands/typedef.d.ts new file mode 100644 index 0000000..f202e7d --- /dev/null +++ b/dist/commands/typedef.d.ts @@ -0,0 +1,2 @@ +import { Command } from "commander"; +export default function (): Command; diff --git a/dist/commands/typedef.js b/dist/commands/typedef.js new file mode 100644 index 0000000..810b2c5 --- /dev/null +++ b/dist/commands/typedef.js @@ -0,0 +1,28 @@ +import { Command } from "commander"; +import init from "../functions/init"; +import dbSchemaToTypeDef from "../lib/sqlite/schema-to-typedef"; +import path from "path"; +import grabDirNames from "../data/grab-dir-names"; +import appendDefaultFieldsToDbSchema from "../utils/append-default-fields-to-db-schema"; +export default function () { + return new Command("typedef") + .description("Build DB From Schema") + .action(async (opts) => { + console.log(`Creating Type Definition From DB Schema ...`); + const { config, dbSchema } = await init(); + const { ROOT_DIR } = grabDirNames(); + const finaldbSchema = appendDefaultFieldsToDbSchema({ dbSchema }); + if (config.typedef_file_path) { + const out_file = path.resolve(ROOT_DIR, config.typedef_file_path); + dbSchemaToTypeDef({ + dbSchema: finaldbSchema, + dst_file: out_file, + }); + } + else { + console.error(``); + process.exit(1); + } + process.exit(); + }); +} diff --git a/dist/data/app-data.d.ts b/dist/data/app-data.d.ts new file mode 100644 index 0000000..09e73e6 --- /dev/null +++ b/dist/data/app-data.d.ts @@ -0,0 +1,3 @@ +export declare const AppData: { + readonly ConfigFileName: "bun-sqlite.config.ts"; +}; diff --git a/dist/data/app-data.js b/dist/data/app-data.js new file mode 100644 index 0000000..cb3d74e --- /dev/null +++ b/dist/data/app-data.js @@ -0,0 +1,3 @@ +export const AppData = { + ConfigFileName: "bun-sqlite.config.ts", +}; diff --git a/dist/data/grab-dir-names.d.ts b/dist/data/grab-dir-names.d.ts new file mode 100644 index 0000000..54d9b88 --- /dev/null +++ b/dist/data/grab-dir-names.d.ts @@ -0,0 +1,3 @@ +export default function grabDirNames(): { + ROOT_DIR: string; +}; diff --git a/dist/data/grab-dir-names.js b/dist/data/grab-dir-names.js new file mode 100644 index 0000000..e92b2dc --- /dev/null +++ b/dist/data/grab-dir-names.js @@ -0,0 +1,7 @@ +import path from "path"; +export default function grabDirNames() { + const ROOT_DIR = process.cwd(); + return { + ROOT_DIR, + }; +} diff --git a/dist/functions/init.d.ts b/dist/functions/init.d.ts new file mode 100644 index 0000000..c7c6d0b --- /dev/null +++ b/dist/functions/init.d.ts @@ -0,0 +1,2 @@ +import type { BunSQLiteConfigReturn } from "../types"; +export default function init(): Promise; diff --git a/dist/functions/init.js b/dist/functions/init.js new file mode 100644 index 0000000..ea6dedd --- /dev/null +++ b/dist/functions/init.js @@ -0,0 +1,49 @@ +import path from "path"; +import fs from "fs"; +import { AppData } from "../data/app-data"; +import grabDirNames from "../data/grab-dir-names"; +export default async function init() { + try { + const { ROOT_DIR } = grabDirNames(); + const { ConfigFileName } = AppData; + const ConfigFilePath = path.join(ROOT_DIR, ConfigFileName); + if (!fs.existsSync(ConfigFilePath)) { + console.log("ConfigFilePath", ConfigFilePath); + console.error(`Please create a \`${ConfigFileName}\` file at the root of your project.`); + process.exit(1); + } + const ConfigImport = await import(ConfigFilePath); + const Config = ConfigImport["default"]; + if (!Config.db_name) { + console.error(`\`db_name\` is required in your config`); + process.exit(1); + } + if (!Config.db_schema_file_name) { + console.error(`\`db_schema_file_name\` is required in your config`); + process.exit(1); + } + if (!Config.db_backup_dir) { + console.error(`\`db_backup_dir\` is required in your config`); + process.exit(1); + } + let db_dir = ROOT_DIR; + if (Config.db_dir) { + db_dir = path.resolve(ROOT_DIR, Config.db_dir); + if (!fs.existsSync(Config.db_dir)) { + fs.mkdirSync(Config.db_dir, { recursive: true }); + } + } + const DBSchemaFilePath = path.join(db_dir, Config.db_schema_file_name); + const DbSchemaImport = await import(DBSchemaFilePath); + const DbSchema = DbSchemaImport["default"]; + const BackupDir = path.resolve(db_dir, Config.db_backup_dir); + if (!fs.existsSync(BackupDir)) { + fs.mkdirSync(BackupDir, { recursive: true }); + } + return { config: Config, dbSchema: DbSchema }; + } + catch (error) { + console.error(`Initialization ERROR => ` + error.message); + process.exit(1); + } +} diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..778c289 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,13 @@ +import DbDelete from "./lib/sqlite/db-delete"; +import DbInsert from "./lib/sqlite/db-insert"; +import DbSelect from "./lib/sqlite/db-select"; +import DbSQL from "./lib/sqlite/db-sql"; +import DbUpdate from "./lib/sqlite/db-update"; +declare const BunSQLite: { + readonly select: typeof DbSelect; + readonly insert: typeof DbInsert; + readonly update: typeof DbUpdate; + readonly delete: typeof DbDelete; + readonly sql: typeof DbSQL; +}; +export default BunSQLite; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..3bff6fb --- /dev/null +++ b/dist/index.js @@ -0,0 +1,13 @@ +import DbDelete from "./lib/sqlite/db-delete"; +import DbInsert from "./lib/sqlite/db-insert"; +import DbSelect from "./lib/sqlite/db-select"; +import DbSQL from "./lib/sqlite/db-sql"; +import DbUpdate from "./lib/sqlite/db-update"; +const BunSQLite = { + select: DbSelect, + insert: DbInsert, + update: DbUpdate, + delete: DbDelete, + sql: DbSQL, +}; +export default BunSQLite; diff --git a/dist/lib/sqlite/db-delete.d.ts b/dist/lib/sqlite/db-delete.d.ts new file mode 100644 index 0000000..0c52179 --- /dev/null +++ b/dist/lib/sqlite/db-delete.d.ts @@ -0,0 +1,16 @@ +import type { APIResponseObject, ServerQueryParam } from "../../types"; +type Params = { + table: string; + query?: ServerQueryParam; + targetId?: number | string; +}; +export default function DbDelete({ table, query, targetId }: Params): Promise; +export {}; diff --git a/dist/lib/sqlite/db-delete.js b/dist/lib/sqlite/db-delete.js new file mode 100644 index 0000000..cddec14 --- /dev/null +++ b/dist/lib/sqlite/db-delete.js @@ -0,0 +1,49 @@ +import DbClient from "."; +import _ from "lodash"; +import sqlGenerator from "../../utils/sql-generator"; +export default async function DbDelete({ table, query, targetId }) { + try { + let finalQuery = query || {}; + if (targetId) { + finalQuery = _.merge(finalQuery, { + query: { + id: { + value: String(targetId), + }, + }, + }); + } + const sqlQueryObj = sqlGenerator({ + tableName: table, + genObject: finalQuery, + }); + const whereClause = sqlQueryObj.string.match(/WHERE .*/)?.[0]; + if (whereClause) { + let sql = `DELETE FROM ${table} ${whereClause}`; + const res = DbClient.run(sql, sqlQueryObj.values); + return { + success: Boolean(res.changes), + postInsertReturn: { + affectedRows: res.changes, + insertId: Number(res.lastInsertRowid), + }, + debug: { + sql, + values: sqlQueryObj.values, + }, + }; + } + else { + return { + success: false, + msg: `No WHERE clause`, + }; + } + } + catch (error) { + return { + success: false, + error: error.message, + }; + } +} diff --git a/dist/lib/sqlite/db-generate-type-defs.d.ts b/dist/lib/sqlite/db-generate-type-defs.d.ts new file mode 100644 index 0000000..e5f450a --- /dev/null +++ b/dist/lib/sqlite/db-generate-type-defs.d.ts @@ -0,0 +1,15 @@ +import type { BUN_SQLITE_TableSchemaType } from "../../types"; +type Param = { + paradigm: "JavaScript" | "TypeScript" | undefined; + table: BUN_SQLITE_TableSchemaType; + query?: any; + typeDefName?: string; + allValuesOptional?: boolean; + addExport?: boolean; + dbName?: string; +}; +export default function generateTypeDefinition({ paradigm, table, query, typeDefName, allValuesOptional, addExport, dbName, }: Param): { + typeDefinition: string | null; + tdName: string; +}; +export {}; diff --git a/dist/lib/sqlite/db-generate-type-defs.js b/dist/lib/sqlite/db-generate-type-defs.js new file mode 100644 index 0000000..1c45eea --- /dev/null +++ b/dist/lib/sqlite/db-generate-type-defs.js @@ -0,0 +1,61 @@ +export default function generateTypeDefinition({ paradigm, table, query, typeDefName, allValuesOptional, addExport, dbName, }) { + let typeDefinition = ``; + let tdName = ``; + try { + tdName = typeDefName + ? typeDefName + : dbName + ? `BUN_SQLITE_${dbName}_${table.tableName}`.toUpperCase() + : `BUN_SQLITE_${query.single}_${query.single_table}`.toUpperCase(); + const fields = table.fields; + function typeMap(schemaType) { + if (schemaType.options && schemaType.options.length > 0) { + return schemaType.options + .map((opt) => schemaType.dataType?.match(/int/i) || + typeof opt == "number" + ? `${opt}` + : `"${opt}"`) + .join(" | "); + } + if (schemaType.dataType?.match(/int|double|decimal/i)) { + return "number"; + } + if (schemaType.dataType?.match(/text|varchar|timestamp/i)) { + return "string"; + } + if (schemaType.dataType?.match(/boolean/i)) { + return "0 | 1"; + } + return "string"; + } + const typesArrayTypeScript = []; + const typesArrayJavascript = []; + typesArrayTypeScript.push(`${addExport ? "export " : ""}type ${tdName} = {`); + typesArrayJavascript.push(`/**\n * @typedef {object} ${tdName}`); + fields.forEach((field) => { + if (field.fieldDescription) { + typesArrayTypeScript.push(` /** \n * ${field.fieldDescription}\n */`); + } + const nullValue = allValuesOptional + ? "?" + : field.notNullValue + ? "" + : "?"; + typesArrayTypeScript.push(` ${field.fieldName}${nullValue}: ${typeMap(field)};`); + typesArrayJavascript.push(` * @property {${typeMap(field)}${nullValue}} ${field.fieldName}`); + }); + typesArrayTypeScript.push(`}`); + typesArrayJavascript.push(` */`); + if (paradigm?.match(/javascript/i)) { + typeDefinition = typesArrayJavascript.join("\n"); + } + if (paradigm?.match(/typescript/i)) { + typeDefinition = typesArrayTypeScript.join("\n"); + } + } + catch (error) { + console.log(error.message); + typeDefinition = null; + } + return { typeDefinition, tdName }; +} diff --git a/dist/lib/sqlite/db-insert.d.ts b/dist/lib/sqlite/db-insert.d.ts new file mode 100644 index 0000000..a33d834 --- /dev/null +++ b/dist/lib/sqlite/db-insert.d.ts @@ -0,0 +1,15 @@ +import type { APIResponseObject } from "../../types"; +type Params = { + table: string; + data: T[]; +}; +export default function DbInsert({ table, data }: Params): Promise; +export {}; diff --git a/dist/lib/sqlite/db-insert.js b/dist/lib/sqlite/db-insert.js new file mode 100644 index 0000000..df0b86d --- /dev/null +++ b/dist/lib/sqlite/db-insert.js @@ -0,0 +1,32 @@ +import DbClient from "."; +import sqlInsertGenerator from "../../utils/sql-insert-generator"; +export default async function DbInsert({ table, data }) { + try { + const finalData = data.map((d) => ({ + ...d, + created_at: Date.now(), + updated_at: Date.now(), + })); + const sqlObj = sqlInsertGenerator({ + tableName: table, + data: finalData, + }); + const res = DbClient.run(sqlObj?.query || "", sqlObj?.values || []); + return { + success: Boolean(Number(res.lastInsertRowid)), + postInsertReturn: { + affectedRows: res.changes, + insertId: Number(res.lastInsertRowid), + }, + debug: { + sqlObj, + }, + }; + } + catch (error) { + return { + success: false, + error: error.message, + }; + } +} diff --git a/dist/lib/sqlite/db-schema-manager.d.ts b/dist/lib/sqlite/db-schema-manager.d.ts new file mode 100644 index 0000000..7a6883a --- /dev/null +++ b/dist/lib/sqlite/db-schema-manager.d.ts @@ -0,0 +1,72 @@ +#!/usr/bin/env bun +import type { BUN_SQLITE_DatabaseSchemaType } from "../../types"; +declare class SQLiteSchemaManager { + private db; + private db_manager_table_name; + private recreate_vector_table; + private db_schema; + constructor({ schema, recreate_vector_table, }: { + schema: BUN_SQLITE_DatabaseSchemaType; + recreate_vector_table?: boolean; + }); + private createDbManagerTable; + private insertDbManagerTable; + private removeDbManagerTable; + /** + * Main synchronization method + */ + syncSchema(): Promise; + /** + * Get list of existing tables in the database + */ + private getExistingTables; + /** + * Drop tables that are no longer in the schema + */ + private dropRemovedTables; + /** + * Sync a single table (create or update) + */ + private syncTable; + /** + * Create a new table + */ + private createTable; + /** + * Update an existing table + */ + private updateTable; + /** + * Get existing columns for a table + */ + private getTableColumns; + /** + * Add a new column to existing table + */ + private addColumn; + /** + * Recreate table (for complex schema changes) + */ + private recreateTable; + /** + * Build column definition SQL + */ + private buildColumnDefinition; + /** + * Map DSQL data types to SQLite types + */ + private mapDataType; + /** + * Build foreign key constraint + */ + private buildForeignKeyConstraint; + /** + * Sync indexes for a table + */ + private syncIndexes; + /** + * Close database connection + */ + close(): void; +} +export { SQLiteSchemaManager }; diff --git a/dist/lib/sqlite/db-schema-manager.js b/dist/lib/sqlite/db-schema-manager.js new file mode 100644 index 0000000..d928fc9 --- /dev/null +++ b/dist/lib/sqlite/db-schema-manager.js @@ -0,0 +1,456 @@ +#!/usr/bin/env bun +import { Database } from "bun:sqlite"; +import _ from "lodash"; +import DbClient from "."; +// Schema Manager Class +class SQLiteSchemaManager { + db; + db_manager_table_name; + recreate_vector_table; + db_schema; + constructor({ schema, recreate_vector_table = false, }) { + this.db = DbClient; + this.db_manager_table_name = "__db_schema_manager__"; + this.db.run("PRAGMA foreign_keys = ON;"); + this.recreate_vector_table = recreate_vector_table; + this.createDbManagerTable(); + this.db_schema = schema; + } + createDbManagerTable() { + this.db.run(` + CREATE TABLE IF NOT EXISTS ${this.db_manager_table_name} ( + table_name TEXT NOT NULL, + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL + ) + `); + } + insertDbManagerTable(tableName) { + this.db.run(`INSERT INTO ${this.db_manager_table_name} (table_name,created_at,updated_at) VALUES (?, ?, ?)`, [tableName, Date.now(), Date.now()]); + } + removeDbManagerTable(tableName) { + this.db.run(`DELETE FROM ${this.db_manager_table_name} WHERE table_name = ?`, [tableName]); + } + /** + * Main synchronization method + */ + async syncSchema() { + console.log("Starting schema synchronization..."); + const existingTables = this.getExistingTables(); + const schemaTables = this.db_schema.tables.map((t) => t.tableName); + // 2. Create or update tables + for (const table of this.db_schema.tables) { + await this.syncTable(table, existingTables); + } + // 1. Drop tables that no longer exist in schema + await this.dropRemovedTables(existingTables, schemaTables); + console.log("Schema synchronization complete!"); + } + /** + * Get list of existing tables in the database + */ + getExistingTables() { + let sql = `SELECT table_name FROM ${this.db_manager_table_name}`; + const query = this.db.query(sql); + const results = query.all(); + return results.map((r) => r.table_name); + } + /** + * Drop tables that are no longer in the schema + */ + async dropRemovedTables(existingTables, schemaTables) { + const tablesToDrop = existingTables.filter((t) => !schemaTables.includes(t) && + !schemaTables.find((scT) => t.startsWith(scT + "_"))); + for (const tableName of tablesToDrop) { + console.log(`Dropping table: ${tableName}`); + this.db.run(`DROP TABLE IF EXISTS "${tableName}"`); + this.db.run(`DELETE FROM ${this.db_manager_table_name} WHERE table_name = "${tableName}"`); + } + } + /** + * Sync a single table (create or update) + */ + async syncTable(table, existingTables) { + let tableExists = existingTables.includes(table.tableName); + // Handle table rename + if (table.tableNameOld && table.tableNameOld !== table.tableName) { + if (existingTables.includes(table.tableNameOld)) { + console.log(`Renaming table: ${table.tableNameOld} -> ${table.tableName}`); + this.db.run(`ALTER TABLE "${table.tableNameOld}" RENAME TO "${table.tableName}"`); + this.insertDbManagerTable(table.tableName); + this.removeDbManagerTable(table.tableNameOld); + tableExists = true; + } + } + if (!tableExists) { + // Create new table + await this.createTable(table); + this.insertDbManagerTable(table.tableName); + } + else { + // Update existing table + await this.updateTable(table); + } + // Sync indexes + await this.syncIndexes(table); + } + /** + * Create a new table + */ + async createTable(table) { + console.log(`Creating table: ${table.tableName}`); + let new_table = _.cloneDeep(table); + if (new_table.parentTableName) { + const parent_table = this.db_schema.tables.find((t) => t.tableName === new_table.parentTableName); + if (!parent_table) { + throw new Error(`Parent table \`${new_table.parentTableName}\` not found for \`${new_table.tableName}\``); + } + new_table = _.merge(parent_table, { + tableName: new_table.tableName, + tableDescription: new_table.tableDescription, + }); + } + const columns = []; + const foreignKeys = []; + for (const field of new_table.fields) { + const columnDef = this.buildColumnDefinition(field); + columns.push(columnDef); + if (field.foreignKey) { + foreignKeys.push(this.buildForeignKeyConstraint(field)); + } + } + // Add unique constraints + if (new_table.uniqueConstraints) { + for (const constraint of new_table.uniqueConstraints) { + if (constraint.constraintTableFields && + constraint.constraintTableFields.length > 0) { + const fields = constraint.constraintTableFields + .map((f) => `"${f.value}"`) + .join(", "); + const constraintName = constraint.constraintName || + `unique_${fields.replace(/"/g, "")}`; + columns.push(`CONSTRAINT "${constraintName}" UNIQUE (${fields})`); + } + } + } + const allConstraints = [...columns, ...foreignKeys]; + const sql = new_table.isVector + ? `CREATE VIRTUAL TABLE "${new_table.tableName}" USING ${new_table.vectorType || "vec0"}(${allConstraints.join(", ")})` + : `CREATE TABLE "${new_table.tableName}" (${allConstraints.join(", ")})`; + this.db.run(sql); + } + /** + * Update an existing table + */ + async updateTable(table) { + console.log(`Updating table: ${table.tableName}`); + const existingColumns = this.getTableColumns(table.tableName); + const schemaColumns = table.fields.map((f) => f.fieldName || ""); + // SQLite has limited ALTER TABLE support + // We need to use the recreation strategy for complex changes + const columnsToAdd = table.fields.filter((f) => f.fieldName && + !existingColumns.find((c) => c.name == f.fieldName && c.type == this.mapDataType(f))); + const columnsToRemove = existingColumns.filter((c) => !schemaColumns.includes(c.name)); + const columnsToUpdate = table.fields.filter((f) => f.fieldName && + f.updatedField && + existingColumns.find((c) => c.name == f.fieldName && c.type == this.mapDataType(f))); + // Simple case: only adding columns + if (columnsToRemove.length === 0 && columnsToUpdate.length === 0) { + for (const field of columnsToAdd) { + await this.addColumn(table.tableName, field); + } + } + else { + // Complex case: need to recreate table + await this.recreateTable(table); + } + } + /** + * Get existing columns for a table + */ + getTableColumns(tableName) { + const query = this.db.query(`PRAGMA table_info("${tableName}")`); + const results = query.all(); + return results; + } + /** + * Add a new column to existing table + */ + async addColumn(tableName, field) { + console.log(`Adding column: ${tableName}.${field.fieldName}`); + const columnDef = this.buildColumnDefinition(field); + // Remove PRIMARY KEY and UNIQUE constraints for ALTER TABLE ADD COLUMN + const cleanDef = columnDef + .replace(/PRIMARY KEY/gi, "") + .replace(/AUTOINCREMENT/gi, "") + .replace(/UNIQUE/gi, "") + .trim(); + const sql = `ALTER TABLE "${tableName}" ADD COLUMN ${cleanDef}`; + this.db.run(sql); + } + /** + * Recreate table (for complex schema changes) + */ + async recreateTable(table) { + if (table.isVector) { + if (!this.recreate_vector_table) { + return; + } + console.log(`Recreating vector table: ${table.tableName}`); + const existingRows = this.db + .query(`SELECT * FROM "${table.tableName}"`) + .all(); + this.db.run(`DROP TABLE "${table.tableName}"`); + await this.createTable(table); + if (existingRows.length > 0) { + for (let i = 0; i < existingRows.length; i++) { + const row = existingRows[i]; + if (!row) + continue; + const columns = Object.keys(row); + const placeholders = columns.map(() => "?").join(", "); + this.db.run(`INSERT INTO "${table.tableName}" (${columns.join(", ")}) VALUES (${placeholders})`, Object.values(row)); + } + } + return; + } + const tempTableName = `${table.tableName}_temp_${Date.now()}`; + // Get existing data + const existingColumns = this.getTableColumns(table.tableName); + const columnsToKeep = table.fields + .filter((f) => f.fieldName && + existingColumns.find((c) => c.name == f.fieldName && + c.type == this.mapDataType(f))) + .map((f) => f.fieldName); + // Create temp table with new schema + const tempTable = { ...table, tableName: tempTableName }; + await this.createTable(tempTable); + // Copy data if there are common columns + if (columnsToKeep.length > 0) { + const columnList = columnsToKeep.map((c) => `"${c}"`).join(", "); + this.db.run(`INSERT INTO "${tempTableName}" (${columnList}) SELECT ${columnList} FROM "${table.tableName}"`); + } + // Drop old table + this.db.run(`DROP TABLE "${table.tableName}"`); + // Rename temp table + this.db.run(`ALTER TABLE "${tempTableName}" RENAME TO "${table.tableName}"`); + } + /** + * Build column definition SQL + */ + buildColumnDefinition(field) { + if (!field.fieldName) { + throw new Error("Field name is required"); + } + const fieldName = field.sideCar + ? `+${field.fieldName}` + : `${field.fieldName}`; + const parts = [fieldName]; + // Data type mapping + const dataType = this.mapDataType(field); + parts.push(dataType); + // Primary key + if (field.primaryKey) { + parts.push("PRIMARY KEY"); + if (field.autoIncrement) { + parts.push("AUTOINCREMENT"); + } + } + // Not null + if (field.notNullValue || field.primaryKey) { + if (!field.primaryKey) { + parts.push("NOT NULL"); + } + } + // Unique + if (field.unique && !field.primaryKey) { + parts.push("UNIQUE"); + } + // Default value + if (field.defaultValue !== undefined) { + if (typeof field.defaultValue === "string") { + parts.push( + // Escape single quotes by doubling them to prevent SQL injection and wrap in single quotes + `DEFAULT '${field.defaultValue.replace(/'/g, "''")}'`); + } + else { + parts.push(`DEFAULT ${field.defaultValue}`); + } + } + else if (field.defaultValueLiteral) { + parts.push(`DEFAULT ${field.defaultValueLiteral}`); + } + return parts.join(" "); + } + /** + * Map DSQL data types to SQLite types + */ + mapDataType(field) { + const dataType = field.dataType?.toLowerCase() || "text"; + const vectorSize = field.vectorSize || 1536; + // Vector Embeddings + if (field.isVector) { + return `FLOAT[${vectorSize}]`; + } + // Integer types + if (dataType.includes("int") || + dataType === "bigint" || + dataType === "smallint" || + dataType === "tinyint") { + return "INTEGER"; + } + // Real/Float types + if (dataType.includes("real") || + dataType.includes("float") || + dataType.includes("double") || + dataType === "decimal" || + dataType === "numeric") { + return "REAL"; + } + // Blob types + if (dataType.includes("blob") || dataType.includes("binary")) { + return "BLOB"; + } + // Boolean + if (dataType === "boolean" || dataType === "bool") { + return "INTEGER"; // SQLite uses INTEGER for boolean (0/1) + } + // Date/Time types + if (dataType.includes("date") || dataType.includes("time")) { + return "TEXT"; // SQLite stores dates as TEXT or INTEGER + } + // Default to TEXT for all text-based types + return "TEXT"; + } + /** + * Build foreign key constraint + */ + buildForeignKeyConstraint(field) { + const fk = field.foreignKey; + let constraint = `FOREIGN KEY ("${field.fieldName}") REFERENCES "${fk.destinationTableName}"("${fk.destinationTableColumnName}")`; + if (fk.cascadeDelete) { + constraint += " ON DELETE CASCADE"; + } + if (fk.cascadeUpdate) { + constraint += " ON UPDATE CASCADE"; + } + return constraint; + } + /** + * Sync indexes for a table + */ + async syncIndexes(table) { + if (!table.indexes || table.indexes.length === 0) { + return; + } + // Get existing indexes + const query = this.db.query(`SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='${table.tableName}' AND name NOT LIKE 'sqlite_%'`); + const existingIndexes = query.all().map((r) => r.name); + // Drop indexes not in schema + for (const indexName of existingIndexes) { + const stillExists = table.indexes.some((idx) => idx.indexName === indexName); + if (!stillExists) { + console.log(`Dropping index: ${indexName}`); + this.db.run(`DROP INDEX IF EXISTS "${indexName}"`); + } + } + // Create new indexes + for (const index of table.indexes) { + if (!index.indexName || + !index.indexTableFields || + index.indexTableFields.length === 0) { + continue; + } + if (!existingIndexes.includes(index.indexName)) { + console.log(`Creating index: ${index.indexName}`); + const fields = index.indexTableFields + .map((f) => `"${f.value}"`) + .join(", "); + const unique = index.indexType === "regular" ? "" : ""; // SQLite doesn't have FULLTEXT in CREATE INDEX + this.db.run(`CREATE ${unique}INDEX "${index.indexName}" ON "${table.tableName}" (${fields})`); + } + } + } + /** + * Close database connection + */ + close() { + this.db.close(); + } +} +// Example usage +async function main() { + const schema = { + dbName: "example_db", + tables: [ + { + tableName: "users", + tableDescription: "User accounts", + fields: [ + { + fieldName: "id", + dataType: "INTEGER", + primaryKey: true, + autoIncrement: true, + }, + { + fieldName: "username", + dataType: "TEXT", + notNullValue: true, + unique: true, + }, + { + fieldName: "email", + dataType: "TEXT", + notNullValue: true, + }, + { + fieldName: "created_at", + dataType: "TEXT", + defaultValueLiteral: "CURRENT_TIMESTAMP", + }, + ], + indexes: [ + { + indexName: "idx_users_email", + indexType: "regular", + indexTableFields: [ + { value: "email", dataType: "TEXT" }, + ], + }, + ], + }, + { + tableName: "posts", + fields: [ + { + fieldName: "id", + dataType: "INTEGER", + primaryKey: true, + autoIncrement: true, + }, + { + fieldName: "user_id", + dataType: "INTEGER", + notNullValue: true, + foreignKey: { + destinationTableName: "users", + destinationTableColumnName: "id", + cascadeDelete: true, + }, + }, + { + fieldName: "title", + dataType: "TEXT", + notNullValue: true, + }, + { + fieldName: "content", + dataType: "TEXT", + }, + ], + }, + ], + }; +} +export { SQLiteSchemaManager }; diff --git a/dist/lib/sqlite/db-schema-to-typedef.d.ts b/dist/lib/sqlite/db-schema-to-typedef.d.ts new file mode 100644 index 0000000..43062ab --- /dev/null +++ b/dist/lib/sqlite/db-schema-to-typedef.d.ts @@ -0,0 +1,6 @@ +import type { BUN_SQLITE_DatabaseSchemaType } from "../../types"; +type Params = { + dbSchema?: BUN_SQLITE_DatabaseSchemaType; +}; +export default function dbSchemaToType(params?: Params): string[] | undefined; +export {}; diff --git a/dist/lib/sqlite/db-schema-to-typedef.js b/dist/lib/sqlite/db-schema-to-typedef.js new file mode 100644 index 0000000..1739dbb --- /dev/null +++ b/dist/lib/sqlite/db-schema-to-typedef.js @@ -0,0 +1,44 @@ +import _ from "lodash"; +import generateTypeDefinition from "./db-generate-type-defs"; +export default function dbSchemaToType(params) { + let datasquirelSchema = params?.dbSchema; + if (!datasquirelSchema) + return; + let tableNames = `export const BunSQLiteTables = [\n${datasquirelSchema.tables + .map((tbl) => ` "${tbl.tableName}",`) + .join("\n")}\n] as const`; + const dbTablesSchemas = datasquirelSchema.tables; + const defDbName = datasquirelSchema.dbName + ?.toUpperCase() + .replace(/ |\-/g, "_"); + const defNames = []; + const schemas = dbTablesSchemas + .map((table) => { + let final_table = _.cloneDeep(table); + if (final_table.parentTableName) { + const parent_table = dbTablesSchemas.find((t) => t.tableName === final_table.parentTableName); + if (parent_table) { + final_table = _.merge(parent_table, { + tableName: final_table.tableName, + tableDescription: final_table.tableDescription, + }); + } + } + const defObj = generateTypeDefinition({ + paradigm: "TypeScript", + table: final_table, + typeDefName: `BUN_SQLITE_${defDbName}_${final_table.tableName.toUpperCase()}`, + allValuesOptional: true, + addExport: true, + }); + if (defObj.tdName?.match(/./)) { + defNames.push(defObj.tdName); + } + return defObj.typeDefinition; + }) + .filter((schm) => typeof schm == "string"); + const allTd = defNames?.[0] + ? `export type BUN_SQLITE_${defDbName}_ALL_TYPEDEFS = ${defNames.join(` & `)}` + : ``; + return [tableNames, ...schemas, allTd]; +} diff --git a/dist/lib/sqlite/db-select.d.ts b/dist/lib/sqlite/db-select.d.ts new file mode 100644 index 0000000..b3c001b --- /dev/null +++ b/dist/lib/sqlite/db-select.d.ts @@ -0,0 +1,17 @@ +import type { APIResponseObject, ServerQueryParam } from "../../types"; +type Params = { + query?: ServerQueryParam; + table: string; + count?: boolean; + targetId?: number | string; +}; +export default function DbSelect({ table, query, count, targetId }: Params): Promise>; +export {}; diff --git a/dist/lib/sqlite/db-select.js b/dist/lib/sqlite/db-select.js new file mode 100644 index 0000000..2f66f11 --- /dev/null +++ b/dist/lib/sqlite/db-select.js @@ -0,0 +1,48 @@ +import mysql from "mysql"; +import DbClient from "."; +import _ from "lodash"; +import sqlGenerator from "../../utils/sql-generator"; +export default async function DbSelect({ table, query, count, targetId }) { + try { + let finalQuery = query || {}; + if (targetId) { + finalQuery = _.merge(finalQuery, { + query: { + id: { + value: String(targetId), + }, + }, + }); + } + const sqlObj = sqlGenerator({ + tableName: table, + genObject: finalQuery, + count, + }); + const sql = mysql.format(sqlObj.string, sqlObj.values); + const res = DbClient.query(sql); + const batchRes = res.all(); + let resp = { + success: Boolean(batchRes[0]), + payload: batchRes, + singleRes: batchRes[0], + debug: { + sqlObj, + sql, + }, + }; + if (count) { + const count_val = count ? batchRes[0]?.["COUNT(*)"] : undefined; + resp["count"] = Number(count_val); + delete resp.payload; + delete resp.singleRes; + } + return resp; + } + catch (error) { + return { + success: false, + error: error.message, + }; + } +} diff --git a/dist/lib/sqlite/db-sql.d.ts b/dist/lib/sqlite/db-sql.d.ts new file mode 100644 index 0000000..2b1471c --- /dev/null +++ b/dist/lib/sqlite/db-sql.d.ts @@ -0,0 +1,11 @@ +import type { APIResponseObject } from "../../types"; +type Params = { + sql: string; + values?: (string | number)[]; +}; +export default function DbSQL({ sql, values }: Params): Promise>; +export {}; diff --git a/dist/lib/sqlite/db-sql.js b/dist/lib/sqlite/db-sql.js new file mode 100644 index 0000000..d348c2b --- /dev/null +++ b/dist/lib/sqlite/db-sql.js @@ -0,0 +1,33 @@ +import DbClient from "."; +import _ from "lodash"; +export default async function DbSQL({ sql, values }) { + try { + const res = sql.match(/^select/i) + ? DbClient.query(sql).all(...(values || [])) + : DbClient.run(sql, values || []); + return { + success: true, + payload: Array.isArray(res) ? res : undefined, + singleRes: Array.isArray(res) ? res?.[0] : undefined, + postInsertReturn: Array.isArray(res) + ? undefined + : { + affectedRows: res.changes, + insertId: Number(res.lastInsertRowid), + }, + debug: { + sqlObj: { + sql, + values, + }, + sql, + }, + }; + } + catch (error) { + return { + success: false, + error: error.message, + }; + } +} diff --git a/dist/lib/sqlite/db-update.d.ts b/dist/lib/sqlite/db-update.d.ts new file mode 100644 index 0000000..ec45082 --- /dev/null +++ b/dist/lib/sqlite/db-update.d.ts @@ -0,0 +1,17 @@ +import type { APIResponseObject, ServerQueryParam } from "../../types"; +type Params = { + table: string; + data: T; + query?: ServerQueryParam; + targetId?: number | string; +}; +export default function DbUpdate({ table, data, query, targetId }: Params): Promise; +export {}; diff --git a/dist/lib/sqlite/db-update.js b/dist/lib/sqlite/db-update.js new file mode 100644 index 0000000..57543e8 --- /dev/null +++ b/dist/lib/sqlite/db-update.js @@ -0,0 +1,68 @@ +import DbClient from "."; +import _ from "lodash"; +import sqlGenerator from "../../utils/sql-generator"; +export default async function DbUpdate({ table, data, query, targetId }) { + try { + let finalQuery = query || {}; + if (targetId) { + finalQuery = _.merge(finalQuery, { + query: { + id: { + value: String(targetId), + }, + }, + }); + } + const sqlQueryObj = sqlGenerator({ + tableName: table, + genObject: finalQuery, + }); + let values = []; + const whereClause = sqlQueryObj.string.match(/WHERE .*/)?.[0]; + if (whereClause) { + let sql = `UPDATE ${table} SET`; + const finalData = { + ...data, + updated_at: Date.now(), + }; + const keys = Object.keys(finalData); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + if (!key) + continue; + const isLast = i == keys.length - 1; + sql += ` ${key}=?`; + values.push(String(finalData[key])); + if (!isLast) { + sql += `,`; + } + } + sql += ` ${whereClause}`; + values = [...values, ...sqlQueryObj.values]; + const res = DbClient.run(sql, values); + return { + success: Boolean(res.changes), + postInsertReturn: { + affectedRows: res.changes, + insertId: Number(res.lastInsertRowid), + }, + debug: { + sql, + values, + }, + }; + } + else { + return { + success: false, + msg: `No WHERE clause`, + }; + } + } + catch (error) { + return { + success: false, + error: error.message, + }; + } +} diff --git a/dist/lib/sqlite/index.d.ts b/dist/lib/sqlite/index.d.ts new file mode 100644 index 0000000..438b68a --- /dev/null +++ b/dist/lib/sqlite/index.d.ts @@ -0,0 +1,3 @@ +import { Database } from "bun:sqlite"; +declare const DbClient: Database; +export default DbClient; diff --git a/dist/lib/sqlite/index.js b/dist/lib/sqlite/index.js new file mode 100644 index 0000000..af98f4f --- /dev/null +++ b/dist/lib/sqlite/index.js @@ -0,0 +1,17 @@ +import { Database } from "bun:sqlite"; +import path from "node:path"; +import * as sqliteVec from "sqlite-vec"; +import grabDirNames from "../../data/grab-dir-names"; +import init from "../../functions/init"; +const { ROOT_DIR } = grabDirNames(); +const { config } = await init(); +let db_dir = ROOT_DIR; +if (config.db_dir) { + db_dir = config.db_dir; +} +const DBFilePath = path.join(db_dir, config.db_name); +const DbClient = new Database(DBFilePath, { + create: true, +}); +sqliteVec.load(DbClient); +export default DbClient; diff --git a/dist/lib/sqlite/schema-to-typedef.d.ts b/dist/lib/sqlite/schema-to-typedef.d.ts new file mode 100644 index 0000000..bc433e8 --- /dev/null +++ b/dist/lib/sqlite/schema-to-typedef.d.ts @@ -0,0 +1,7 @@ +import type { BUN_SQLITE_DatabaseSchemaType } from "../../types"; +type Params = { + dbSchema: BUN_SQLITE_DatabaseSchemaType; + dst_file: string; +}; +export default function dbSchemaToTypeDef({ dbSchema, dst_file }: Params): void; +export {}; diff --git a/dist/lib/sqlite/schema-to-typedef.js b/dist/lib/sqlite/schema-to-typedef.js new file mode 100644 index 0000000..ff0231d --- /dev/null +++ b/dist/lib/sqlite/schema-to-typedef.js @@ -0,0 +1,18 @@ +import path from "node:path"; +import { existsSync, mkdirSync, writeFileSync } from "node:fs"; +import dbSchemaToType from "./db-schema-to-typedef"; +export default function dbSchemaToTypeDef({ dbSchema, dst_file }) { + try { + if (!dbSchema) + throw new Error("No schema found"); + const definitions = dbSchemaToType({ dbSchema }); + const ourfileDir = path.dirname(dst_file); + if (!existsSync(ourfileDir)) { + mkdirSync(ourfileDir, { recursive: true }); + } + writeFileSync(dst_file, definitions?.join("\n\n") || "", "utf-8"); + } + catch (error) { + console.log(`Schema to Typedef Error =>`, error.message); + } +} diff --git a/dist/lib/sqlite/schema.d.ts b/dist/lib/sqlite/schema.d.ts new file mode 100644 index 0000000..355768e --- /dev/null +++ b/dist/lib/sqlite/schema.d.ts @@ -0,0 +1,2 @@ +import type { BUN_SQLITE_DatabaseSchemaType } from "../../types"; +export declare const DbSchema: BUN_SQLITE_DatabaseSchemaType; diff --git a/dist/lib/sqlite/schema.js b/dist/lib/sqlite/schema.js new file mode 100644 index 0000000..5778e50 --- /dev/null +++ b/dist/lib/sqlite/schema.js @@ -0,0 +1,5 @@ +import _ from "lodash"; +export const DbSchema = { + dbName: "travis-ai", + tables: [], +}; diff --git a/dist/types/index.d.ts b/dist/types/index.d.ts new file mode 100644 index 0000000..fdd652b --- /dev/null +++ b/dist/types/index.d.ts @@ -0,0 +1,1014 @@ +import type { RequestOptions } from "https"; +export type BUN_SQLITE_DatabaseFullName = string; +export declare const UsersOmitedFields: readonly ["password", "social_id", "verification_status", "date_created", "date_created_code", "date_created_timestamp", "date_updated", "date_updated_code", "date_updated_timestamp"]; +export interface BUN_SQLITE_DatabaseSchemaType { + id?: string | number; + dbName?: string; + dbSlug?: string; + dbFullName?: string; + dbDescription?: string; + dbImage?: string; + tables: BUN_SQLITE_TableSchemaType[]; + childrenDatabases?: BUN_SQLITE_ChildrenDatabaseObject[]; + childDatabase?: boolean; + childDatabaseDbId?: string | number; + updateData?: boolean; + collation?: (typeof MariaDBCollations)[number]; +} +export interface BUN_SQLITE_ChildrenDatabaseObject { + dbId?: string | number; +} +export declare const MariaDBCollations: readonly ["utf8mb4_bin", "utf8mb4_unicode_520_ci"]; +export interface BUN_SQLITE_TableSchemaType { + id?: string | number; + tableName: string; + tableDescription?: string; + fields: BUN_SQLITE_FieldSchemaType[]; + indexes?: BUN_SQLITE_IndexSchemaType[]; + uniqueConstraints?: BUN_SQLITE_UniqueConstraintSchemaType[]; + childrenTables?: BUN_SQLITE_ChildrenTablesType[]; + /** + * Whether this is a child table + */ + childTable?: boolean; + updateData?: boolean; + /** + * ID of the parent table + */ + childTableId?: string | number; + /** + * ID of the parent table + */ + parentTableId?: string | number; + /** + * ID of the Database of parent table + */ + parentTableDbId?: string | number; + /** + * Name of the Database of parent table + */ + parentTableDbName?: string; + /** + * Name of the parent table + */ + parentTableName?: string; + tableNameOld?: string; + /** + * ID of the Database of parent table + */ + childTableDbId?: string | number; + collation?: (typeof MariaDBCollations)[number]; + isVector?: boolean; + vectorType?: string; +} +export interface BUN_SQLITE_ChildrenTablesType { + tableId?: string | number; + dbId?: string | number; +} +export declare const TextFieldTypesArray: readonly [{ + readonly title: "Plain Text"; + readonly value: "plain"; +}, { + readonly title: "Rich Text"; + readonly value: "richText"; +}, { + readonly title: "Markdown"; + readonly value: "markdown"; +}, { + readonly title: "JSON"; + readonly value: "json"; +}, { + readonly title: "YAML"; + readonly value: "yaml"; +}, { + readonly title: "HTML"; + readonly value: "html"; +}, { + readonly title: "CSS"; + readonly value: "css"; +}, { + readonly title: "Javascript"; + readonly value: "javascript"; +}, { + readonly title: "Shell"; + readonly value: "shell"; +}, { + readonly title: "Code"; + readonly value: "code"; +}]; +export declare const BUN_SQLITE_DATATYPES: readonly [{ + readonly value: "TEXT"; +}, { + readonly value: "INTEGER"; +}]; +export type BUN_SQLITE_FieldSchemaType = { + id?: number | string; + fieldName?: string; + fieldDescription?: string; + originName?: string; + updatedField?: boolean; + dataType: (typeof BUN_SQLITE_DATATYPES)[number]["value"]; + nullValue?: boolean; + notNullValue?: boolean; + primaryKey?: boolean; + encrypted?: boolean; + autoIncrement?: boolean; + defaultValue?: string | number; + defaultValueLiteral?: string; + foreignKey?: BUN_SQLITE_ForeignKeyType; + defaultField?: boolean; + plainText?: boolean; + unique?: boolean; + pattern?: string; + patternFlags?: string; + onUpdate?: string; + onUpdateLiteral?: string; + onDelete?: string; + onDeleteLiteral?: string; + cssFiles?: string[]; + integerLength?: string | number; + decimals?: string | number; + code?: boolean; + options?: (string | number)[]; + isVector?: boolean; + vectorSize?: number; + /** + * ### Adds a `+` prefix to colums + * In sqlite-vec, the + prefix is a specialized syntax for Virtual Table Columns. It essentially tells the database: "Keep this data associated with the vector, but don't try to index it for math." +Here is the breakdown of why they matter and how they work: +1. Performance Separation +In a standard table, adding a massive TEXT column (like a 2,000-word article) slows down full-table scans. In a vec0 virtual table, columns prefixed with + are stored in a separate internal side-car table. +The Vector Index: Stays lean and fast for "Nearest Neighbor" math. +The Content: Is only fetched after the vector search identifies the winning rows. +2. The "No Join" Convenience +Normally, you would store vectors in one table and the actual text content in another, linking them with a FOREIGN KEY. +Without + columns: You must JOIN two tables to get the text after finding the vector. +With + columns: You can SELECT content directly from the virtual table. It handles the "join" logic internally, making your code cleaner. +3. Syntax Example +When defining your schema, the + is only used in the CREATE statement. When querying or inserting, you treat it like a normal name. +```sql +-- SCHEMA DEFINITION +CREATE VIRTUAL TABLE documents USING vec0( + embedding float, -- The vector (indexed) + +title TEXT, -- Side-car metadata (not indexed) + +raw_body TEXT -- Side-car "heavy" data (not indexed) +); + +-- INSERTING (Notice: No '+' here) +INSERT INTO documents(embedding, title, raw_body) +VALUES (vec_f32(?), 'Bun Docs', 'Bun is a fast JavaScript runtime...'); + +-- QUERYING (Notice: No '+' here) +SELECT title, raw_body +FROM documents +WHERE embedding MATCH ? AND k = 1; +``` + */ + sideCar?: boolean; +} & { + [key in (typeof TextFieldTypesArray)[number]["value"]]?: boolean; +}; +export interface BUN_SQLITE_ForeignKeyType { + foreignKeyName?: string; + destinationTableName?: string; + destinationTableColumnName?: string; + destinationTableColumnType?: string; + cascadeDelete?: boolean; + cascadeUpdate?: boolean; +} +export interface BUN_SQLITE_IndexSchemaType { + id?: string | number; + indexName?: string; + indexType?: (typeof IndexTypes)[number]; + indexTableFields?: BUN_SQLITE_IndexTableFieldType[]; + alias?: string; + newTempIndex?: boolean; +} +export interface BUN_SQLITE_UniqueConstraintSchemaType { + id?: string | number; + constraintName?: string; + alias?: string; + constraintTableFields?: BUN_SQLITE_UniqueConstraintFieldType[]; +} +export interface BUN_SQLITE_UniqueConstraintFieldType { + value: string; +} +export interface BUN_SQLITE_IndexTableFieldType { + value: string; + dataType: string; +} +export interface BUN_SQLITE_MYSQL_SHOW_INDEXES_Type { + Key_name: string; + Table: string; + Column_name: string; + Collation: string; + Index_type: string; + Cardinality: string; + Index_comment: string; + Comment: string; +} +export interface BUN_SQLITE_MYSQL_SHOW_COLUMNS_Type { + Field: string; + Type: string; + Null: string; + Key: string; + Default: string; + Extra: string; +} +export interface BUN_SQLITE_MARIADB_SHOW_INDEXES_TYPE { + Table: string; + Non_unique: 0 | 1; + Key_name: string; + Seq_in_index: number; + Column_name: string; + Collation: string; + Cardinality: number; + Sub_part?: string; + Packed?: string; + Index_type?: "BTREE"; + Comment?: string; + Index_comment?: string; + Ignored?: "YES" | "NO"; +} +export interface BUN_SQLITE_MYSQL_FOREIGN_KEYS_Type { + CONSTRAINT_NAME: string; + CONSTRAINT_SCHEMA: string; + TABLE_NAME: string; +} +export interface BUN_SQLITE_MYSQL_user_databases_Type { + id: number; + user_id: number; + db_full_name: string; + db_name: string; + db_slug: string; + db_image: string; + db_description: string; + active_clone: number; + active_data: 0 | 1; + active_clone_parent_db: string; + remote_connected?: number; + remote_db_full_name?: string; + remote_connection_host?: string; + remote_connection_key?: string; + remote_connection_type?: string; + user_priviledge?: string; + date_created?: string; + image_thumbnail?: string; + first_name?: string; + last_name?: string; + email?: string; +} +export type ImageInputFileToBase64FunctionReturn = { + imageBase64?: string; + imageBase64Full?: string; + imageName?: string; + imageSize?: number; +}; +export interface GetReqQueryObject { + db: string; + query: string; + queryValues?: string; + tableName?: string; + debug?: boolean; +} +export type DATASQUIREL_LoggedInUser = { + id: number; + uuid?: string; + first_name: string; + last_name: string; + email: string; + phone?: string; + user_type?: string; + username?: string; + image?: string; + image_thumbnail?: string; + social_login?: number; + social_platform?: string; + social_id?: string; + verification_status?: number; + csrf_k: string; + logged_in_status: boolean; + date: number; +} & { + [key: string]: any; +}; +export interface AuthenticatedUser { + success: boolean; + payload: DATASQUIREL_LoggedInUser | null; + msg?: string; + userId?: number; + cookieNames?: any; +} +export interface SuccessUserObject { + id: number; + first_name: string; + last_name: string; + email: string; +} +export interface AddUserFunctionReturn { + success: boolean; + payload?: SuccessUserObject | null; + msg?: string; + sqlResult?: any; +} +export interface GoogleIdentityPromptNotification { + getMomentType: () => string; + getDismissedReason: () => string; + getNotDisplayedReason: () => string; + getSkippedReason: () => string; + isDismissedMoment: () => boolean; + isDisplayMoment: () => boolean; + isDisplayed: () => boolean; + isNotDisplayed: () => boolean; + isSkippedMoment: () => boolean; +} +export type UserDataPayload = { + first_name: string; + last_name: string; + email: string; + password?: string; + username?: string; +} & { + [key: string]: any; +}; +export interface GetUserFunctionReturn { + success: boolean; + payload: { + id: number; + first_name: string; + last_name: string; + username: string; + email: string; + phone: string; + social_id: [string]; + image: string; + image_thumbnail: string; + verification_status: [number]; + } | null; +} +export interface ReauthUserFunctionReturn { + success: boolean; + payload: DATASQUIREL_LoggedInUser | null; + msg?: string; + userId?: number; + token?: string; +} +export interface UpdateUserFunctionReturn { + success: boolean; + payload?: Object[] | string; +} +export interface GetReturn { + success: boolean; + payload?: R; + msg?: string; + error?: string; + schema?: BUN_SQLITE_TableSchemaType; + finalQuery?: string; +} +export interface GetSchemaRequestQuery { + database?: string; + table?: string; + field?: string; + user_id?: string | number; + env?: { + [k: string]: string; + }; +} +export interface GetSchemaAPICredentialsParam { + key: string; +} +export type GetSchemaAPIParam = GetSchemaRequestQuery & GetSchemaAPICredentialsParam; +export interface PostReturn { + success: boolean; + payload?: Object[] | string | PostInsertReturn; + msg?: string; + error?: any; + schema?: BUN_SQLITE_TableSchemaType; +} +export interface PostDataPayload { + action: "insert" | "update" | "delete"; + table: string; + data?: object; + identifierColumnName?: string; + identifierValue?: string; + duplicateColumnName?: string; + duplicateColumnValue?: string; + update?: boolean; +} +export interface LocalPostReturn { + success: boolean; + payload?: any; + msg?: string; + error?: string; +} +export interface LocalPostQueryObject { + query: string | PostDataPayload; + tableName?: string; + queryValues?: string[]; +} +export interface PostInsertReturn { + fieldCount?: number; + affectedRows?: number; + insertId?: number; + serverStatus?: number; + warningCount?: number; + message?: string; + protocol41?: boolean; + changedRows?: number; + error?: string; +} +export type UserType = DATASQUIREL_LoggedInUser & { + isSuperUser?: boolean; + staticHost?: string; + appHost?: string; + appName?: string; +}; +export interface ApiKeyDef { + name: string; + scope: string; + date_created: string; + apiKeyPayload: string; +} +export interface MetricsType { + dbCount: number; + tablesCount: number; + mediaCount: number; + apiKeysCount: number; +} +export interface MYSQL_mariadb_users_table_def { + id?: number; + user_id?: number; + username?: string; + host?: string; + password?: string; + primary?: number; + grants?: string; + date_created?: string; + date_created_code?: number; + date_created_timestamp?: string; + date_updated?: string; + date_updated_code?: number; + date_updated_timestamp?: string; +} +export interface MariaDBUserCredType { + mariadb_user?: string; + mariadb_host?: string; + mariadb_pass?: string; +} +export declare const ServerQueryOperators: readonly ["AND", "OR"]; +export declare const ServerQueryEqualities: readonly ["EQUAL", "LIKE", "LIKE_RAW", "LIKE_LOWER", "LIKE_LOWER_RAW", "NOT LIKE", "NOT LIKE_RAW", "NOT_LIKE_LOWER", "NOT_LIKE_LOWER_RAW", "NOT EQUAL", "REGEXP", "FULLTEXT", "IN", "NOT IN", "BETWEEN", "NOT BETWEEN", "IS NULL", "IS NOT NULL", "EXISTS", "NOT EXISTS", "GREATER THAN", "GREATER THAN OR EQUAL", "LESS THAN", "LESS THAN OR EQUAL", "MATCH", "MATCH_BOOLEAN"]; +export type ServerQueryParam = { + selectFields?: (keyof T | TableSelectFieldsObject)[]; + omitFields?: (keyof T)[]; + query?: ServerQueryQueryObject; + limit?: number; + page?: number; + offset?: number; + order?: ServerQueryParamOrder | ServerQueryParamOrder[]; + searchOperator?: (typeof ServerQueryOperators)[number]; + searchEquality?: (typeof ServerQueryEqualities)[number]; + addUserId?: { + fieldName: keyof T; + }; + join?: (ServerQueryParamsJoin | ServerQueryParamsJoin[] | undefined)[]; + group?: keyof T | ServerQueryParamGroupBy | (keyof T | ServerQueryParamGroupBy)[]; + countSubQueries?: ServerQueryParamsCount[]; + fullTextSearch?: ServerQueryParamFullTextSearch; + [key: string]: any; +}; +export type ServerQueryParamGroupBy = { + field: keyof T; + table?: string; +}; +export type ServerQueryParamOrder = { + field: keyof T; + strategy: "ASC" | "DESC"; +}; +export type ServerQueryParamFullTextSearch = { + fields: (keyof T)[]; + searchTerm: string; + /** Field Name to user to Rank the Score of Search Results */ + scoreAlias: string; +}; +export type ServerQueryParamsCount = { + table: string; + /** Alias for the Table From which the count is fetched */ + table_alias?: string; + srcTrgMap: { + src: string; + trg: string | ServerQueryParamsCountSrcTrgMap; + }[]; + alias: string; +}; +export type ServerQueryParamsCountSrcTrgMap = { + table: string; + field: string; +}; +export type TableSelectFieldsObject = { + fieldName: keyof T; + alias?: string; +}; +export type ServerQueryValuesObject = { + value?: string | number; + equality?: (typeof ServerQueryEqualities)[number]; + tableName?: string; + fieldName?: string; +}; +export type ServerQueryObjectValue = string | (string | ServerQueryValuesObject | undefined | null) | (string | ServerQueryValuesObject | undefined | null)[]; +export type ServerQueryObject = { + value?: ServerQueryObjectValue; + nullValue?: boolean; + notNullValue?: boolean; + operator?: (typeof ServerQueryOperators)[number]; + equality?: (typeof ServerQueryEqualities)[number]; + tableName?: K; + /** + * This will replace the top level field name if + * provided + */ + fieldName?: string; + __query?: { + [key in keyof T]: Omit, "__query">; + }; + vector?: boolean; + /** + * ### The Function to be used to generate the vector. + * Eg. `vec_f32`. This will come out as `vec_f32(?)` + * instead of just `?` + */ + vectorFunction?: string; +}; +export type ServerQueryQueryObject = { + [key in keyof T]: ServerQueryObject; +}; +export type FetchDataParams = { + path: string; + method?: (typeof DataCrudRequestMethods)[number]; + body?: object | string; + query?: AuthFetchQuery; + tableName?: string; +}; +export type AuthFetchQuery = ServerQueryParam & { + [key: string]: any; +}; +export type ServerQueryParamsJoin = { + joinType: "INNER JOIN" | "JOIN" | "LEFT JOIN" | "RIGHT JOIN"; + alias?: string; + tableName: Table; + match?: ServerQueryParamsJoinMatchObject | ServerQueryParamsJoinMatchObject[]; + selectFields?: (keyof Field | { + field: keyof Field; + alias?: string; + count?: boolean; + })[]; + omitFields?: (keyof Field | { + field: keyof Field; + alias?: string; + count?: boolean; + })[]; + operator?: (typeof ServerQueryOperators)[number]; +}; +export type ServerQueryParamsJoinMatchObject = { + /** Field name from the **Root Table** */ + source?: string | ServerQueryParamsJoinMatchSourceTargetObject; + /** Field name from the **Join Table** */ + target?: keyof Field | ServerQueryParamsJoinMatchSourceTargetObject; + /** A literal value: No source and target Needed! */ + targetLiteral?: string | number; + __batch?: { + matches: Omit, "__batch">[]; + operator: "AND" | "OR"; + }; +}; +export type ServerQueryParamsJoinMatchSourceTargetObject = { + tableName: string; + fieldName: string; +}; +export type ApiConnectBody = { + url: string; + key: string; + database: BUN_SQLITE_MYSQL_user_databases_Type; + dbSchema: BUN_SQLITE_DatabaseSchemaType; + type: "pull" | "push"; + user_id?: string | number; +}; +export type SuUserType = { + email: string; + password: string; + authKey: string; + logged_in_status: boolean; + date: number; +}; +export type MariadbRemoteServerObject = { + host: string; + port: number; + primary?: boolean; + loadBalanced?: boolean; + users?: MariadbRemoteServerUserObject[]; +}; +export type MariadbRemoteServerUserObject = { + name: string; + password: string; + host: string; +}; +export type APILoginFunctionReturn = { + success: boolean; + msg?: string; + payload?: DATASQUIREL_LoggedInUser | null; + userId?: number | string; + key?: string; + token?: string; + csrf?: string; + cookieNames?: any; +}; +export type APICreateUserFunctionParams = { + encryptionKey?: string; + payload: any; + database: string; + dsqlUserID?: string | number; + verify?: boolean; +}; +export type APICreateUserFunction = (params: APICreateUserFunctionParams) => Promise; +export type HandleSocialDbFunctionReturn = { + success: boolean; + user?: DATASQUIREL_LoggedInUser | null; + msg?: string; + social_id?: string | number; + social_platform?: string; + payload?: any; + alert?: boolean; + newUser?: any; + error?: any; +} | null; +export type CookieObject = { + name: string; + value: string; + domain?: string; + path?: string; + expires?: Date; + maxAge?: number; + secure?: boolean; + httpOnly?: boolean; + sameSite?: "Strict" | "Lax" | "None"; + priority?: "Low" | "Medium" | "High"; +}; +export type HttpRequestParams = RequestOptions & { + scheme?: "http" | "https"; + body?: ReqObj; + query?: ReqObj; + urlEncodedFormBody?: boolean; +}; +export type HttpRequestFunction = (param: HttpRequestParams) => Promise>; +export type HttpFunctionResponse = { + status: number; + data?: ResObj; + error?: string; + str?: string; + requestedPath?: string; +}; +export type ApiGetQueryObject = { + query: ServerQueryParam; + table: string; + dbFullName?: string; +}; +export declare const DataCrudRequestMethods: readonly ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]; +export declare const DataCrudRequestMethodsLowerCase: readonly ["get", "post", "put", "patch", "delete", "options"]; +export type DsqlMethodCrudParam = { + method: (typeof DataCrudRequestMethods)[number]; + body?: T; + query?: DsqlCrudQueryObject; + tableName: string; + addUser?: { + field: keyof T; + }; + user?: DATASQUIREL_LoggedInUser; + extraData?: T; + transformData?: DsqlCrudTransformDataFunction; + transformQuery?: DsqlCrudTransformQueryFunction; + existingData?: T; + targetId?: string | number; + sanitize?: ({ data, batchData }: { + data?: T; + batchData?: T[]; + }) => T | T[]; + debug?: boolean; +}; +export type DsqlCrudTransformDataFunction = (params: { + data: T; + user?: DATASQUIREL_LoggedInUser; + existingData?: T; + reqMethod: (typeof DataCrudRequestMethods)[number]; +}) => Promise; +export type DsqlCrudTransformQueryFunction = (params: { + query: DsqlCrudQueryObject; + user?: DATASQUIREL_LoggedInUser; + reqMethod: (typeof DataCrudRequestMethods)[number]; +}) => Promise>; +export declare const DsqlCrudActions: readonly ["insert", "update", "delete", "get"]; +export type DsqlCrudQueryObject = ServerQueryParam & { + query?: ServerQueryQueryObject; +}; +export type SQLDeleteGeneratorParams = { + tableName: string; + deleteKeyValues?: SQLDeleteData[]; + deleteKeyValuesOperator?: "AND" | "OR"; + dbFullName?: string; + data?: any; +}; +export type SQLDeleteData = { + key: keyof T; + value: string | number | null | undefined; + operator?: (typeof ServerQueryEqualities)[number]; +}; +export type DsqlCrudParamWhereClause = { + clause: string; + params?: string[]; +}; +export type ErrorCallback = (title: string, error: Error, data?: any) => void; +export interface MariaDBUser { + Host?: string; + User?: string; + Password?: string; + Select_priv?: string; + Insert_priv?: string; + Update_priv?: string; + Delete_priv?: string; + Create_priv?: string; + Drop_priv?: string; + Reload_priv?: string; + Shutdown_priv?: string; + Process_priv?: string; + File_priv?: string; + Grant_priv?: string; + References_priv?: string; + Index_priv?: string; + Alter_priv?: string; + Show_db_priv?: string; + Super_priv?: string; + Create_tmp_table_priv?: string; + Lock_tables_priv?: string; + Execute_priv?: string; + Repl_slave_priv?: string; + Repl_client_priv?: string; + Create_view_priv?: string; + Show_view_priv?: string; + Create_routine_priv?: string; + Alter_routine_priv?: string; + Create_user_priv?: string; + Event_priv?: string; + Trigger_priv?: string; + Create_tablespace_priv?: string; + Delete_history_priv?: string; + ssl_type?: string; + ssl_cipher?: string; + x509_issuer?: string; + x509_subject?: string; + max_questions?: number; + max_updates?: number; + max_connections?: number; + max_user_connections?: number; + plugin?: string; + authentication_string?: string; + password_expired?: string; + is_role?: string; + default_role?: string; + max_statement_time?: number; +} +export declare const QueryFields: readonly ["duplicate", "user_id", "delegated_user_id", "db_id", "table_id", "db_slug"]; +export type LocalFolderType = { + name: string; + isPrivate: boolean; +}; +export type ResponseQueryObject = { + sql?: string; + params?: (string | number)[]; +}; +export type APIResponseObject = { + success: boolean; + payload?: T[] | null; + singleRes?: T | null; + stringRes?: string | null; + numberRes?: number | null; + postInsertReturn?: PostInsertReturn | null; + payloadBase64?: string; + payloadThumbnailBase64?: string; + payloadURL?: string; + payloadThumbnailURL?: string; + error?: any; + msg?: string; + queryObject?: ResponseQueryObject; + countQueryObject?: ResponseQueryObject; + status?: number; + count?: number; + errors?: BUNSQLITEErrorObject[]; + debug?: any; + batchPayload?: any[][] | null; + errorData?: any; + token?: string; + csrf?: string; + cookieNames?: any; + key?: string; + userId?: string | number; + code?: string; + createdAt?: number; + email?: string; + requestOptions?: RequestOptions; + logoutUser?: boolean; + redirect?: string; +}; +/** + * # Docker Compose Types + */ +export type DockerCompose = { + services: DockerComposeServicesType; + networks: DockerComposeNetworks; + name: string; +}; +export declare const DockerComposeServices: readonly ["setup", "cron", "reverse-proxy", "webapp", "websocket", "static", "db", "maxscale", "post-db-setup", "web-app-post-db-setup", "post-replica-db-setup", "db-replica-1", "db-replica-2", "db-cron", "web-app-post-db-setup"]; +export type DockerComposeServicesType = { + [key in (typeof DockerComposeServices)[number]]: DockerComposeServiceWithBuildObject; +}; +export type DockerComposeNetworks = { + [k: string]: { + driver?: "bridge"; + ipam?: { + config: DockerComposeNetworkConfigObject[]; + }; + external?: boolean; + }; +}; +export type DockerComposeNetworkConfigObject = { + subnet: string; + gateway: string; +}; +export type DockerComposeServiceWithBuildObject = { + build: DockerComposeServicesBuildObject; + env_file: string; + container_name: string; + hostname: string; + volumes: string[]; + environment: string[]; + ports?: string[]; + networks?: DockerComposeServiceNetworkObject; + restart?: string; + depends_on?: { + [k: string]: { + condition: string; + }; + }; + user?: string; +}; +export type DockerComposeServiceWithImage = Omit & { + image: string; +}; +export type DockerComposeServicesBuildObject = { + context: string; + dockerfile: string; +}; +export type DockerComposeServiceNetworkObject = { + [k: string]: { + ipv4_address: string; + }; +}; +export type ClonedTableInfo = { + dbId?: string | number; + tableId?: string | number; + keepUpdated?: boolean; + keepDataUpdated?: boolean; +}; +export type DefaultEntryType = { + id?: number; + uuid?: string; + date_created?: string; + date_created_code?: number; + date_created_timestamp?: string; + date_updated?: string; + date_updated_code?: number; + date_updated_timestamp?: string; +} & { + [k: string]: string | number | null; +}; +export declare const IndexTypes: readonly ["regular", "full_text", "vector"]; +export type BUNSQLITEErrorObject = { + sql?: string; + sqlValues?: any[]; + error?: string; +}; +export interface SQLInsertGenReturn { + query: string; + values: (string | number)[]; +} +export type SQLInsertGenDataFn = () => { + placeholder: string; + value: string | number | Float32Array; +}; +export type SQLInsertGenDataType = { + [k: string]: string | number | SQLInsertGenDataFn | undefined | null; +}; +export type SQLInsertGenParams = { + data: SQLInsertGenDataType[]; + tableName: string; + dbFullName?: string; +}; +export type BunSQLiteConfig = { + db_name: string; + /** + * The Name of the Database Schema File. Eg `db_schema.ts`. This is + * relative to `db_dir`, or root dir if `db_dir` is not provided + */ + db_schema_file_name: string; + /** + * The Directory for backups + */ + db_backup_dir: string; + /** + * The Root Directory for the DB file and schema + */ + db_dir?: string; + /** + * The File Path relative to the root(working) directory for the type + * definition export. Example `db_types.ts` or `types/db_types.ts` + */ + typedef_file_path?: string; +}; +export type BunSQLiteConfigReturn = { + config: BunSQLiteConfig; + dbSchema: BUN_SQLITE_DatabaseSchemaType; +}; +export declare const DefaultFields: BUN_SQLITE_FieldSchemaType[]; diff --git a/dist/types/index.js b/dist/types/index.js new file mode 100644 index 0000000..391067f --- /dev/null +++ b/dist/types/index.js @@ -0,0 +1,123 @@ +export const UsersOmitedFields = [ + "password", + "social_id", + "verification_status", + "date_created", + "date_created_code", + "date_created_timestamp", + "date_updated", + "date_updated_code", + "date_updated_timestamp", +]; +export const MariaDBCollations = [ + "utf8mb4_bin", + "utf8mb4_unicode_520_ci", +]; +export const TextFieldTypesArray = [ + { title: "Plain Text", value: "plain" }, + { title: "Rich Text", value: "richText" }, + { title: "Markdown", value: "markdown" }, + { title: "JSON", value: "json" }, + { title: "YAML", value: "yaml" }, + { title: "HTML", value: "html" }, + { title: "CSS", value: "css" }, + { title: "Javascript", value: "javascript" }, + { title: "Shell", value: "shell" }, + { title: "Code", value: "code" }, +]; +export const BUN_SQLITE_DATATYPES = [ + { value: "TEXT" }, + { value: "INTEGER" }, +]; +export const ServerQueryOperators = ["AND", "OR"]; +export const ServerQueryEqualities = [ + "EQUAL", + "LIKE", + "LIKE_RAW", + "LIKE_LOWER", + "LIKE_LOWER_RAW", + "NOT LIKE", + "NOT LIKE_RAW", + "NOT_LIKE_LOWER", + "NOT_LIKE_LOWER_RAW", + "NOT EQUAL", + "REGEXP", + "FULLTEXT", + "IN", + "NOT IN", + "BETWEEN", + "NOT BETWEEN", + "IS NULL", + "IS NOT NULL", + "EXISTS", + "NOT EXISTS", + "GREATER THAN", + "GREATER THAN OR EQUAL", + "LESS THAN", + "LESS THAN OR EQUAL", + "MATCH", + "MATCH_BOOLEAN", +]; +export const DataCrudRequestMethods = [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE", + "OPTIONS", +]; +export const DataCrudRequestMethodsLowerCase = [ + "get", + "post", + "put", + "patch", + "delete", + "options", +]; +export const DsqlCrudActions = ["insert", "update", "delete", "get"]; +export const QueryFields = [ + "duplicate", + "user_id", + "delegated_user_id", + "db_id", + "table_id", + "db_slug", +]; +export const DockerComposeServices = [ + "setup", + "cron", + "reverse-proxy", + "webapp", + "websocket", + "static", + "db", + "maxscale", + "post-db-setup", + "web-app-post-db-setup", + "post-replica-db-setup", + "db-replica-1", + "db-replica-2", + "db-cron", + "web-app-post-db-setup", +]; +export const IndexTypes = ["regular", "full_text", "vector"]; +export const DefaultFields = [ + { + fieldName: "id", + dataType: "INTEGER", + primaryKey: true, + autoIncrement: true, + notNullValue: true, + fieldDescription: "The unique identifier of the record.", + }, + { + fieldName: "created_at", + dataType: "INTEGER", + fieldDescription: "The time when the record was created. (Unix Timestamp)", + }, + { + fieldName: "updated_at", + dataType: "INTEGER", + fieldDescription: "The time when the record was updated. (Unix Timestamp)", + }, +]; diff --git a/dist/utils/append-default-fields-to-db-schema.d.ts b/dist/utils/append-default-fields-to-db-schema.d.ts new file mode 100644 index 0000000..5c65648 --- /dev/null +++ b/dist/utils/append-default-fields-to-db-schema.d.ts @@ -0,0 +1,6 @@ +import { type BUN_SQLITE_DatabaseSchemaType } from "../types"; +type Params = { + dbSchema: BUN_SQLITE_DatabaseSchemaType; +}; +export default function ({ dbSchema }: Params): BUN_SQLITE_DatabaseSchemaType; +export {}; diff --git a/dist/utils/append-default-fields-to-db-schema.js b/dist/utils/append-default-fields-to-db-schema.js new file mode 100644 index 0000000..d1b758a --- /dev/null +++ b/dist/utils/append-default-fields-to-db-schema.js @@ -0,0 +1,12 @@ +import _ from "lodash"; +import { DefaultFields } from "../types"; +export default function ({ dbSchema }) { + const finaldbSchema = _.cloneDeep(dbSchema); + finaldbSchema.tables = finaldbSchema.tables.map((t) => { + const newTable = _.cloneDeep(t); + newTable.fields = newTable.fields.filter((f) => !f.fieldName?.match(/^(id|created_at|updated_at)$/)); + newTable.fields.unshift(...DefaultFields); + return newTable; + }); + return finaldbSchema; +} diff --git a/dist/utils/sql-equality-parser.d.ts b/dist/utils/sql-equality-parser.d.ts new file mode 100644 index 0000000..cd5a253 --- /dev/null +++ b/dist/utils/sql-equality-parser.d.ts @@ -0,0 +1,2 @@ +import { ServerQueryEqualities } from "../types"; +export default function sqlEqualityParser(eq: (typeof ServerQueryEqualities)[number]): string; diff --git a/dist/utils/sql-equality-parser.js b/dist/utils/sql-equality-parser.js new file mode 100644 index 0000000..207d670 --- /dev/null +++ b/dist/utils/sql-equality-parser.js @@ -0,0 +1,39 @@ +import { ServerQueryEqualities } from "../types"; +export default function sqlEqualityParser(eq) { + switch (eq) { + case "EQUAL": + return "="; + case "LIKE": + return "LIKE"; + case "NOT LIKE": + return "NOT LIKE"; + case "NOT EQUAL": + return "<>"; + case "IN": + return "IN"; + case "NOT IN": + return "NOT IN"; + case "BETWEEN": + return "BETWEEN"; + case "NOT BETWEEN": + return "NOT BETWEEN"; + case "IS NULL": + return "IS NULL"; + case "IS NOT NULL": + return "IS NOT NULL"; + case "EXISTS": + return "EXISTS"; + case "NOT EXISTS": + return "NOT EXISTS"; + case "GREATER THAN": + return ">"; + case "GREATER THAN OR EQUAL": + return ">="; + case "LESS THAN": + return "<"; + case "LESS THAN OR EQUAL": + return "<="; + default: + return "="; + } +} diff --git a/dist/utils/sql-gen-operator-gen.d.ts b/dist/utils/sql-gen-operator-gen.d.ts new file mode 100644 index 0000000..d8535ef --- /dev/null +++ b/dist/utils/sql-gen-operator-gen.d.ts @@ -0,0 +1,20 @@ +import type { ServerQueryEqualities, ServerQueryObject } from "../types"; +type Params = { + fieldName: string; + value?: string; + equality?: (typeof ServerQueryEqualities)[number]; + queryObj: ServerQueryObject<{ + [key: string]: any; + }, string>; + isValueFieldValue?: boolean; +}; +type Return = { + str?: string; + param?: string; +}; +/** + * # SQL Gen Operator Gen + * @description Generates an SQL operator for node module `mysql` or `serverless-mysql` + */ +export default function sqlGenOperatorGen({ fieldName, value, equality, queryObj, isValueFieldValue, }: Params): Return; +export {}; diff --git a/dist/utils/sql-gen-operator-gen.js b/dist/utils/sql-gen-operator-gen.js new file mode 100644 index 0000000..5c93273 --- /dev/null +++ b/dist/utils/sql-gen-operator-gen.js @@ -0,0 +1,127 @@ +import sqlEqualityParser from "./sql-equality-parser"; +/** + * # SQL Gen Operator Gen + * @description Generates an SQL operator for node module `mysql` or `serverless-mysql` + */ +export default function sqlGenOperatorGen({ fieldName, value, equality, queryObj, isValueFieldValue, }) { + if (queryObj.nullValue) { + return { str: `${fieldName} IS NULL` }; + } + if (queryObj.notNullValue) { + return { str: `${fieldName} IS NOT NULL` }; + } + if (value) { + const finalValue = isValueFieldValue ? value : "?"; + const finalParams = isValueFieldValue ? undefined : value; + if (equality == "MATCH") { + return { + str: `MATCH(${fieldName}) AGAINST(${finalValue} IN NATURAL LANGUAGE MODE)`, + param: finalParams, + }; + } + else if (equality == "MATCH_BOOLEAN") { + return { + str: `MATCH(${fieldName}) AGAINST(${finalValue} IN BOOLEAN MODE)`, + param: finalParams, + }; + } + else if (equality == "LIKE_LOWER") { + return { + str: `LOWER(${fieldName}) LIKE LOWER(${finalValue})`, + param: `%${finalParams}%`, + }; + } + else if (equality == "LIKE_LOWER_RAW") { + return { + str: `LOWER(${fieldName}) LIKE LOWER(${finalValue})`, + param: finalParams, + }; + } + else if (equality == "LIKE") { + return { + str: `${fieldName} LIKE ${finalValue}`, + param: `%${finalParams}%`, + }; + } + else if (equality == "LIKE_RAW") { + return { + str: `${fieldName} LIKE ${finalValue}`, + param: finalParams, + }; + } + else if (equality == "NOT_LIKE_LOWER") { + return { + str: `LOWER(${fieldName}) NOT LIKE LOWER(${finalValue})`, + param: `%${finalParams}%`, + }; + } + else if (equality == "NOT_LIKE_LOWER_RAW") { + return { + str: `LOWER(${fieldName}) NOT LIKE LOWER(${finalValue})`, + param: finalParams, + }; + } + else if (equality == "NOT LIKE") { + return { + str: `${fieldName} NOT LIKE ${finalValue}`, + param: finalParams, + }; + } + else if (equality == "NOT LIKE_RAW") { + return { + str: `${fieldName} NOT LIKE ${finalValue}`, + param: finalParams, + }; + } + else if (equality == "REGEXP") { + return { + str: `LOWER(${fieldName}) REGEXP LOWER(${finalValue})`, + param: finalParams, + }; + } + else if (equality == "FULLTEXT") { + return { + str: `MATCH(${fieldName}) AGAINST(${finalValue} IN BOOLEAN MODE)`, + param: finalParams, + }; + } + else if (equality == "NOT EQUAL") { + return { + str: `${fieldName} != ${finalValue}`, + param: finalParams, + }; + } + else if (equality) { + return { + str: `${fieldName} ${sqlEqualityParser(equality)} ${finalValue}`, + param: finalParams, + }; + } + else { + return { + str: `${fieldName} = ${finalValue}`, + param: finalParams, + }; + } + } + else { + if (equality == "IS NULL") { + return { str: `${fieldName} IS NULL` }; + } + else if (equality == "IS NOT NULL") { + return { str: `${fieldName} IS NOT NULL` }; + } + else if (equality) { + return { + str: `${fieldName} ${sqlEqualityParser(equality)} ?`, + param: value, + }; + } + else { + return { + str: `${fieldName} = ?`, + param: value, + }; + } + } +} diff --git a/dist/utils/sql-generator.d.ts b/dist/utils/sql-generator.d.ts new file mode 100644 index 0000000..30f714a --- /dev/null +++ b/dist/utils/sql-generator.d.ts @@ -0,0 +1,25 @@ +import type { ServerQueryParam } from "../types"; +type Param = { + genObject?: ServerQueryParam; + tableName: string; + dbFullName?: string; + count?: boolean; +}; +type Return = { + string: string; + values: (string | number)[]; +}; +/** + * # SQL Query Generator + * @description Generates an SQL Query for node module `mysql` or `serverless-mysql` + */ +export default function sqlGenerator({ tableName, genObject, dbFullName, count }: Param): Return; +export {}; diff --git a/dist/utils/sql-generator.js b/dist/utils/sql-generator.js new file mode 100644 index 0000000..2b90c09 --- /dev/null +++ b/dist/utils/sql-generator.js @@ -0,0 +1,392 @@ +import { isUndefined } from "lodash"; +import sqlGenOperatorGen from "./sql-gen-operator-gen"; +/** + * # SQL Query Generator + * @description Generates an SQL Query for node module `mysql` or `serverless-mysql` + */ +export default function sqlGenerator({ tableName, genObject, dbFullName, count }) { + const finalQuery = genObject?.query ? genObject.query : undefined; + const queryKeys = finalQuery ? Object.keys(finalQuery) : undefined; + const sqlSearhValues = []; + const finalDbName = dbFullName ? `${dbFullName}.` : ""; + /** + * # Generate Query + */ + function genSqlSrchStr({ queryObj, join, field, }) { + const finalFieldName = (() => { + if (queryObj?.tableName) { + return `${finalDbName}${queryObj.tableName}.${field}`; + } + if (join) { + return `${finalDbName}${tableName}.${field}`; + } + return field; + })(); + let str = `${finalFieldName}=?`; + function grabValue(val) { + const valueParsed = val; + if (!valueParsed) + return; + const valueString = typeof valueParsed == "string" + ? valueParsed + : valueParsed + ? valueParsed.fieldName && valueParsed.tableName + ? `${valueParsed.tableName}.${valueParsed.fieldName}` + : valueParsed.value?.toString() + : undefined; + const valueEquality = typeof valueParsed == "object" + ? valueParsed.equality || queryObj.equality + : queryObj.equality; + const operatorStrParam = sqlGenOperatorGen({ + queryObj, + equality: valueEquality, + fieldName: finalFieldName || "", + value: valueString?.toString() || "", + isValueFieldValue: Boolean(typeof valueParsed == "object" && + valueParsed.fieldName && + valueParsed.tableName), + }); + return operatorStrParam; + } + if (Array.isArray(queryObj.value)) { + const strArray = []; + queryObj.value.forEach((val) => { + const operatorStrParam = grabValue(val); + if (!operatorStrParam) + return; + if (operatorStrParam.str && operatorStrParam.param) { + strArray.push(operatorStrParam.str); + sqlSearhValues.push(operatorStrParam.param); + } + else if (operatorStrParam.str) { + strArray.push(operatorStrParam.str); + } + }); + str = "(" + strArray.join(` ${queryObj.operator || "AND"} `) + ")"; + } + else if (typeof queryObj.value == "object") { + const operatorStrParam = grabValue(queryObj.value); + if (operatorStrParam?.str) { + str = operatorStrParam.str; + if (operatorStrParam.param) { + sqlSearhValues.push(operatorStrParam.param); + } + } + } + else { + const valueParsed = queryObj.value + ? String(queryObj.value) + : undefined; + const operatorStrParam = sqlGenOperatorGen({ + equality: queryObj.equality, + fieldName: finalFieldName || "", + value: valueParsed, + queryObj, + }); + if (operatorStrParam.str && operatorStrParam.param) { + str = operatorStrParam.str; + sqlSearhValues.push(operatorStrParam.param); + } + else if (operatorStrParam.str) { + str = operatorStrParam.str; + } + } + return str; + } + function generateJoinStr(mtch, join) { + if (mtch.__batch) { + let btch_mtch = ``; + btch_mtch += `(`; + for (let i = 0; i < mtch.__batch.matches.length; i++) { + const __mtch = mtch.__batch.matches[i]; + btch_mtch += `${generateJoinStr(__mtch, join)}`; + if (i < mtch.__batch.matches.length - 1) { + btch_mtch += ` ${mtch.__batch.operator || "OR"} `; + } + } + btch_mtch += `)`; + return btch_mtch; + } + return `${finalDbName}${typeof mtch.source == "object" ? mtch.source.tableName : tableName}.${typeof mtch.source == "object" ? mtch.source.fieldName : mtch.source}=${(() => { + if (mtch.targetLiteral) { + if (typeof mtch.targetLiteral == "number") { + return `${mtch.targetLiteral}`; + } + return `'${mtch.targetLiteral}'`; + } + if (join.alias) { + return `${finalDbName}${typeof mtch.target == "object" + ? mtch.target.tableName + : join.alias}.${typeof mtch.target == "object" + ? mtch.target.fieldName + : mtch.target}`; + } + return `${finalDbName}${typeof mtch.target == "object" + ? mtch.target.tableName + : join.tableName}.${typeof mtch.target == "object" + ? mtch.target.fieldName + : mtch.target}`; + })()}`; + } + let fullTextMatchStr = genObject?.fullTextSearch + ? ` MATCH(${genObject.fullTextSearch.fields + .map((f) => genObject.join ? `${tableName}.${String(f)}` : `${String(f)}`) + .join(",")}) AGAINST (? IN BOOLEAN MODE)` + : undefined; + const fullTextSearchStr = genObject?.fullTextSearch + ? genObject.fullTextSearch.searchTerm + .split(` `) + .map((t) => `${t}`) + .join(" ") + : undefined; + let queryString = (() => { + let str = "SELECT"; + if (count) { + str += ` COUNT(*)`; + } + else if (genObject?.selectFields?.[0]) { + if (genObject.join) { + str += ` ${genObject.selectFields + ?.map((fld) => typeof fld == "object" + ? `${finalDbName}${tableName}.${fld.fieldName.toString()}` + + (fld.alias ? ` as ${fld.alias}` : ``) + : `${finalDbName}${tableName}.${String(fld)}`) + .join(",")}`; + } + else { + str += ` ${genObject.selectFields + ?.map((fld) => typeof fld == "object" + ? `${fld.fieldName.toString()}` + + (fld.alias ? ` as ${fld.alias}` : ``) + : fld) + .join(",")}`; + } + } + else { + if (genObject?.join) { + str += ` ${finalDbName}${tableName}.*`; + } + else { + str += " *"; + } + } + if (genObject?.countSubQueries) { + let countSqls = []; + for (let i = 0; i < genObject.countSubQueries.length; i++) { + const countSubQuery = genObject.countSubQueries[i]; + if (!countSubQuery) + continue; + const tableAlias = countSubQuery.table_alias; + let subQStr = `(SELECT COUNT(*)`; + subQStr += ` FROM ${countSubQuery.table}${tableAlias ? ` ${tableAlias}` : ""}`; + subQStr += ` WHERE (`; + for (let j = 0; j < countSubQuery.srcTrgMap.length; j++) { + const csqSrc = countSubQuery.srcTrgMap[j]; + if (!csqSrc) + continue; + subQStr += ` ${tableAlias || countSubQuery.table}.${csqSrc.src}`; + if (typeof csqSrc.trg == "string") { + subQStr += ` = ?`; + sqlSearhValues.push(csqSrc.trg); + } + else if (typeof csqSrc.trg == "object") { + subQStr += ` = ${csqSrc.trg.table}.${csqSrc.trg.field}`; + } + if (j < countSubQuery.srcTrgMap.length - 1) { + subQStr += ` AND `; + } + } + subQStr += ` )) AS ${countSubQuery.alias}`; + countSqls.push(subQStr); + } + str += `, ${countSqls.join(",")}`; + } + if (genObject?.join && !count) { + const existingJoinTableNames = [tableName]; + str += + "," + + genObject.join + .flat() + .filter((j) => !isUndefined(j)) + .map((joinObj) => { + const joinTableName = joinObj.alias + ? joinObj.alias + : joinObj.tableName; + if (existingJoinTableNames.includes(joinTableName)) + return null; + existingJoinTableNames.push(joinTableName); + if (joinObj.selectFields) { + return joinObj.selectFields + .map((selectField) => { + if (typeof selectField == "string") { + return `${finalDbName}${joinTableName}.${selectField}`; + } + else if (typeof selectField == "object") { + let aliasSelectField = selectField.count + ? `COUNT(${finalDbName}${joinTableName}.${selectField.field})` + : `${finalDbName}${joinTableName}.${selectField.field}`; + if (selectField.alias) + aliasSelectField += ` AS ${selectField.alias}`; + return aliasSelectField; + } + }) + .join(","); + } + else { + return `${finalDbName}${joinTableName}.*`; + } + }) + .filter((_) => Boolean(_)) + .join(","); + } + if (genObject?.fullTextSearch && + fullTextMatchStr && + fullTextSearchStr) { + str += `, ${fullTextMatchStr} AS ${genObject.fullTextSearch.scoreAlias}`; + sqlSearhValues.push(fullTextSearchStr); + } + str += ` FROM ${finalDbName}${tableName}`; + if (genObject?.join) { + str += + " " + + genObject.join + .flat() + .filter((j) => !isUndefined(j)) + .map((join) => { + return (join.joinType + + " " + + (join.alias + ? `${finalDbName}${join.tableName}` + + " " + + join.alias + : `${finalDbName}${join.tableName}`) + + " ON " + + (() => { + if (Array.isArray(join.match)) { + return ("(" + + join.match + .map((mtch) => generateJoinStr(mtch, join)) + .join(join.operator + ? ` ${join.operator} ` + : " AND ") + + ")"); + } + else if (typeof join.match == "object") { + return generateJoinStr(join.match, join); + } + })()); + }) + .join(" "); + } + return str; + })(); + const sqlSearhString = queryKeys?.map((field) => { + const queryObj = finalQuery?.[field]; + if (!queryObj) + return; + if (queryObj.__query) { + const subQueryGroup = queryObj.__query; + const subSearchKeys = Object.keys(subQueryGroup); + const subSearchString = subSearchKeys.map((_field) => { + const newSubQueryObj = subQueryGroup?.[_field]; + if (newSubQueryObj) { + return genSqlSrchStr({ + queryObj: newSubQueryObj, + field: newSubQueryObj.fieldName || _field, + join: genObject?.join, + }); + } + }); + return ("(" + + subSearchString.join(` ${queryObj.operator || "AND"} `) + + ")"); + } + return genSqlSrchStr({ + queryObj, + field: queryObj.fieldName || field, + join: genObject?.join, + }); + }); + const cleanedUpSearchStr = sqlSearhString?.filter((str) => typeof str == "string"); + const isSearchStr = cleanedUpSearchStr?.[0] && cleanedUpSearchStr.find((str) => str); + if (isSearchStr) { + const stringOperator = genObject?.searchOperator || "AND"; + queryString += ` WHERE ${cleanedUpSearchStr.join(` ${stringOperator} `)}`; + } + if (genObject?.fullTextSearch && fullTextSearchStr && fullTextMatchStr) { + queryString += `${isSearchStr ? " AND" : " WHERE"} ${fullTextMatchStr}`; + sqlSearhValues.push(fullTextSearchStr); + } + if (genObject?.group) { + let group_by_txt = ``; + if (typeof genObject.group == "string") { + group_by_txt = genObject.group; + } + else if (Array.isArray(genObject.group)) { + for (let i = 0; i < genObject.group.length; i++) { + const group = genObject.group[i]; + if (typeof group == "string") { + group_by_txt += `\`${group.toString()}\``; + } + else if (typeof group == "object" && group.table) { + group_by_txt += `${group.table}.${String(group.field)}`; + } + else if (typeof group == "object") { + group_by_txt += `${String(group.field)}`; + } + if (i < genObject.group.length - 1) { + group_by_txt += ","; + } + } + } + else if (typeof genObject.group == "object") { + if (genObject.group.table) { + group_by_txt = `${genObject.group.table}.${String(genObject.group.field)}`; + } + else { + group_by_txt = `${String(genObject.group.field)}`; + } + } + queryString += ` GROUP BY ${group_by_txt}`; + } + function grabOrderString(order) { + let orderFields = []; + let orderSrt = ``; + if (genObject?.fullTextSearch && genObject.fullTextSearch.scoreAlias) { + orderFields.push(genObject.fullTextSearch.scoreAlias); + } + else if (genObject?.join) { + orderFields.push(`${finalDbName}${tableName}.${String(order.field)}`); + } + else { + orderFields.push(order.field); + } + orderSrt += ` ${orderFields.join(", ")} ${order.strategy}`; + return orderSrt; + } + if (genObject?.order && !count) { + let orderSrt = ` ORDER BY`; + if (Array.isArray(genObject.order)) { + for (let i = 0; i < genObject.order.length; i++) { + const order = genObject.order[i]; + if (order) { + orderSrt += + grabOrderString(order) + + (i < genObject.order.length - 1 ? `,` : ""); + } + } + } + else { + orderSrt += grabOrderString(genObject.order); + } + queryString += ` ${orderSrt}`; + } + if (genObject?.limit && !count) + queryString += ` LIMIT ${genObject.limit}`; + if (genObject?.offset && !count) + queryString += ` OFFSET ${genObject.offset}`; + return { + string: queryString, + values: sqlSearhValues, + }; +} diff --git a/dist/utils/sql-insert-generator.d.ts b/dist/utils/sql-insert-generator.d.ts new file mode 100644 index 0000000..1b43778 --- /dev/null +++ b/dist/utils/sql-insert-generator.d.ts @@ -0,0 +1,5 @@ +import type { SQLInsertGenParams, SQLInsertGenReturn } from "../types"; +/** + * # SQL Insert Generator + */ +export default function sqlInsertGenerator({ tableName, data, dbFullName, }: SQLInsertGenParams): SQLInsertGenReturn | undefined; diff --git a/dist/utils/sql-insert-generator.js b/dist/utils/sql-insert-generator.js new file mode 100644 index 0000000..7b0ddc7 --- /dev/null +++ b/dist/utils/sql-insert-generator.js @@ -0,0 +1,56 @@ +/** + * # SQL Insert Generator + */ +export default function sqlInsertGenerator({ tableName, data, dbFullName, }) { + const finalDbName = dbFullName ? `${dbFullName}.` : ""; + try { + if (Array.isArray(data) && data?.[0]) { + let insertKeys = []; + data.forEach((dt) => { + const kys = Object.keys(dt); + kys.forEach((ky) => { + if (!insertKeys.includes(ky)) { + insertKeys.push(ky); + } + }); + }); + let queryBatches = []; + let queryValues = []; + data.forEach((item) => { + queryBatches.push(`(${insertKeys + .map((ky) => { + const value = item[ky]; + const finalValue = typeof value == "string" || + typeof value == "number" + ? value + : value + ? String(value().value) + : null; + if (!finalValue) { + queryValues.push(""); + return "?"; + } + queryValues.push(finalValue); + const placeholder = typeof value == "function" + ? value().placeholder + : "?"; + return placeholder; + }) + .filter((k) => Boolean(k)) + .join(",")})`); + }); + let query = `INSERT INTO ${finalDbName}${tableName} (${insertKeys.join(",")}) VALUES ${queryBatches.join(",")}`; + return { + query: query, + values: queryValues, + }; + } + else { + return undefined; + } + } + catch ( /** @type {any} */error) { + console.log(`SQL insert gen ERROR: ${error.message}`); + return undefined; + } +} diff --git a/package.json b/package.json index 6aca06e..299147b 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "@moduletrace/bun-sqlite", "version": "1.0.0", "description": "SQLite manager for Bun", + "author": "Benjamin Toby", "main": "dist/index.js", "bin": { "bun-sqlite": "dist/commands/index.js" diff --git a/tsconfig.json b/tsconfig.json index e353072..e8f4da1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,9 +7,7 @@ "jsx": "react-jsx", "allowJs": true, "moduleResolution": "bundler", - "allowImportingTsExtensions": true, "verbatimModuleSyntax": true, - "noEmit": true, "strict": true, "skipLibCheck": true, "noFallthroughCasesInSwitch": true, @@ -20,6 +18,9 @@ "noPropertyAccessFromIndexSignature": false, "declaration": true, "resolveJsonModule": true, + "maxNodeModuleJsDepth": 10, + "forceConsistentCasingInFileNames": true, + "incremental": true, "outDir": "dist" }, "include": ["src"],