diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c039f15..1d14a3f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,15 +20,12 @@ jobs: with: node-version: "20" cache: "npm" - cache-dependency-path: plugin/package-lock.json - - name: Install plugin dependencies + - name: Install dependencies run: npm ci - working-directory: plugin - name: Build plugin run: npm run build - working-directory: plugin - name: Verify manifest version matches tag run: | @@ -43,8 +40,8 @@ jobs: run: | mkdir -p release cp manifest.json release/manifest.json - cp plugin/main.js release/main.js - cp plugin/styles.css release/styles.css + cp main.js release/main.js + cp styles.css release/styles.css - name: Create GitHub release uses: softprops/action-gh-release@v2 diff --git a/.gitignore b/.gitignore index 3866d14..41ba740 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,7 @@ dist/ .DS_Store .env .env.* -plugin/main.js -plugin/manifest.json -plugin/versions.json -plugin/node_modules/ -plugin/coverage/ +main.js +coverage/ supabase/.temp/ testing/ diff --git a/README.md b/README.md index ccaa26a..9eaca98 100644 --- a/README.md +++ b/README.md @@ -141,17 +141,17 @@ Until this plugin is published to the Obsidian community registry, install it ma 1. Build the plugin: ```bash - npm install --prefix plugin - npm run build:plugin + npm install + npm run build ``` 2. Copy the output files into your vault's plugin folder: ``` /.obsidian/plugins/telegram-bridge/ - ├── main.js (from plugin/main.js) - ├── manifest.json (from repo root) - └── styles.css (from plugin/styles.css) + ├── main.js + ├── manifest.json + └── styles.css ``` 3. In Obsidian → **Settings → Community plugins**, enable **Telegram Bridge**. @@ -325,21 +325,22 @@ The Telegram Bot API limits file downloads to **20 MB**. Anything larger cannot ## Repository layout ``` -obsidian-telegram/ -├── manifest.json Obsidian plugin manifest (release source of truth) +obsidian-telegram-bridge/ +├── manifest.json Obsidian plugin manifest ├── versions.json Plugin → min Obsidian app version map -├── plugin/ Obsidian plugin (TypeScript + esbuild) -│ ├── manifest.json Copied from root manifest by esbuild (gitignored) -│ ├── styles.css Plugin styles -│ └── src/ -│ ├── main.ts Plugin entry point -│ ├── sync-engine.ts Polling, cursor management, Realtime -│ ├── vault-writer.ts File creation and editing in the vault -│ ├── message-renderer.ts Markdown rendering with block markers -│ ├── template-engine.ts Template variable expansion -│ ├── distribution-rules.ts Filter query evaluation -│ ├── settings-tab.ts Settings UI -│ └── types.ts Shared TypeScript types +├── styles.css Plugin styles +├── esbuild.config.mjs Build config +├── tsconfig.json TypeScript config +├── src/ +│ ├── main.ts Plugin entry point +│ ├── sync-engine.ts Polling, cursor management, Realtime +│ ├── vault-writer.ts File creation and editing in the vault +│ ├── message-renderer.ts Markdown rendering with block markers +│ ├── template-engine.ts Template variable expansion +│ ├── distribution-rules.ts Filter query evaluation +│ ├── settings-tab.ts Settings UI +│ └── types.ts Shared TypeScript types +├── test/ Vitest unit tests ├── supabase/ │ ├── config.toml Local dev config │ ├── functions/ diff --git a/plugin/esbuild.config.mjs b/esbuild.config.mjs similarity index 74% rename from plugin/esbuild.config.mjs rename to esbuild.config.mjs index 71bfb9a..e921eb8 100644 --- a/plugin/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -1,6 +1,5 @@ import esbuild from "esbuild"; import process from "process"; -import { copyFileSync } from "node:fs"; import builtins from "builtin-modules"; const banner = `/* @@ -11,19 +10,6 @@ if you want to view the source, please visit the github repository of this plugi const isProduction = process.argv[2] === "production"; -const filesToMirror = ["manifest.json", "versions.json"]; - -const mirrorRootFilesPlugin = { - name: "mirror-root-files", - setup(build) { - build.onEnd(() => { - for (const file of filesToMirror) { - copyFileSync(`../${file}`, file); - } - }); - }, -}; - const context = await esbuild.context({ banner: { js: banner }, entryPoints: ["src/main.ts"], @@ -50,7 +36,6 @@ const context = await esbuild.context({ sourcemap: isProduction ? false : "inline", treeShaking: true, outfile: "main.js", - plugins: [mirrorRootFilesPlugin], }); if (isProduction) { diff --git a/plugin/package-lock.json b/package-lock.json similarity index 99% rename from plugin/package-lock.json rename to package-lock.json index 65f32d9..92397b1 100644 --- a/plugin/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "obsidian-telegram-plugin", + "name": "telegram-bridge", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "obsidian-telegram-plugin", + "name": "telegram-bridge", "version": "0.1.0", "dependencies": { "@supabase/supabase-js": "^2.49.8" @@ -17,6 +17,9 @@ "obsidian": "latest", "typescript": "^5.8.2", "vitest": "^3.0.8" + }, + "engines": { + "node": ">=20" } }, "node_modules/@codemirror/state": { @@ -498,7 +501,8 @@ "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.59.0", @@ -1179,7 +1183,8 @@ "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/debug": { "version": "4.4.3", @@ -1425,7 +1430,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -1556,7 +1560,8 @@ "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/tinybench": { "version": "2.9.0", @@ -2305,7 +2310,8 @@ "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/why-is-node-running": { "version": "2.3.0", diff --git a/package.json b/package.json index ceddd70..1ee42bd 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,28 @@ { "name": "telegram-bridge", "version": "0.1.0", - "private": true, - "description": "Monorepo for the Telegram Bridge Obsidian plugin and Supabase backend", + "description": "Sync messages from Telegram into your vault through a self-hosted Supabase backend.", + "main": "main.js", "scripts": { + "dev": "node esbuild.config.mjs", + "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", + "test": "vitest run", + "test:watch": "vitest", "verify": "node scripts/verify-local-env.mjs", - "bootstrap:supabase": "node scripts/bootstrap-supabase.mjs", - "build:plugin": "npm run build --prefix plugin", - "dev:plugin": "npm run dev --prefix plugin", - "test:plugin": "npm run test --prefix plugin" + "bootstrap:supabase": "node scripts/bootstrap-supabase.mjs" }, "engines": { "node": ">=20" + }, + "devDependencies": { + "@types/node": "^20.17.30", + "builtin-modules": "^3.3.0", + "esbuild": "^0.25.1", + "obsidian": "latest", + "typescript": "^5.8.2", + "vitest": "^3.0.8" + }, + "dependencies": { + "@supabase/supabase-js": "^2.49.8" } } diff --git a/plugin/package.json b/plugin/package.json deleted file mode 100644 index 241184a..0000000 --- a/plugin/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "telegram-bridge-plugin", - "version": "0.1.0", - "description": "Telegram Bridge Obsidian plugin backed by Supabase", - "main": "main.js", - "scripts": { - "dev": "node esbuild.config.mjs", - "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", - "test": "vitest run", - "test:watch": "vitest" - }, - "devDependencies": { - "@types/node": "^20.17.30", - "builtin-modules": "^3.3.0", - "esbuild": "^0.25.1", - "obsidian": "latest", - "typescript": "^5.8.2", - "vitest": "^3.0.8" - }, - "dependencies": { - "@supabase/supabase-js": "^2.49.8" - } -} diff --git a/scripts/bootstrap-supabase.mjs b/scripts/bootstrap-supabase.mjs index f97c9b6..82e999a 100644 --- a/scripts/bootstrap-supabase.mjs +++ b/scripts/bootstrap-supabase.mjs @@ -51,8 +51,8 @@ function main() { console.log("supabase secrets set SUPABASE_ANON_KEY= SUPABASE_SERVICE_ROLE_KEY="); console.log(""); console.log("After deployment:"); - console.log("1. Install plugin dependencies with: npm install --prefix plugin"); - console.log("2. Build the plugin with: npm run build:plugin"); + console.log("1. Install plugin dependencies with: npm install"); + console.log("2. Build the plugin with: npm run build"); console.log("3. Open Obsidian and complete bot setup in the plugin settings."); } diff --git a/scripts/verify-local-env.mjs b/scripts/verify-local-env.mjs index 321bf53..2a303e9 100644 --- a/scripts/verify-local-env.mjs +++ b/scripts/verify-local-env.mjs @@ -5,8 +5,8 @@ import { constants } from "node:fs"; import { spawnSync } from "node:child_process"; const requiredFiles = [ - "plugin/manifest.json", - "plugin/package.json", + "manifest.json", + "package.json", "supabase/config.toml", "supabase/migrations/202603030001_initial_schema.sql", ]; diff --git a/plugin/src/distribution-rules.ts b/src/distribution-rules.ts similarity index 100% rename from plugin/src/distribution-rules.ts rename to src/distribution-rules.ts diff --git a/plugin/src/main.ts b/src/main.ts similarity index 97% rename from plugin/src/main.ts rename to src/main.ts index 051d26b..9b6c37c 100644 --- a/plugin/src/main.ts +++ b/src/main.ts @@ -1,4 +1,4 @@ -import { Notice, Platform, Plugin } from "obsidian"; +import { Notice, Platform, Plugin, requestUrl } from "obsidian"; import { createDefaultDistributionRule, DEFAULT_SETTINGS, @@ -234,7 +234,8 @@ export default class ObsidianTelegramPlugin extends Plugin { throw new Error("Sign in before setting up the Telegram bot."); } - const response = await fetch(`${this.settings.supabase_url}/functions/v1/setup-bot`, { + const response = await requestUrl({ + url: `${this.settings.supabase_url}/functions/v1/setup-bot`, method: "POST", headers: { "Content-Type": "application/json", @@ -242,13 +243,17 @@ export default class ObsidianTelegramPlugin extends Plugin { apikey: this.settings.supabase_anon_key, }, body: JSON.stringify({ bot_token: botToken }), + throw: false, }); - const payload = (await response.json().catch(() => null)) as - | { error?: string; bot_username?: string; webhook_url?: string } - | null; + let payload: { error?: string; bot_username?: string; webhook_url?: string } | null = null; + try { + payload = response.json as { error?: string; bot_username?: string; webhook_url?: string }; + } catch { + payload = null; + } - if (!response.ok) { + if (response.status < 200 || response.status >= 300) { throw new Error(payload?.error ?? `Bot setup failed with status ${response.status}.`); } diff --git a/plugin/src/message-renderer.ts b/src/message-renderer.ts similarity index 100% rename from plugin/src/message-renderer.ts rename to src/message-renderer.ts diff --git a/plugin/src/settings-tab.ts b/src/settings-tab.ts similarity index 100% rename from plugin/src/settings-tab.ts rename to src/settings-tab.ts diff --git a/plugin/src/status-indicator.ts b/src/status-indicator.ts similarity index 100% rename from plugin/src/status-indicator.ts rename to src/status-indicator.ts diff --git a/plugin/src/supabase-client.ts b/src/supabase-client.ts similarity index 100% rename from plugin/src/supabase-client.ts rename to src/supabase-client.ts diff --git a/plugin/src/sync-engine.ts b/src/sync-engine.ts similarity index 100% rename from plugin/src/sync-engine.ts rename to src/sync-engine.ts diff --git a/plugin/src/template-engine.ts b/src/template-engine.ts similarity index 100% rename from plugin/src/template-engine.ts rename to src/template-engine.ts diff --git a/plugin/src/types.ts b/src/types.ts similarity index 100% rename from plugin/src/types.ts rename to src/types.ts diff --git a/plugin/src/vault-writer.ts b/src/vault-writer.ts similarity index 100% rename from plugin/src/vault-writer.ts rename to src/vault-writer.ts diff --git a/plugin/styles.css b/styles.css similarity index 100% rename from plugin/styles.css rename to styles.css diff --git a/plugin/test/distribution-rules.test.ts b/test/distribution-rules.test.ts similarity index 100% rename from plugin/test/distribution-rules.test.ts rename to test/distribution-rules.test.ts diff --git a/plugin/test/template-engine.test.ts b/test/template-engine.test.ts similarity index 100% rename from plugin/test/template-engine.test.ts rename to test/template-engine.test.ts diff --git a/plugin/tsconfig.json b/tsconfig.json similarity index 100% rename from plugin/tsconfig.json rename to tsconfig.json