PolyTools Trade Cost 0.1.1 / src/background.js

56 lines · 2,235 bytes · SHA-256 88aad779e0828adbdb0965c832007a9fff8c06983344c676434a58247c7b7f31

1/* PolyTools — background service worker: cached API proxy for content scripts + tab helpers. */
2'use strict';
3
4importScripts('flavor.js', 'lib/journal-stats.js', 'lib/api.js');
5
6const { api, flavor } = self.PWL;
7
8const TTL = { event: 60000, book: 900 };
9const cache = new Map(); // key → { at, promise }
10
11function cached(key, ttl, fn) {
12 const hit = cache.get(key);
13 const now = Date.now();
14 if (hit && now - hit.at < ttl) return hit.promise;
15 const promise = fn().catch((e) => { cache.delete(key); throw e; });
16 cache.set(key, { at: now, promise });
17 if (cache.size > 300) { // drop the oldest entries
18 const keys = [...cache.keys()].slice(0, cache.size - 200);
19 for (const k of keys) cache.delete(k);
20 }
21 return promise;
22}
23
24const handlers = {
25 async event({ slug }) {
26 if (!flavor.cost) throw new Error('no cost panel in this build');
27 if (!slug || typeof slug !== 'string' || slug.length > 300) throw new Error('bad slug');
28 return cached('ev:' + slug, TTL.event, () => api.eventBySlug(slug));
29 },
30 async book({ tokenId }) {
31 if (!flavor.cost) throw new Error('no cost panel in this build');
32 if (!/^\d{5,90}$/.test(String(tokenId || ''))) throw new Error('bad token id');
33 return cached('bk:' + tokenId, TTL.book, () => api.book(tokenId));
34 },
35 async openJournal({ address, user }) {
36 if (!flavor.journal) throw new Error('no journal in this build');
37 const q = address && /^0x[0-9a-fA-F]{40}$/.test(address) ? 'address=' + address
38 : user ? 'user=' + encodeURIComponent(String(user).slice(0, 80)) : '';
39 await chrome.tabs.create({ url: chrome.runtime.getURL('src/journal/journal.html') + (q ? '?' + q : '') });
40 return true;
41 },
42 async openOptions() {
43 await chrome.runtime.openOptionsPage();
44 return true;
45 },
46};
47
48chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
49 const h = msg && typeof msg.type === 'string' && Object.hasOwn(handlers, msg.type) ? handlers[msg.type] : null;
50 if (!h) return false;
51 h(msg).then(
52 (data) => sendResponse({ ok: true, data }),
53 (e) => sendResponse({ ok: false, error: e && e.message ? e.message : String(e), status: e && e.status }),
54 );
55 return true; // async response
56});