diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 760b461..82f9035 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -3,4 +3,4 @@ Here are some known issues and resolutions to it ## I have changed my file but the changes are not reflected in files table -Sometimes changes might not be properly added to the `files` table. If this happens, you can call `SQLSeal: Reload Vault Database` command (using `CTRL + P` / `CMD + P` shortcut to activate command window). If this does not help, please report an issue on project page with details. \ No newline at end of file +Sometimes changes might not be properly added to the `files` table. If this happens, you can call `SQLSeal: Reload vault database` command (using `CTRL + P` / `CMD + P` shortcut to activate command window). If this does not help, please report an issue on project page with details. \ No newline at end of file diff --git a/manifest.json b/manifest.json index 95490dc..cda4cca 100644 --- a/manifest.json +++ b/manifest.json @@ -1,5 +1,5 @@ { - "id": "hypersphere-sqlseal", + "id": "sqlseal", "name": "SQLSeal", "version": "0.2.0", "minAppVersion": "0.15.0", diff --git a/package.json b/package.json index e8c2bc0..a0013a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,5 @@ - { - "name": "obsidian-sqlseal", + "name": "sqlseal", "version": "0.2.0", "description": "A plugin for Obsidian that allows you to run SQL queries on your notes.", "main": "main.js", @@ -38,7 +37,6 @@ }, "dependencies": { "better-sqlite3": "^9.5.0", - "gray-matter": "^4.0.3", "node-sql-parser": "^5.0.0", "papaparse": "^5.4.1" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5e6fe17..29773dd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,6 @@ dependencies: better-sqlite3: specifier: ^9.5.0 version: 9.6.0 - gray-matter: - specifier: ^4.0.3 - version: 4.0.3 node-sql-parser: specifier: ^5.0.0 version: 5.1.0 @@ -1501,12 +1498,6 @@ packages: readable-stream: 3.6.2 dev: true - /argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - dependencies: - sprintf-js: 1.0.3 - dev: false - /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true @@ -2082,12 +2073,6 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - dev: false - /esquery@1.5.0: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} @@ -2130,13 +2115,6 @@ packages: resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} dev: true - /extend-shallow@2.0.1: - resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} - engines: {node: '>=0.10.0'} - dependencies: - is-extendable: 0.1.1 - dev: false - /extract-zip@2.0.1: resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} @@ -2437,16 +2415,6 @@ packages: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true - /gray-matter@4.0.3: - resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} - engines: {node: '>=6.0'} - dependencies: - js-yaml: 3.14.1 - kind-of: 6.0.3 - section-matter: 1.0.0 - strip-bom-string: 1.0.0 - dev: false - /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -2591,11 +2559,6 @@ packages: sprintf-js: 1.1.3 dev: true - /is-extendable@0.1.1: - resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} - engines: {node: '>=0.10.0'} - dev: false - /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -2641,14 +2604,6 @@ packages: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true - /js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - dev: false - /js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -2698,11 +2653,6 @@ packages: json-buffer: 3.0.1 dev: true - /kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - dev: false - /levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -3380,14 +3330,6 @@ packages: resolution: {integrity: sha512-Orrsjf9trHHxFRuo9/rzm0KIWmgzE8RMlZMzuhZOJ01Rnz3D0YBAe+V6473t6/H6c7irs6Lt48brULAiRWb3Vw==} dev: true - /section-matter@1.0.0: - resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} - engines: {node: '>=4'} - dependencies: - extend-shallow: 2.0.1 - kind-of: 6.0.3 - dev: false - /semver-compare@1.0.0: resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} requiresBuild: true @@ -3497,10 +3439,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - dev: false - /sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} dev: true @@ -3533,11 +3471,6 @@ packages: ansi-regex: 5.0.1 dev: true - /strip-bom-string@1.0.0: - resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} - engines: {node: '>=0.10.0'} - dev: false - /strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} diff --git a/src/SealFileSync.ts b/src/SealFileSync.ts index f02241b..96f2e24 100644 --- a/src/SealFileSync.ts +++ b/src/SealFileSync.ts @@ -1,16 +1,5 @@ import { App, EventRef, Plugin, TAbstractFile, TFile } from "obsidian"; -import GrayMatter from "gray-matter"; import { SqlSeal } from "./sqlSeal"; -import { delay } from "./utils"; - - - -async function extractFrontmatterFromFile(file: TAbstractFile) { - const content = await this.app.vault.read(file); - const gm = GrayMatter(content) - - return gm.data -} function fileData(file: TAbstractFile, frontmatter: Record) { return { @@ -21,18 +10,21 @@ function fileData(file: TAbstractFile, frontmatter: Record) { } } +const extractFrontmatterFromFile = async (file: TFile, plugin: Plugin) => { + return plugin.app.metadataCache.getFileCache(file)?.frontmatter || {} +} + export class SealFileSync { private currentSchema: Record = {} - private refs: Array = [] constructor(public readonly app: App, private readonly sqlSeal: SqlSeal, private readonly plugin: Plugin) { - this.refs.push(this.app.vault.on('modify', async (file) => { + plugin.registerEvent(this.app.vault.on('modify', async (file) => { if (!(file instanceof TFile)) { return } - const frontmatter = await extractFrontmatterFromFile(file) + const frontmatter = extractFrontmatterFromFile(file, plugin) if (this.hasNewColumns(frontmatter)) { - await delay(1000) + await sleep(1000) await this.init() return @@ -45,20 +37,20 @@ export class SealFileSync { // Wait 1 second before updating tags table - await delay(1000) + await sleep(1000) await this.sqlSeal.db.insertData('tags', await this.getFileTags(file)) this.sqlSeal.observer.fireObservers('table:tags') })) - this.refs.push(this.app.vault.on('create', async (file) => { + plugin.registerEvent(this.app.vault.on('create', async (file) => { if (!(file instanceof TFile)) { return } - const frontmatter = await extractFrontmatterFromFile(file) + const frontmatter = await extractFrontmatterFromFile(file, plugin) if (this.hasNewColumns(frontmatter)) { - await delay(1000) + await sleep(1000) await this.init() return @@ -70,12 +62,12 @@ export class SealFileSync { this.sqlSeal.observer.fireObservers('table:files') // Wait 1 second before updating tags table - await delay(1000) + await sleep(1000) await this.sqlSeal.db.insertData('tags', await this.getFileTags(file)) this.sqlSeal.observer.fireObservers('table:tags') })) - this.refs.push(this.app.vault.on('delete', async (file) => { + plugin.registerEvent(this.app.vault.on('delete', async (file) => { if (!(file instanceof TFile)) { return } @@ -87,7 +79,7 @@ export class SealFileSync { this.sqlSeal.observer.fireObservers('table:tags') })) - this.refs.push(this.app.vault.on('rename', async (file, oldPath) => { + plugin.registerEvent(this.app.vault.on('rename', async (file, oldPath) => { if (!(file instanceof TFile)) { return } @@ -98,11 +90,11 @@ export class SealFileSync { // delete old tags await this.sqlSeal.db.deleteData('tags', [{ fileId: oldPath }], 'fileId') - await this.sqlSeal.db.insertData('files', [fileData(file, await extractFrontmatterFromFile(file))]) + await this.sqlSeal.db.insertData('files', [fileData(file, await extractFrontmatterFromFile(file, this.plugin))]) this.sqlSeal.observer.fireObservers('table:files') // Wait 1 second before updating tags table - await delay(1000) + await sleep(1000) await this.sqlSeal.db.insertData('tags', await this.getFileTags(file)) this.sqlSeal.observer.fireObservers('table:tags') @@ -111,17 +103,13 @@ export class SealFileSync { // add Obsidian command to reload SQLSeal file database this.plugin.addCommand({ id: 'reload-sqlseal', - name: 'Reload Vault Database', + name: 'Reload vault database', callback: async () => { await this.init() } }) } - destroy() { - this.refs.forEach(ref => this.app.vault.offref(ref)) - } - async getFileTags(file: TFile) { return (this.app.metadataCache.getFileCache(file)?.tags || []) .map(t => t.tag) @@ -135,7 +123,7 @@ export class SealFileSync { const tags: Array<{fileId: string, tag: string }> = [] for (const file of files) { - const frontmatter = await extractFrontmatterFromFile(file) + const frontmatter = await extractFrontmatterFromFile(file, this.plugin) tags.push(...await this.getFileTags(file)) data.push(fileData(file, frontmatter)) } diff --git a/src/database.ts b/src/database.ts index b3ec272..47b6ca5 100644 --- a/src/database.ts +++ b/src/database.ts @@ -1,10 +1,10 @@ import Database from "better-sqlite3" -import { App } from "obsidian" +import { App, normalizePath } from "obsidian" import path from 'path' import Papa from 'papaparse' import { prefixedIfNotGlobal } from "./sqlReparseTables" import fs from 'fs' -import { delay, fetchBlobData } from "./utils" +import { fetchBlobData } from "./utils" function isNumeric(str: string) { if (typeof str != "string") return false // we only process strings! @@ -81,19 +81,21 @@ export class SqlSealDatabase { this.connectingPromiseResolve = resolve }) //@ts-ignore - const dbPath = path.resolve(this.app.vault.adapter.basePath, this.app.vault.configDir, "plugins/sqlseal/better_sqlite3.node") + const dbPath = normalizePath(this.app.vault.adapter.basePath + '/' + this.app.vault.configDir + '/sqlseal.db') + + // const dbPath = path.resolve(this.app.vault.adapter.basePath, this.app.vault.configDir, "plugins/sqlseal/better_sqlite3.node") // check if file "better_sqlite3.node" exists in the plugin folder, if not, download it from github release - if (!fs.existsSync(dbPath)) { - const url = 'https://github.com/h-sphere/sql-seal/releases/download/0.2.0/better_sqlite3.node' // my github release url + // if (!fs.existsSync(dbPath)) { + // const url = 'https://github.com/h-sphere/sql-seal/releases/download/0.2.0/better_sqlite3.node' // my github release url - await fetchBlobData(url, dbPath) - } + // await fetchBlobData(url, dbPath) + // } - await delay(500) + // await sleep(500) - //@ts-ignore - const defaultDbPath = path.resolve(this.app.vault.adapter.basePath, this.app.vault.configDir, "sqlseal.db") - this.db = new Database(defaultDbPath, { verbose: this.verbose ? console.log : undefined }) + // //@ts-ignore + // const defaultDbPath = path.resolve(this.app.vault.adapter.basePath, this.app.vault.configDir, "sqlseal.db") + this.db = new Database(dbPath, { verbose: this.verbose ? console.log : undefined }) this.isConnected = true this.connectingPromiseResolve() diff --git a/src/hash.ts b/src/hash.ts index dd3f2d9..5169661 100644 --- a/src/hash.ts +++ b/src/hash.ts @@ -1,10 +1,118 @@ -import * as crypto from 'crypto'; -export const hashString = (path: string) => { - const hash = crypto.createHash('sha256') - hash.update(path) - return hash.digest('hex') +export function hashString(inputString: string): string { + // Function to convert a number to alphanumeric character + function toAlphanumeric(num: number): string { + const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + return chars[num % chars.length]; + } + + // Function to generate SHA-256 hash + function sha256(input: string): string { + // Simple SHA-256 implementation + function sha256Internal(input: string): string { + function rightRotate(value: number, shift: number): number { + return (value >>> shift) | (value << (32 - shift)); + } + + function toHexString(num: number): string { + let hex = ''; + for (let i = 0; i < 8; i++) { + hex += ((num >>> (24 - i * 4)) & 0xf).toString(16); + } + return hex; + } + + const words: number[] = []; + for (let i = 0; i < input.length * 8; i += 8) { + words[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << (24 - i % 32); + } + words[input.length >> 2] |= 0x80 << (24 - (input.length % 4) * 8); + words[((input.length + 64 >> 9) << 4) + 15] = input.length * 8; + + let a = 0x6a09e667; + let b = 0xbb67ae85; + let c = 0x3c6ef372; + let d = 0xa54ff53a; + let e = 0x510e527f; + let f = 0x9b05688c; + let g = 0x1f83d9ab; + let h = 0x5be0cd19; + + for (let i = 0; i < words.length; i += 16) { + let aa = a; + let bb = b; + let cc = c; + let dd = d; + let ee = e; + let ff = f; + let gg = g; + let hh = h; + + for (let j = 0; j < 64; j++) { + let T1 = hh + ((ee & ff) | (~ee & gg)) + words[i + j] + K[j]; + T1 = T1 | 0; + let T2 = rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25); + T2 = T2 | 0; + let T3 = (e & f) ^ (~e & g); + T3 = T3 | 0; + let T4 = h + T1 + T2 + T3 + H[j]; + T4 = T4 | 0; + + h = g; + g = f; + f = e; + e = d + T4; + e = e | 0; + d = c; + c = b; + b = a; + a = T4 + T1; + a = a | 0; + } + + a = a + aa; + b = b + bb; + c = c + cc; + d = d + dd; + e = e + ee; + f = f + ff; + g = g + gg; + h = h + hh; + } + + return toHexString(a) + toHexString(b) + toHexString(c) + toHexString(d) + + toHexString(e) + toHexString(f) + toHexString(g) + toHexString(h); + } + + // Convert the SHA-256 hash to alphanumeric characters + function hashToAlphanumeric(hash: string): string { + return hash.replace(/[^\w]/g, ''); + } + + return hashToAlphanumeric(sha256Internal(input)); + } + + // Constants for SHA-256 + const K = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + ]; + + const H = [ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 + ]; + + return sha256(inputString); } + + + export const generatePrefix = (prefix: string, tableName: string) => { return `TTT${prefix}_${tableName}` } \ No newline at end of file diff --git a/src/settings.ts b/src/settings.ts index eb60353..9de8726 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -25,14 +25,12 @@ export class SqlSealSettingsTab extends PluginSettingTab { containerEl.empty(); - containerEl.createEl('h2', { text: 'My Plugin Settings' }); - new Setting(containerEl) .setName('Rows') .setDesc('Configure your rows') .addButton((button: ButtonComponent) => { button - .setButtonText('Add Row') + .setButtonText('Add row') .onClick(async () => { this.plugin.settings.rows.push({ name: '', type: 'File', dynamicField: '' }); await this.plugin.saveSettings(); @@ -74,10 +72,10 @@ export class SqlSealSettingsTab extends PluginSettingTab { if (row.type === 'File') { new Setting(rowContainer) - .setName('File Selector') + .setName('File selector') .addText(text => { text - .setPlaceholder('Select File') + .setPlaceholder('Select file') .setValue(row.dynamicField) .onChange(async (value) => { this.plugin.settings.rows[index].dynamicField = value; diff --git a/src/sqlSeal.ts b/src/sqlSeal.ts index 9dbeb08..affbb2d 100644 --- a/src/sqlSeal.ts +++ b/src/sqlSeal.ts @@ -1,4 +1,3 @@ -import GrayMatter from "gray-matter"; import { SqlSealDatabase } from "./database"; import { displayData, displayError, displayInfo, displayLoader } from "./ui"; import { App, MarkdownPostProcessorContext } from "obsidian"; @@ -36,11 +35,7 @@ export class SqlSeal { if (!file) { return null } - const data = await this.app.vault.cachedRead(file) - - const gm = GrayMatter(data) - const frontmatter = gm.data - return frontmatter + return this.app.metadataCache.getFileCache(file)?.frontmatter } @@ -99,7 +94,6 @@ export class SqlSeal { displayLoader(el) - // await delay(1000) await renderSelect() } diff --git a/src/utils.ts b/src/utils.ts index 86221db..79b5714 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -48,6 +48,4 @@ export const fetchBlobData = async (url: string, filePath: string) => { reject(`Error writing to ${filePath}: ${err}`) }); }) - }; - -export const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) \ No newline at end of file + }; \ No newline at end of file