lumencode 0.4.3

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/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "lumencode",
3
+ "version": "0.4.3",
4
+ "description": "LumenCode — AI 编码助手使用报告工具,从 JSONL 日志和 Git 仓库提取 AI 贡献度、效率与使用指标,支持 Web 可视化和命令行两种模式",
5
+ "type": "module",
6
+ "bin": {
7
+ "lumencode": "index.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node index.js",
11
+ "test": "node --test test/*.test.js"
12
+ },
13
+ "keywords": [
14
+ "lumencode",
15
+ "ai-coding",
16
+ "usage-analytics",
17
+ "claude-code",
18
+ "codex",
19
+ "opencode",
20
+ "git-insights",
21
+ "weekly-report",
22
+ "cli",
23
+ "visualization"
24
+ ],
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/yaowen51888-rich/lumencode.git"
28
+ },
29
+ "license": "MIT",
30
+ "author": "zhangyaowen",
31
+ "files": [
32
+ "lib/",
33
+ "public/",
34
+ "data/pricing.json",
35
+ "index.js",
36
+ "README.md"
37
+ ],
38
+ "engines": {
39
+ "node": ">=18.0.0"
40
+ },
41
+ "dependencies": {
42
+ "sql.js": "^1.14.1"
43
+ }
44
+ }
package/public/api.js ADDED
@@ -0,0 +1,109 @@
1
+ import { API } from './config.js';
2
+
3
+ // ── Request Guard ──
4
+ export function createLatestRequestGuard() {
5
+ let currentId = 0;
6
+ let currentController = null;
7
+
8
+ return {
9
+ next() {
10
+ currentId += 1;
11
+ if (currentController) currentController.abort();
12
+
13
+ const id = currentId;
14
+ currentController = new AbortController();
15
+
16
+ return {
17
+ signal: currentController.signal,
18
+ isCurrent() {
19
+ return id === currentId;
20
+ },
21
+ };
22
+ },
23
+ };
24
+ }
25
+
26
+ // ── Cached Fetch ──
27
+ const cache = new Map();
28
+ const pending = new Map();
29
+ const DEFAULT_TTL = 30_000;
30
+
31
+ export function clearApiCache() {
32
+ cache.clear();
33
+ pending.clear();
34
+ }
35
+
36
+ export async function cachedFetch(url, options = {}, ttl = DEFAULT_TTL) {
37
+ const key = `${url}`;
38
+
39
+ // 去重:同一 URL 100ms 内返回同一 Promise
40
+ if (pending.has(key)) return pending.get(key);
41
+
42
+ // 缓存命中
43
+ const cached = cache.get(key);
44
+ if (cached && Date.now() < cached.expire) return cached.data;
45
+
46
+ const promise = (async () => {
47
+ try {
48
+ const res = await fetch(url, options);
49
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
50
+ const data = await res.json();
51
+ cache.set(key, { data, expire: Date.now() + ttl });
52
+ return data;
53
+ } finally {
54
+ pending.delete(key);
55
+ }
56
+ })();
57
+
58
+ pending.set(key, promise);
59
+ return promise;
60
+ }
61
+
62
+ // ── API Functions ──
63
+
64
+ export async function fetchTools() {
65
+ const res = await fetch(API.TOOLS);
66
+ return res.json();
67
+ }
68
+
69
+ export async function fetchReport(params, signal) {
70
+ const url = `${API.REPORT}?${new URLSearchParams(params).toString()}`;
71
+ const res = await fetch(url, { signal });
72
+ return res.json();
73
+ }
74
+
75
+ export async function fetchConfig() {
76
+ const res = await fetch(API.CONFIG);
77
+ if (!res.ok) throw new Error('Failed to fetch config');
78
+ return res.json();
79
+ }
80
+
81
+ export async function saveConfig(cfg) {
82
+ const res = await fetch(API.CONFIG, {
83
+ method: 'POST',
84
+ headers: { 'Content-Type': 'application/json' },
85
+ body: JSON.stringify(cfg),
86
+ });
87
+ if (!res.ok) {
88
+ const err = await res.json().catch(() => ({}));
89
+ throw new Error(err.error || '保存失败');
90
+ }
91
+ return res.json();
92
+ }
93
+
94
+ export async function fetchDetails(params) {
95
+ const qs = new URLSearchParams(params).toString();
96
+ return cachedFetch(`${API.DETAILS}?${qs}`);
97
+ }
98
+
99
+ export async function fetchSessions(params) {
100
+ const qs = new URLSearchParams(params).toString();
101
+ return cachedFetch(`${API.SESSIONS}?${qs}`);
102
+ }
103
+
104
+ export async function fetchWorkReport(params) {
105
+ const qs = new URLSearchParams(params).toString();
106
+ const res = await fetch(`${API.REPORT}?${qs}&format=work`);
107
+ if (!res.ok) throw new Error('Failed to fetch work report');
108
+ return res.text();
109
+ }