roast-api 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/api/client.js ADDED
@@ -0,0 +1,94 @@
1
+ const RaaS = (() => {
2
+ const BASE_URL = 'https://maijied.github.io/roast-as-a-service/api';
3
+ const CACHE_TTL_MS = 1000 * 60 * 60 * 24; // 24h
4
+
5
+ let manifestCache = null;
6
+
7
+ async function fetchJSON(url) {
8
+ const res = await fetch(url, { cache: 'force-cache' });
9
+ if (!res.ok) throw new Error('Network error');
10
+ return res.json();
11
+ }
12
+
13
+ async function getManifest() {
14
+ if (manifestCache) return manifestCache;
15
+ const data = await fetchJSON(`${BASE_URL}/manifest.json`);
16
+ manifestCache = data;
17
+ return data;
18
+ }
19
+
20
+ function getLocalCacheKey(lang, shard) {
21
+ return `raas-${lang}-${shard}`;
22
+ }
23
+
24
+ function readLocalCache(lang, shard) {
25
+ const key = getLocalCacheKey(lang, shard);
26
+ const raw = localStorage.getItem(key);
27
+ if (!raw) return null;
28
+ try {
29
+ const parsed = JSON.parse(raw);
30
+ if (Date.now() - parsed.ts > CACHE_TTL_MS) {
31
+ localStorage.removeItem(key);
32
+ return null;
33
+ }
34
+ return parsed.data;
35
+ } catch {
36
+ localStorage.removeItem(key);
37
+ return null;
38
+ }
39
+ }
40
+
41
+ function writeLocalCache(lang, shard, data) {
42
+ const key = getLocalCacheKey(lang, shard);
43
+ localStorage.setItem(key, JSON.stringify({ ts: Date.now(), data }));
44
+ }
45
+
46
+ function pickRandomIndex(max) {
47
+ return Math.floor(Math.random() * max);
48
+ }
49
+
50
+ function filterRoasts(list, options) {
51
+ if (!options) return list;
52
+ let result = list;
53
+ if (options.intensity) {
54
+ result = result.filter(r => r.intensity === options.intensity);
55
+ }
56
+ if (options.maxLength) {
57
+ result = result.filter(r => r.length <= options.maxLength);
58
+ }
59
+ return result.length ? result : list;
60
+ }
61
+
62
+ async function getRandomRoast(options = {}) {
63
+ const lang = options.lang || 'en';
64
+ const manifest = await getManifest();
65
+ const langInfo = manifest.languages[lang];
66
+ if (!langInfo) throw new Error('Language not supported');
67
+
68
+ const shardCount = langInfo.shards;
69
+ const shard = 1 + pickRandomIndex(shardCount);
70
+
71
+ let shardData = readLocalCache(lang, shard);
72
+ if (!shardData) {
73
+ shardData = await fetchJSON(`${BASE_URL}/${lang}/roasts-${lang}-${shard}.json`);
74
+ writeLocalCache(lang, shard, shardData);
75
+ }
76
+
77
+ const list = filterRoasts(shardData.roasts, options);
78
+ const idx = pickRandomIndex(list.length);
79
+ const roast = list[idx];
80
+
81
+ return {
82
+ language: shardData.language,
83
+ shard: shardData.shard,
84
+ text: roast.text,
85
+ intensity: roast.intensity,
86
+ length: roast.length
87
+ };
88
+ }
89
+
90
+ return {
91
+ getRandomRoast,
92
+ getManifest
93
+ };
94
+ })();