UNPKG

@feelback/js

Version:

Client side js integration for Feelback service

219 lines (215 loc) 6.29 kB
// src/content.ts function calculateContentKey(keyOrMode, url) { if (!keyOrMode || keyOrMode === "$auto") { return url?.toString() || (typeof window !== "undefined" ? window.location.href : "/"); } if (keyOrMode === "$path") { if (typeof url === "string") url = new URL(url); const loc = url || (typeof window !== "undefined" ? window.location : void 0); if (!loc) return "/"; return `${loc.origin}${loc.pathname}`; } return keyOrMode; } // src/store.ts var STORAGE_KEY = "fbs-store"; function calculateLocalKey(target) { return "contentId" in target ? target.contentId : `${target.contentSetId}/${calculateContentKey(target.key)}`; } var FeelbackStore = class { constructor(type) { this.feelbacks = void 0; type ??= "local"; if (typeof window === "undefined") { type = "memory"; } if (type === "local") { this.storage = window.localStorage; } else if (type === "session") { this.storage = window.sessionStorage; } else { const NOOP = () => { }; this.storage = { getItem: NOOP, setItem: NOOP, removeItem: NOOP, clear: NOOP, key: NOOP, length: 0 }; } this.load(); } add(data) { const key = calculateLocalKey(data.target); const idx = (this.feelbacks ??= []).findIndex((x) => x.key === key); if (idx >= 0) this.feelbacks.splice(idx, 1); this.feelbacks.push({ key, value: data.value, expire: data.expireIn && data.expireIn > 0 ? Math.floor(Date.now() / 1e3) + data.expireIn : void 0, feelbackId: data.feelbackId, revokeToken: data.revokable?.token, revokeExpire: data.revokable?.expireAt && Math.floor(new Date(data.revokable.expireAt).getTime() / 1e3) || void 0 }); this.save(); } clear() { this.feelbacks?.splice(0, this.feelbacks.length); this.storage.removeItem(STORAGE_KEY); } remove(key) { const idx = typeof key === "string" ? this.feelbacks?.findIndex((x) => x.feelbackId === key) : (key = calculateLocalKey(key), this.feelbacks?.findIndex((x) => x.key === key)); if (idx !== void 0 && idx >= 0) { this.feelbacks.splice(idx, 1); this.save(); } } getValue(target) { return this.getFeelback(target)?.value; } isRevokable(target) { return !!this.getRevocable(target); } getRevocable(key) { const entry = this.getFeelback(key); if (!entry) return; if (!entry.revokeToken) return; if (entry.revokeExpire && entry.revokeExpire < Date.now() / 1e3) return; return { feelbackId: entry.feelbackId, revokeToken: entry.revokeToken }; } load(refresh) { if (this.feelbacks && !refresh) { return; } let entries; try { entries = JSON.parse(this.storage.getItem(STORAGE_KEY)) || []; } catch { entries = []; } this.feelbacks = entries.filter((x) => !x.expire || x.expire > Date.now() / 1e3); if (entries.length !== this.feelbacks.length) { this.save(); } } save() { try { this.storage.setItem(STORAGE_KEY, JSON.stringify(this.feelbacks)); } catch { } } getFeelback(key) { const item = typeof key === "string" ? this.feelbacks?.find((x) => x.feelbackId === key) : (key = calculateLocalKey(key), this.feelbacks?.find((x) => x.key === key)); if (item && item.expire && item.expire < Date.now() / 1e3) { this.remove(item.feelbackId); return; } return item; } }; var store; var storeType; function getFeelbackStore(type) { type ??= storeType || "local"; if (store && storeType === type) { return store; } storeType = type; return store = new FeelbackStore(type); } function getLocalFeelbackValue(target, store2) { return getFeelbackStore(store2).getValue(target); } // src/http.ts async function get(url, ...params) { if (params.length > 0) { url = `${url}?$p=${JSON.stringify(params)}`; } return await getResponse(fetch(url, { method: "GET" })); } async function post(url, ...params) { return await getResponse(fetch(url, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(params) })); } async function getResponse(response) { response = await response; if (response.status >= 400) { throw new Error("[feelback] API error"); } if (response.status === 204) { return; } return await response.json(); } var http_default = { get, post }; // src/feelback.ts var ENDPOINT = "https://api.feelback.dev/v0"; async function sendFeelback(params) { const { endpoint = ENDPOINT, store: store2 = "local", revokable = true, value, metadata, expireIn = 3600 // 1h } = params; const target = "contentId" in params ? { contentId: params.contentId } : { contentSetId: params.contentSetId, key: calculateContentKey(params.key) }; const storage = store2 && store2 !== "none" && getFeelbackStore(store2) || void 0; const revoke = revokable && storage?.getRevocable(target) || void 0; const result = revoke ? await http_default.post(`${endpoint}/feelbacks/edit`, { ...revoke, value }) : await http_default.post(`${endpoint}/feelbacks/create`, { ...target, value, context: metadata, revokable }); storage?.add({ ...result, target, value, expireIn }); } async function removeFeelback(options) { const { endpoint = ENDPOINT, feelbackId } = options; let revokeToken = options.revokeToken; const storage = getFeelbackStore(); if (!revokeToken) { const revoke = storage.getRevocable(feelbackId); if (!revoke) { throw new Error("Cannot revoke feelback"); } revokeToken = revoke.revokeToken; } await http_default.post(`${endpoint}/feelbacks/remove`, { feelbackId, revokeToken }); storage.remove(feelbackId); } async function getFeelbackAggregates(params) { const { endpoint = ENDPOINT } = params; const target = "contentId" in params ? params.contentId : { contentSetId: params.contentSetId, key: calculateContentKey(params.key) }; return await http_default.get(`${endpoint}/feelbacks/getAggregates`, target); } export { calculateContentKey, getFeelbackAggregates, getFeelbackStore, getLocalFeelbackValue, removeFeelback, sendFeelback };