From 40f4edd363b74fa06d9839442012d123da9e2155 Mon Sep 17 00:00:00 2001 From: matcha Date: Sun, 4 Oct 2026 02:45:08 +0000 Subject: [PATCH] Consolidate Persimmons source --- extension/connection.mjs | 78 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 extension/connection.mjs diff --git a/extension/connection.mjs b/extension/connection.mjs new file mode 100644 index 0000000..d2d8c3a --- /dev/null +++ b/extension/connection.mjs @@ -0,0 +1,78 @@ +import {runJuice, juiceRequest, giteaToken} from './juice.mjs'; +import {vaultUnlocked, vaultUsername, useVaultCredential} from './vault-worker.mjs'; +import {CONNECTION} from './connection-state.mjs'; +import {vaultRequest} from './human-api.mjs'; + +function trustedPage(message, sender, type) { + return sender.origin === 'https://persimmons.app' && + new URL(sender.url).origin === sender.origin && sender.frameId === 0 && + message?.type === type; +} + +export async function connectionStatus(message, sender) { + if (!trustedPage(message, sender, 'persimmons.connection.v1')) return null; + const result = {connected: false, version: chrome.runtime.getManifest().version}; + if (!/^[a-z0-9][a-z0-9_.-]{0,99}$/.test(message.owner)) return result; + const hint = (await chrome.storage.local.get(CONNECTION))[CONNECTION]; + if (hint?.owner !== message.owner || + !await chrome.permissions.contains({origins: ['https://persimmons.app/*']})) return result; + try { + const data = await vaultRequest('GET', hint.owner); + const storageId = `human-vault:https://persimmons.app:${hint.owner}`; + result.connected = true; + result.initialized = !!(await chrome.storage.local.get(storageId))[storageId]; + result.unlocked = vaultUnlocked(hint.owner); + result.empty = data.items.length === 0; + } catch {} + return result; +} +export async function vaultInventory(message, sender) { + if (!trustedPage(message, sender, 'persimmons.inventory.v1')) return null; + if (!/^[a-z0-9][a-z0-9_.-]{0,99}$/.test(message.owner)) return null; + const hint = (await chrome.storage.local.get(CONNECTION))[CONNECTION]; + if (hint?.owner !== message.owner || !vaultUnlocked(message.owner) || + !await chrome.permissions.contains({origins: ['https://persimmons.app/*']})) return null; + try { + const data = await vaultRequest('GET', hint.owner); + const items = []; + for (const metadata of data.items) { + const full = await vaultRequest('GET', hint.owner, metadata.id); + const username = await vaultUsername(hint.owner, full.item); + if (typeof username === 'string' && username) { + items.push({id: metadata.id, origin: metadata.origin, username}); + } + } + return {items}; + } catch { + return null; + } +} + +async function liveJuice(owner) { + const hint = (await chrome.storage.local.get(CONNECTION))[CONNECTION]; + if (hint?.owner !== owner || !await chrome.permissions.contains({origins: ['https://persimmons.app/*']})) throw new Error(); + await vaultRequest('GET', owner); +} +let juiceBusy = false; +export async function juiceMessage(message, sender) { + if (juiceBusy) return {ok: false, message: 'A Juice operation is already running.'}; + juiceBusy = true; + try { + return await runJuice(message, sender, { + live: liveJuice, + unlocked: async owner => { + if (!vaultUnlocked(owner) || !(await chrome.storage.local.get(`human-vault:https://persimmons.app:${owner}`))[`human-vault:https://persimmons.app:${owner}`]) throw new Error(); + }, + source: async (owner, id) => (await vaultRequest('GET', owner, id)).item, + use: useVaultCredential, request: juiceRequest, provider: giteaToken, + id: () => crypto.randomUUID().replaceAll('-', '') + }); + } finally { juiceBusy = false; } +} + +// External responses contain only status, operation IDs and safe username metadata. +chrome.runtime.onMessageExternal.addListener((message, sender, reply) => { + const work = message?.type === 'persimmons.juice.v1' ? juiceMessage(message, sender) : message?.type === 'persimmons.inventory.v1' ? vaultInventory(message, sender) : connectionStatus(message, sender); + work.then(reply, () => reply(null)); + return true; +});