oddmisc 1.1.1 → 1.1.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/README.md CHANGED
@@ -1,133 +1,124 @@
1
- # oddmisc
2
-
3
- 杂七杂八奇怪小工具 npm 包
4
-
5
- [![npm version](https://img.shields.io/npm/v/oddmisc.svg)](https://www.npmjs.com/package/oddmisc)
6
- [![License](https://img.shields.io/npm/l/oddmisc.svg)](https://github.com/yCENzh/oddmisc/blob/main/LICENSE)
7
-
8
- ## 安装
9
-
10
- ```bash
11
- npm install oddmisc
12
- # 或
13
- pnpm add oddmisc
14
- # 或
15
- yarn add oddmisc
16
- ```
17
-
18
- ## 快速开始
19
-
20
- ### 基本使用
21
-
22
- ```javascript
23
- import { createUmamiClient } from 'oddmisc';
24
-
25
- // 创建客户端
26
- const client = createUmamiClient({
27
- shareUrl: 'https://your-umami-instance.com/share/your-share-id'
28
- });
29
-
30
- // 获取页面访问统计
31
- const stats = await client.getPageStats('/about');
32
- console.log(`页面浏览量: ${stats.pageviews}, 访客数: ${stats.visitors}`);
33
-
34
- // 获取网站整体统计
35
- const siteStats = await client.getSiteStats();
36
- console.log(`总浏览量: ${siteStats.pageviews}, 总访客数: ${siteStats.visitors}`);
37
- ```
38
-
39
- ### Astro 集成
40
-
41
- 在 `astro.config.mjs` 中配置:
42
-
43
- ```javascript
44
- import { defineConfig } from 'astro/config';
45
- import { umami } from 'oddmisc';
46
-
47
- export default defineConfig({
48
- integrations: [
49
- umami({
50
- shareUrl: 'https://your-umami-instance.com/share/your-share-id',
51
- timezone: 'Asia/Shanghai',
52
- enableCache: true,
53
- cacheTTL: 3600000 // 1小时
54
- })
55
- ]
56
- });
57
- ```
58
-
59
- 然后在前端代码中使用:
60
-
61
- ```javascript
62
- // 全局可用
63
- const stats = await window.oddmisc.getStats('/some-page');
64
- const siteStats = await window.oddmisc.getSiteStats();
65
- ```
66
-
67
- ## API 参考
68
-
69
- ### UmamiClient
70
-
71
- #### 构造函数
72
- ```javascript
73
- const client = createUmamiClient(config);
74
- ```
75
-
76
- #### 配置选项
77
- ```javascript
78
- const config = {
79
- shareUrl: 'https://umami.example.com/share/abc123', // 必需
80
- timezone: 'Asia/Shanghai', // 可选,默认 'Asia/Shanghai'
81
- enableCache: true, // 可选,默认 true
82
- cacheTTL: 3600000 // 可选,默认 1小时(毫秒)
83
- };
84
- ```
85
-
86
- #### 方法
87
-
88
- - `getPageStats(path, options)` - 获取指定页面统计
89
- - `getPageStatsByUrl(url, options)` - 通过 URL 获取页面统计
90
- - `getSiteStats(options)` - 获取网站整体统计
91
- - `clearCache()` - 清除缓存
92
- - `getConfig()` - 获取当前配置
93
- - `updateConfig(newConfig)` - 更新配置
94
-
95
- ### Astro 集成
96
-
97
- ```javascript
98
- umami(options: UmamiIntegrationOptions)
99
- ```
100
-
101
- 选项:
102
- ```javascript
103
- interface UmamiIntegrationOptions {
104
- shareUrl: string; // Umami 分享链接
105
- timezone?: string; // 时区,默认 'Asia/Shanghai'
106
- enableCache?: boolean; // 启用缓存,默认 true
107
- cacheTTL?: number; // 缓存时间,单位毫秒
108
- }
109
- ```
110
-
111
- ## 支持的 Umami URL 格式
112
-
113
- - `https://umami.example.com/share/abc123`
114
- - `https://cloud.umami.is/analytics/us/share/abc123`
115
- - `https://umami.example.com/analytics/share/abc123`
116
-
117
- ## 浏览器兼容性
118
-
119
- - 现代浏览器(Chrome 60+, Firefox 60+, Safari 12+)
120
- - 支持 localStorage 的环境
121
-
122
- ## License
123
-
124
- MIT © yCENzh
125
-
126
- ## 贡献
127
-
128
- 欢迎提交 Issue 和 Pull Request!
129
-
130
- ## 相关链接
131
-
132
- - [Umami 官网](https://umami.is/)
1
+ # oddmisc
2
+
3
+ 杂七杂八奇怪小工具 npm 包
4
+
5
+ [![npm version](https://img.shields.io/npm/v/oddmisc.svg)](https://www.npmjs.com/package/oddmisc)
6
+ [![License](https://img.shields.io/npm/l/oddmisc.svg)](https://github.com/yCENzh/oddmisc/blob/main/LICENSE)
7
+
8
+ ## 安装
9
+
10
+ ```bash
11
+ npm install oddmisc
12
+ # 或
13
+ pnpm add oddmisc
14
+ # 或
15
+ yarn add oddmisc
16
+ ```
17
+
18
+ ## 快速开始
19
+
20
+ ### 基本使用
21
+
22
+ ```javascript
23
+ import { createUmamiClient } from 'oddmisc';
24
+
25
+ // 创建客户端
26
+ const client = createUmamiClient({
27
+ shareUrl: 'https://your-umami-instance.com/share/your-share-id'
28
+ });
29
+
30
+ // 获取页面访问统计
31
+ const stats = await client.getPageStats('/about');
32
+ console.log(`页面浏览量: ${stats.pageviews}, 访客数: ${stats.visitors}, 访问次数: ${stats.visits}`);
33
+
34
+ // 获取网站整体统计
35
+ const siteStats = await client.getSiteStats();
36
+ console.log(`总浏览量: ${siteStats.pageviews}, 总访客数: ${siteStats.visitors}, 总访问次数: ${siteStats.visits}`);
37
+ ```
38
+
39
+ ### Astro 集成
40
+
41
+ 在 `astro.config.mjs` 中配置:
42
+
43
+ ```javascript
44
+ import { defineConfig } from 'astro/config';
45
+ import { umami } from 'oddmisc';
46
+
47
+ export default defineConfig({
48
+ integrations: [
49
+ umami({
50
+ shareUrl: 'https://your-umami-instance.com/share/your-share-id'
51
+ })
52
+ ]
53
+ });
54
+ ```
55
+
56
+ 然后在前端代码中使用:
57
+
58
+ ```javascript
59
+ // 全局可用
60
+ const stats = await window.oddmisc.getStats('/some-page');
61
+ const siteStats = await window.oddmisc.getSiteStats();
62
+ ```
63
+
64
+ ## API 参考
65
+
66
+ ### UmamiClient
67
+
68
+ #### 构造函数
69
+ ```javascript
70
+ const client = createUmamiClient(config);
71
+ ```
72
+
73
+ #### 配置选项
74
+ ```javascript
75
+ const config = {
76
+ shareUrl: 'https://umami.example.com/share/abc123' // 必需
77
+ };
78
+ ```
79
+
80
+ #### 方法
81
+
82
+ - `getPageStats(path)` - 获取指定页面统计
83
+ - `getPageStatsByUrl(url)` - 通过 URL 获取页面统计
84
+ - `getSiteStats()` - 获取网站整体统计
85
+ - `clearCache()` - 清除缓存
86
+ - `getConfig()` - 获取当前配置
87
+ - `updateConfig(newConfig)` - 更新配置
88
+
89
+ ### Astro 集成
90
+
91
+ ```javascript
92
+ umami(options: UmamiIntegrationOptions)
93
+ ```
94
+
95
+ 选项:
96
+ ```javascript
97
+ interface UmamiIntegrationOptions {
98
+ shareUrl: string; // Umami 分享链接
99
+ }
100
+ ```
101
+
102
+ ## 支持的 Umami URL 格式
103
+
104
+ - `https://umami.example.com/share/abc123`
105
+ - `https://cloud.umami.is/analytics/us/share/abc123`
106
+ - `https://umami.example.com/analytics/share/abc123`
107
+
108
+ ## 浏览器兼容性
109
+
110
+ - 现代浏览器(Chrome 60+, Firefox 60+, Safari 12+)
111
+ - 支持 localStorage 的环境
112
+
113
+ ## License
114
+
115
+ MIT © yCENzh
116
+
117
+ ## 贡献
118
+
119
+ 欢迎提交 Issue Pull Request!
120
+
121
+ ## 相关链接
122
+
123
+ - [Umami 官网](https://umami.is/)
133
124
  - [GitHub 仓库](https://github.com/yCENzh/oddmisc)
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var S=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var _=Object.getOwnPropertyNames;var O=Object.prototype.hasOwnProperty;var B=(r,t)=>{for(var e in t)S(r,e,{get:t[e],enumerable:!0})},T=(r,t,e,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of _(t))!O.call(r,s)&&s!==e&&S(r,s,{get:()=>t[s],enumerable:!(a=b(t,s))||a.enumerable});return r};var L=r=>T(S({},"__esModule",{value:!0}),r);var K={};B(K,{CacheManager:()=>c,UmamiClient:()=>l,UmamiError:()=>p,VERSION:()=>P,createUmamiClient:()=>R,initUmamiRuntime:()=>$,isValidConfig:()=>E,isValidShareUrl:()=>I,parseShareUrl:()=>d,umami:()=>y});module.exports=L(K);var P="1.0.0";function E(r){return typeof r=="object"&&r!==null&&typeof r.baseUrl=="string"&&typeof r.shareId=="string"&&r.baseUrl.length>0&&r.shareId.length>0}var p=class extends Error{constructor(e,a){super(e);this.code=a;this.name="UmamiError"}};var u=typeof window<"u"&&typeof localStorage<"u",c=class{constructor(t="default",e=36e5){this.memoryCache=new Map;this.CACHE_KEY=`cache-${t}`,this.DEFAULT_TTL=e}get(t){if(this.memoryCache.has(t))return this.memoryCache.get(t);if(u)try{let e=localStorage.getItem(this.CACHE_KEY);if(e){let s=JSON.parse(e)[t];if(s&&Date.now()-s.timestamp<this.DEFAULT_TTL)return this.memoryCache.set(t,s.value),s.value}}catch{}return null}set(t,e){if(this.memoryCache.set(t,e),u)try{let a=localStorage.getItem(this.CACHE_KEY),s=a?JSON.parse(a):{};s[t]={timestamp:Date.now(),value:e},localStorage.setItem(this.CACHE_KEY,JSON.stringify(s))}catch{}}clear(){if(this.memoryCache.clear(),u)try{localStorage.removeItem(this.CACHE_KEY)}catch{}}delete(t){if(this.memoryCache.delete(t),u)try{let e=localStorage.getItem(this.CACHE_KEY);if(e){let a=JSON.parse(e);delete a[t],localStorage.setItem(this.CACHE_KEY,JSON.stringify(a))}}catch{}}};var f=class{constructor(t){this.sharePromise=null;this.cacheManager=t}async getShareData(t,e){return this.sharePromise||(this.sharePromise=this.fetchShareData(t,e).catch(a=>{throw this.sharePromise=null,a})),this.sharePromise}async fetchShareData(t,e){let a=await fetch(`${t}/share/${e}`);if(!a.ok)throw new Error(`\u83B7\u53D6\u5206\u4EAB\u4FE1\u606F\u5931\u8D25: ${a.status} ${a.statusText}`);return a.json()}async getStats(t,e,a){let s=`${t}|${e}|${JSON.stringify(a)}`,i=this.cacheManager.get(s);if(i)return{...i,_fromCache:!0};let{websiteId:o,token:n}=await this.getShareData(t,e),h=new URLSearchParams({startAt:"0",endAt:Date.now().toString()});a.path&&h.set("path",a.path);let g=`${t}/websites/${o}/stats?${h.toString()}`,m=await fetch(g,{headers:{"x-umami-share-token":n}});if(!m.ok)throw m.status===401?(this.cacheManager.clear(),new Error("\u8BA4\u8BC1\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5 shareId")):new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${m.status} ${m.statusText}`);let U=await m.json();return this.cacheManager.set(s,U),U}clearShareCache(){this.sharePromise=null}};function d(r){try{let t=new URL(r),e=t.pathname.split("/"),a=e.indexOf("share");if(a===-1||a===e.length-1)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB URL\uFF1A\u672A\u627E\u5230 share \u8DEF\u5F84");let s=e[a+1];if(!s||s.length<10)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB ID");let o=e.slice(0,a).join("/")+"/api";return{apiBase:`${t.protocol}//${t.host}${o}`,shareId:s}}catch(t){throw t instanceof Error?new Error(`URL \u89E3\u6790\u5931\u8D25: ${t.message}`):new Error("URL \u89E3\u6790\u5931\u8D25: \u65E0\u6548\u7684 URL \u683C\u5F0F")}}function I(r){try{return new URL(r).pathname.includes("/share/")}catch{return!1}}var l=class{constructor(t){if(!t.shareUrl)throw new Error("shareUrl \u662F\u5FC5\u9700\u53C2\u6570");let{apiBase:e,shareId:a}=d(t.shareUrl);this.config={baseUrl:e,shareId:a,...t},this.cacheManager=new c("umami",36e5),this.api=new f(this.cacheManager)}async getPageStats(t,e={}){if(!this.config.baseUrl||!this.config.shareId)throw new Error("\u5BA2\u6237\u7AEF\u672A\u6B63\u786E\u521D\u59CB\u5316");let a=await this.api.getStats(this.config.baseUrl,this.config.shareId,{path:`eq.${t}`,...e});return{pageviews:a.pageviews?.value||a.pageviews||0,visitors:a.visitors?.value||a.visitors||0,_fromCache:a._fromCache}}async getPageStatsByUrl(t,e={}){if(!this.config.baseUrl||!this.config.shareId)throw new Error("\u5BA2\u6237\u7AEF\u672A\u6B63\u786E\u521D\u59CB\u5316");let a=await this.api.getStats(this.config.baseUrl,this.config.shareId,{url:t,...e});return{pageviews:a.pageviews?.value||a.pageviews||0,visitors:a.visitors?.value||a.visitors||0,_fromCache:a._fromCache}}async getSiteStats(t={}){if(!this.config.baseUrl||!this.config.shareId)throw new Error("\u5BA2\u6237\u7AEF\u672A\u6B63\u786E\u521D\u59CB\u5316");let e=await this.api.getStats(this.config.baseUrl,this.config.shareId,t);return{pageviews:e.pageviews||0,visitors:e.visitors||0,_fromCache:e._fromCache}}clearCache(){this.cacheManager.clear(),this.api.clearShareCache()}getConfig(){return{...this.config}}updateConfig(t){this.config={...this.config,...t}}};function R(r){return new l(r)}var x=require("fs"),D=require("url"),w=require("path"),A={};function y(r){if(!r.shareUrl)throw new Error("[oddmisc] \u9700\u8981\u914D\u7F6E shareUrl");return{name:"oddmisc-umami-integration",hooks:{"astro:config:setup":({injectScript:t})=>{let e="";try{let s=(0,w.dirname)((0,D.fileURLToPath)(A.url)),i=(0,w.join)(s,"./runtime/client.global.js");e=(0,x.readFileSync)(i,"utf-8")}catch{console.warn("[oddmisc] \u65E0\u6CD5\u8BFB\u53D6\u8FD0\u884C\u65F6\u6587\u4EF6\uFF0C\u4F7F\u7528\u5907\u7528\u65B9\u6848")}let a=`
1
+ "use strict";var S=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var _=Object.getOwnPropertyNames;var O=Object.prototype.hasOwnProperty;var B=(r,t)=>{for(var e in t)S(r,e,{get:t[e],enumerable:!0})},T=(r,t,e,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of _(t))!O.call(r,s)&&s!==e&&S(r,s,{get:()=>t[s],enumerable:!(a=b(t,s))||a.enumerable});return r};var L=r=>T(S({},"__esModule",{value:!0}),r);var K={};B(K,{CacheManager:()=>c,UmamiClient:()=>l,UmamiError:()=>p,VERSION:()=>P,createUmamiClient:()=>R,initUmamiRuntime:()=>$,isValidConfig:()=>E,isValidShareUrl:()=>I,parseShareUrl:()=>d,umami:()=>y});module.exports=L(K);var P="1.0.0";function E(r){return typeof r=="object"&&r!==null&&typeof r.baseUrl=="string"&&typeof r.shareId=="string"&&r.baseUrl.length>0&&r.shareId.length>0}var p=class extends Error{constructor(e,a){super(e);this.code=a;this.name="UmamiError"}};var u=typeof window<"u"&&typeof localStorage<"u",c=class{constructor(t="default",e=36e5){this.memoryCache=new Map;this.CACHE_KEY=`cache-${t}`,this.DEFAULT_TTL=e}get(t){if(this.memoryCache.has(t))return this.memoryCache.get(t);if(u)try{let e=localStorage.getItem(this.CACHE_KEY);if(e){let s=JSON.parse(e)[t];if(s&&Date.now()-s.timestamp<this.DEFAULT_TTL)return this.memoryCache.set(t,s.value),s.value}}catch{}return null}set(t,e){if(this.memoryCache.set(t,e),u)try{let a=localStorage.getItem(this.CACHE_KEY),s=a?JSON.parse(a):{};s[t]={timestamp:Date.now(),value:e},localStorage.setItem(this.CACHE_KEY,JSON.stringify(s))}catch{}}clear(){if(this.memoryCache.clear(),u)try{localStorage.removeItem(this.CACHE_KEY)}catch{}}delete(t){if(this.memoryCache.delete(t),u)try{let e=localStorage.getItem(this.CACHE_KEY);if(e){let a=JSON.parse(e);delete a[t],localStorage.setItem(this.CACHE_KEY,JSON.stringify(a))}}catch{}}};var f=class{constructor(t){this.sharePromise=null;this.cacheManager=t}async getShareData(t,e){return this.sharePromise||(this.sharePromise=this.fetchShareData(t,e).catch(a=>{throw this.sharePromise=null,a})),this.sharePromise}async fetchShareData(t,e){let a=await fetch(`${t}/share/${e}`);if(!a.ok)throw new Error(`\u83B7\u53D6\u5206\u4EAB\u4FE1\u606F\u5931\u8D25: ${a.status} ${a.statusText}`);return a.json()}async getStats(t,e,a){let s=`${t}|${e}|${JSON.stringify(a)}`,i=this.cacheManager.get(s);if(i)return{...i,_fromCache:!0};let{websiteId:n,token:h}=await this.getShareData(t,e),o=new URLSearchParams({startAt:"0",endAt:Date.now().toString()});a.path&&o.set("path",a.path);let g=`${t}/websites/${n}/stats?${o.toString()}`,m=await fetch(g,{headers:{"x-umami-share-token":h}});if(!m.ok)throw m.status===401?(this.cacheManager.clear(),new Error("\u8BA4\u8BC1\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5 shareId")):new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${m.status} ${m.statusText}`);let U=await m.json();return this.cacheManager.set(s,U),U}clearShareCache(){this.sharePromise=null}};function d(r){try{let t=new URL(r),e=t.pathname.split("/"),a=e.indexOf("share");if(a===-1||a===e.length-1)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB URL\uFF1A\u672A\u627E\u5230 share \u8DEF\u5F84");let s=e[a+1];if(!s||s.length<10)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB ID");let n=e.slice(0,a).join("/")+"/api";return{apiBase:`${t.protocol}//${t.host}${n}`,shareId:s}}catch(t){throw t instanceof Error?new Error(`URL \u89E3\u6790\u5931\u8D25: ${t.message}`):new Error("URL \u89E3\u6790\u5931\u8D25: \u65E0\u6548\u7684 URL \u683C\u5F0F")}}function I(r){try{return new URL(r).pathname.includes("/share/")}catch{return!1}}var l=class{constructor(t){if(!t.shareUrl)throw new Error("shareUrl \u662F\u5FC5\u9700\u53C2\u6570");let{apiBase:e,shareId:a}=d(t.shareUrl);this.config={baseUrl:e,shareId:a,...t},this.cacheManager=new c("umami",36e5),this.api=new f(this.cacheManager)}async getPageStats(t,e={}){if(!this.config.baseUrl||!this.config.shareId)throw new Error("\u5BA2\u6237\u7AEF\u672A\u6B63\u786E\u521D\u59CB\u5316");let a=await this.api.getStats(this.config.baseUrl,this.config.shareId,{path:`eq.${t}`,...e});return{pageviews:a.pageviews?.value||a.pageviews||0,visitors:a.visitors?.value||a.visitors||0,_fromCache:a._fromCache}}async getPageStatsByUrl(t,e={}){if(!this.config.baseUrl||!this.config.shareId)throw new Error("\u5BA2\u6237\u7AEF\u672A\u6B63\u786E\u521D\u59CB\u5316");let a=await this.api.getStats(this.config.baseUrl,this.config.shareId,{url:t,...e});return{pageviews:a.pageviews?.value||a.pageviews||0,visitors:a.visitors?.value||a.visitors||0,_fromCache:a._fromCache}}async getSiteStats(t={}){if(!this.config.baseUrl||!this.config.shareId)throw new Error("\u5BA2\u6237\u7AEF\u672A\u6B63\u786E\u521D\u59CB\u5316");let e=await this.api.getStats(this.config.baseUrl,this.config.shareId,t);return{pageviews:e.pageviews||0,visitors:e.visitors||0,_fromCache:e._fromCache}}clearCache(){this.cacheManager.clear(),this.api.clearShareCache()}getConfig(){return{...this.config}}updateConfig(t){this.config={...this.config,...t}}};function R(r){return new l(r)}var x=require("fs"),D=require("url"),w=require("path"),A={};function y(r){if(!r.shareUrl)throw new Error("[oddmisc] \u9700\u8981\u914D\u7F6E shareUrl");return{name:"oddmisc-umami-integration",hooks:{"astro:config:setup":({injectScript:t})=>{let e="";try{let s=(0,w.dirname)((0,D.fileURLToPath)(A.url)),i=(0,w.join)(s,"./runtime/client.global.js");e=(0,x.readFileSync)(i,"utf-8")}catch{console.warn("[oddmisc] \u65E0\u6CD5\u8BFB\u53D6\u8FD0\u884C\u65F6\u6587\u4EF6\uFF0C\u4F7F\u7528\u5907\u7528\u65B9\u6848")}let a=`
2
2
  // oddmisc Umami Runtime
3
3
  ${e}
4
4
 
@@ -6,5 +6,5 @@ ${e}
6
6
  if (typeof window !== 'undefined') {
7
7
  __oddmiscRuntime.initUmamiRuntime(${JSON.stringify({shareUrl:r.shareUrl})});
8
8
  }
9
- `;t("page",a)}}}}function M(r){let t=new URL(r),e=t.pathname.split("/"),a=e.indexOf("share");if(a===-1||a===e.length-1)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB URL\uFF1A\u672A\u627E\u5230 share \u8DEF\u5F84");let s=e[a+1];if(!s||s.length<10)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB ID");let o=e.slice(0,a).join("/")+"/api";return{apiBase:`${t.protocol}//${t.host}${o}`,shareId:s}}var v=class{constructor(t,e){this.cache=new Map;this.storageKey=t,this.ttl=e,this.loadFromStorage()}loadFromStorage(){try{let t=localStorage.getItem(this.storageKey);if(t){let e=JSON.parse(t),a=Date.now();for(let[s,i]of Object.entries(e))a-i.timestamp<this.ttl&&this.cache.set(s,i)}}catch{}}saveToStorage(){try{let t={};this.cache.forEach((e,a)=>{t[a]=e}),localStorage.setItem(this.storageKey,JSON.stringify(t))}catch{}}get(t){let e=this.cache.get(t);return e&&Date.now()-e.timestamp<this.ttl?e.value:null}set(t,e){this.cache.set(t,{value:e,timestamp:Date.now()}),this.saveToStorage()}clear(){this.cache.clear();try{localStorage.removeItem(this.storageKey)}catch{}}},C=class{constructor(t){this.shareData=null;this.sharePromise=null;let{apiBase:e,shareId:a}=M(t.shareUrl);this.apiBase=e,this.shareId=a,this.cache=new v(`umami-runtime-${a}`,36e5)}async getShareData(){return this.shareData?this.shareData:this.sharePromise?this.sharePromise:(this.sharePromise=(async()=>{let t=await fetch(`${this.apiBase}/share/${this.shareId}`);if(!t.ok)throw this.sharePromise=null,new Error(`\u83B7\u53D6\u5206\u4EAB\u4FE1\u606F\u5931\u8D25: ${t.status}`);let e=await t.json();return this.shareData=e,e})(),this.sharePromise)}async getStats(t){let e=t?`stats-${t}`:"stats-site",a=this.cache.get(e);if(a)return{...a,_fromCache:!0};let{websiteId:s,token:i}=await this.getShareData(),o=new URLSearchParams({startAt:"0",endAt:Date.now().toString()});t&&o.set("path",`eq.${t}`);let n=await fetch(`${this.apiBase}/websites/${s}/stats?${o.toString()}`,{headers:{"x-umami-share-token":i}});if(!n.ok)throw new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${n.status}`);let h=await n.json(),g={pageviews:h.pageviews?.value??h.pageviews??0,visitors:h.visitors?.value??h.visitors??0};return this.cache.set(e,g),g}async getSiteStats(){return this.getStats()}async getPageStats(t){return this.getStats(t)}clearCache(){this.cache.clear(),this.shareData=null,this.sharePromise=null}};function $(r){let t=new C(r);window.oddmisc=window.oddmisc||{},window.oddmisc.umami=t,window.oddmisc.getStats=e=>t.getStats(e),window.oddmisc.getSiteStats=()=>t.getSiteStats(),window.oddmisc.getPageStats=e=>t.getPageStats(e),window.oddmisc.clearCache=()=>t.clearCache(),console.log("[oddmisc] Umami runtime client initialized")}0&&(module.exports={CacheManager,UmamiClient,UmamiError,VERSION,createUmamiClient,initUmamiRuntime,isValidConfig,isValidShareUrl,parseShareUrl,umami});
9
+ `;t("page",a)}}}}function M(r){let t=new URL(r),e=t.pathname.split("/"),a=e.indexOf("share");if(a===-1||a===e.length-1)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB URL\uFF1A\u672A\u627E\u5230 share \u8DEF\u5F84");let s=e[a+1];if(!s||s.length<10)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB ID");let n=e.slice(0,a).join("/")+"/api";return{apiBase:`${t.protocol}//${t.host}${n}`,shareId:s}}var v=class{constructor(t,e){this.cache=new Map;this.storageKey=t,this.ttl=e,this.loadFromStorage()}loadFromStorage(){try{let t=localStorage.getItem(this.storageKey);if(t){let e=JSON.parse(t),a=Date.now();for(let[s,i]of Object.entries(e))a-i.timestamp<this.ttl&&this.cache.set(s,i)}}catch{}}saveToStorage(){try{let t={};this.cache.forEach((e,a)=>{t[a]=e}),localStorage.setItem(this.storageKey,JSON.stringify(t))}catch{}}get(t){let e=this.cache.get(t);return e&&Date.now()-e.timestamp<this.ttl?e.value:null}set(t,e){this.cache.set(t,{value:e,timestamp:Date.now()}),this.saveToStorage()}clear(){this.cache.clear();try{localStorage.removeItem(this.storageKey)}catch{}}},C=class{constructor(t){this.shareData=null;this.sharePromise=null;let{apiBase:e,shareId:a}=M(t.shareUrl);this.apiBase=e,this.shareId=a,this.cache=new v(`umami-runtime-${a}`,36e5)}async getShareData(){return this.shareData?this.shareData:this.sharePromise?this.sharePromise:(this.sharePromise=(async()=>{let t=await fetch(`${this.apiBase}/share/${this.shareId}`);if(!t.ok)throw this.sharePromise=null,new Error(`\u83B7\u53D6\u5206\u4EAB\u4FE1\u606F\u5931\u8D25: ${t.status}`);let e=await t.json();return this.shareData=e,e})(),this.sharePromise)}async getStats(t){let e=t?`stats-${t}`:"stats-site",a=this.cache.get(e);if(a)return{...a,_fromCache:!0};let{websiteId:s,token:i}=await this.getShareData(),n=new URLSearchParams({startAt:"0",endAt:Date.now().toString()});t&&n.set("path",`eq.${t}`);let h=await fetch(`${this.apiBase}/websites/${s}/stats?${n.toString()}`,{headers:{"x-umami-share-token":i}});if(!h.ok)throw new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${h.status}`);let o=await h.json(),g={pageviews:o.pageviews?.value??o.pageviews??0,visitors:o.visitors?.value??o.visitors??0,visits:o.visits?.value??o.visits??0};return this.cache.set(e,g),g}async getSiteStats(){return this.getStats()}async getPageStats(t){return this.getStats(t)}clearCache(){this.cache.clear(),this.shareData=null,this.sharePromise=null}};function $(r){let t=new C(r);window.oddmisc=window.oddmisc||{},window.oddmisc.umami=t,window.oddmisc.getStats=e=>t.getStats(e),window.oddmisc.getSiteStats=()=>t.getSiteStats(),window.oddmisc.getPageStats=e=>t.getPageStats(e),window.oddmisc.clearCache=()=>t.clearCache(),console.log("[oddmisc] Umami runtime client initialized")}0&&(module.exports={CacheManager,UmamiClient,UmamiError,VERSION,createUmamiClient,initUmamiRuntime,isValidConfig,isValidShareUrl,parseShareUrl,umami});
10
10
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/shared/index.ts","../src/utils/umami/cache.ts","../src/modules/umami/api.ts","../src/utils/umami/url-parser.ts","../src/modules/umami/client.ts","../src/astro/integration.ts","../src/runtime/client.ts"],"sourcesContent":["// 核心导出\nexport { VERSION, isValidConfig, UmamiError } from './shared';\n\n// Umami 模块\nexport { UmamiClient, createUmamiClient } from './modules/umami/client';\nexport type { UmamiConfig, StatsResult, StatsQueryParams } from './modules/umami/types';\n\n// 工具函数\nexport { CacheManager } from './utils/umami/cache';\nexport { parseShareUrl, isValidShareUrl } from './utils/umami/url-parser';\n\n// Astro 集成\nexport { umami } from './astro';\nexport type { UmamiIntegrationOptions } from './astro';\n\n// 运行时客户端(用于手动初始化)\nexport { initUmamiRuntime } from './runtime/client';\nexport type { UmamiRuntimeConfig as RuntimeConfig, StatsResult as RuntimeStatsResult } from './runtime/client';","// 版本信息\nexport const VERSION = '1.0.0';\n\n// 配置验证\nexport function isValidConfig(config: any): boolean {\n return (\n typeof config === 'object' &&\n config !== null &&\n typeof config.baseUrl === 'string' &&\n typeof config.shareId === 'string' &&\n config.baseUrl.length > 0 &&\n config.shareId.length > 0\n );\n}\n\n// 自定义错误类\nexport class UmamiError extends Error {\n constructor(message: string, public code?: string) {\n super(message);\n this.name = 'UmamiError';\n }\n}","// 检测是否在浏览器环境\nconst isBrowser = typeof window !== 'undefined' && typeof localStorage !== 'undefined';\n\n// 通用缓存管理器\nexport class CacheManager<T = any> {\n private memoryCache = new Map<string, T>();\n private readonly CACHE_KEY: string;\n private readonly DEFAULT_TTL: number;\n\n constructor(namespace = 'default', ttl = 3600000) {\n this.CACHE_KEY = `cache-${namespace}`;\n this.DEFAULT_TTL = ttl;\n }\n\n get(key: string): T | null {\n // 内存缓存(SSR 和浏览器都可用)\n if (this.memoryCache.has(key)) {\n return this.memoryCache.get(key)!;\n }\n \n // localStorage 缓存(仅浏览器环境)\n if (isBrowser) {\n try {\n const cached = localStorage.getItem(this.CACHE_KEY);\n if (cached) {\n const parsed = JSON.parse(cached);\n const cachedData = parsed[key];\n if (cachedData && Date.now() - cachedData.timestamp < this.DEFAULT_TTL) {\n // 命中 localStorage 缓存时,同步到内存缓存\n this.memoryCache.set(key, cachedData.value);\n return cachedData.value;\n }\n }\n } catch {\n // localStorage 访问失败时忽略\n }\n }\n \n return null;\n }\n\n set(key: string, value: T): void {\n // 内存缓存(SSR 和浏览器都可用)\n this.memoryCache.set(key, value);\n \n // localStorage 缓存(仅浏览器环境)\n if (isBrowser) {\n try {\n const cached = localStorage.getItem(this.CACHE_KEY);\n const cacheObj = cached ? JSON.parse(cached) : {};\n \n cacheObj[key] = {\n timestamp: Date.now(),\n value: value\n };\n \n localStorage.setItem(this.CACHE_KEY, JSON.stringify(cacheObj));\n } catch {\n // localStorage 访问失败时忽略\n }\n }\n }\n\n clear(): void {\n this.memoryCache.clear();\n if (isBrowser) {\n try {\n localStorage.removeItem(this.CACHE_KEY);\n } catch {\n // localStorage 访问失败时忽略\n }\n }\n }\n\n delete(key: string): void {\n this.memoryCache.delete(key);\n if (isBrowser) {\n try {\n const cached = localStorage.getItem(this.CACHE_KEY);\n if (cached) {\n const cacheObj = JSON.parse(cached);\n delete cacheObj[key];\n localStorage.setItem(this.CACHE_KEY, JSON.stringify(cacheObj));\n }\n } catch {\n // localStorage 访问失败时忽略\n }\n }\n }\n}","import type { ShareData } from './types';\nimport { CacheManager } from '../../utils/umami/cache';\n\nexport class UmamiAPI {\n private cacheManager: CacheManager;\n private sharePromise: Promise<ShareData> | null = null;\n\n constructor(cacheManager: CacheManager) {\n this.cacheManager = cacheManager;\n }\n\n async getShareData(baseUrl: string, shareId: string): Promise<ShareData> {\n if (!this.sharePromise) {\n this.sharePromise = this.fetchShareData(baseUrl, shareId).catch((err) => {\n this.sharePromise = null;\n throw err;\n });\n }\n return this.sharePromise;\n }\n\n private async fetchShareData(baseUrl: string, shareId: string): Promise<ShareData> {\n const res = await fetch(`${baseUrl}/share/${shareId}`);\n if (!res.ok) {\n throw new Error(`获取分享信息失败: ${res.status} ${res.statusText}`);\n }\n return res.json();\n }\n\n async getStats(baseUrl: string, shareId: string, params: any) {\n const cacheKey = `${baseUrl}|${shareId}|${JSON.stringify(params)}`;\n \n const cached = this.cacheManager.get(cacheKey);\n if (cached) {\n return { ...cached, _fromCache: true };\n }\n\n const { websiteId, token } = await this.getShareData(baseUrl, shareId);\n \n const queryParams = new URLSearchParams({\n startAt: '0',\n endAt: Date.now().toString()\n });\n\n // 添加可选的 path 参数\n if (params.path) {\n queryParams.set('path', params.path);\n }\n\n const statsUrl = `${baseUrl}/websites/${websiteId}/stats?${queryParams.toString()}`;\n \n const res = await fetch(statsUrl, {\n headers: { 'x-umami-share-token': token }\n });\n\n if (!res.ok) {\n if (res.status === 401) {\n this.cacheManager.clear();\n throw new Error('认证失败,请检查 shareId');\n }\n throw new Error(`获取统计失败: ${res.status} ${res.statusText}`);\n }\n\n const data = await res.json();\n this.cacheManager.set(cacheKey, data);\n return data;\n }\n\n clearShareCache(): void {\n this.sharePromise = null;\n }\n}","/**\n * 从分享 URL 中提取 apiBase 和 shareId\n * 支持多种 Umami 实例格式:\n * - https://umami.example.com/share/abc123 → apiBase: https://umami.example.com/api\n * - https://cloud.umami.is/analytics/us/share/abc123 → apiBase: https://cloud.umami.is/analytics/us/api\n */\nexport function parseShareUrl(shareUrl: string): { apiBase: string; shareId: string } {\n try {\n const url = new URL(shareUrl);\n \n // 提取 shareId(/share/ 后面的部分)\n const pathParts = url.pathname.split('/');\n const shareIndex = pathParts.indexOf('share');\n \n if (shareIndex === -1 || shareIndex === pathParts.length - 1) {\n throw new Error('无效的分享 URL:未找到 share 路径');\n }\n \n const shareId = pathParts[shareIndex + 1];\n \n if (!shareId || shareId.length < 10) {\n throw new Error('无效的分享 ID');\n }\n \n // 构造 apiBase:去掉 /share/{shareId},加上 /api\n // 例如: /analytics/us/share/abc123 → /analytics/us/api\n const pathBeforeShare = pathParts.slice(0, shareIndex).join('/');\n const apiPath = pathBeforeShare + '/api';\n const apiBase = `${url.protocol}//${url.host}${apiPath}`;\n \n return { apiBase, shareId };\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`URL 解析失败: ${error.message}`);\n }\n throw new Error('URL 解析失败: 无效的 URL 格式');\n }\n}\n\n/**\n * 验证分享 URL 格式\n */\nexport function isValidShareUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return parsed.pathname.includes('/share/');\n } catch {\n return false;\n }\n}","import { CacheManager } from '../../utils/umami/cache';\nimport { UmamiAPI } from './api';\nimport { parseShareUrl } from '../../utils/umami/url-parser';\nimport type { UmamiConfig, StatsResult, StatsQueryParams } from './types';\n\nexport class UmamiClient {\n private config: UmamiConfig;\n private cacheManager: CacheManager;\n private api: UmamiAPI;\n\n constructor(config: UmamiConfig) {\n if (!config.shareUrl) {\n throw new Error('shareUrl 是必需参数');\n }\n \n const { apiBase, shareId } = parseShareUrl(config.shareUrl);\n \n this.config = {\n baseUrl: apiBase,\n shareId,\n ...config\n };\n \n this.cacheManager = new CacheManager('umami', 3600000);\n this.api = new UmamiAPI(this.cacheManager);\n }\n\n async getPageStats(\n path: string, \n options: Partial<StatsQueryParams> = {}\n ): Promise<StatsResult> {\n if (!this.config.baseUrl || !this.config.shareId) {\n throw new Error('客户端未正确初始化');\n }\n \n const data = await this.api.getStats(this.config.baseUrl, this.config.shareId, {\n path: `eq.${path}`,\n ...options\n });\n \n return {\n pageviews: (data.pageviews?.value) || data.pageviews || 0,\n visitors: (data.visitors?.value) || data.visitors || 0,\n _fromCache: data._fromCache\n };\n }\n\n async getPageStatsByUrl(\n url: string,\n options: Partial<StatsQueryParams> = {}\n ): Promise<StatsResult> {\n if (!this.config.baseUrl || !this.config.shareId) {\n throw new Error('客户端未正确初始化');\n }\n \n const data = await this.api.getStats(this.config.baseUrl, this.config.shareId, {\n url: url,\n ...options\n });\n \n return {\n pageviews: (data.pageviews?.value) || data.pageviews || 0,\n visitors: (data.visitors?.value) || data.visitors || 0,\n _fromCache: data._fromCache\n };\n }\n\n async getSiteStats(options: Partial<StatsQueryParams> = {}): Promise<StatsResult> {\n if (!this.config.baseUrl || !this.config.shareId) {\n throw new Error('客户端未正确初始化');\n }\n \n const data = await this.api.getStats(this.config.baseUrl, this.config.shareId, options);\n \n return {\n pageviews: data.pageviews || 0,\n visitors: data.visitors || 0,\n _fromCache: data._fromCache\n };\n }\n\n clearCache(): void {\n this.cacheManager.clear();\n this.api.clearShareCache();\n }\n\n getConfig(): Readonly<UmamiConfig> {\n return { ...this.config };\n }\n\n updateConfig(newConfig: Partial<UmamiConfig>): void {\n this.config = { ...this.config, ...newConfig };\n }\n}\n\nexport function createUmamiClient(config: UmamiConfig): UmamiClient {\n return new UmamiClient(config);\n}","import { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\n\n// Astro 集成配置\nexport interface UmamiIntegrationOptions {\n shareUrl: string; // Umami 分享链接\n}\n\n// Astro 集成函数\nexport function umami(options: UmamiIntegrationOptions) {\n if (!options.shareUrl) {\n throw new Error('[oddmisc] 需要配置 shareUrl');\n }\n\n return {\n name: 'oddmisc-umami-integration',\n hooks: {\n 'astro:config:setup': ({ injectScript }: any) => {\n // 读取运行时代码\n let runtimeCode = '';\n try {\n const __dirname = dirname(fileURLToPath(import.meta.url));\n const runtimePath = join(__dirname, './runtime/client.global.js');\n runtimeCode = readFileSync(runtimePath, 'utf-8');\n } catch {\n console.warn('[oddmisc] 无法读取运行时文件,使用备用方案');\n }\n\n // 注入运行时 + 初始化配置\n const initCode = `\n// oddmisc Umami Runtime\n${runtimeCode}\n\n// 初始化\nif (typeof window !== 'undefined') {\n __oddmiscRuntime.initUmamiRuntime(${JSON.stringify({ shareUrl: options.shareUrl })});\n}\n`;\n\n injectScript('page', initCode);\n }\n }\n };\n}","/**\n * 浏览器运行时客户端\n * 用于 Astro 集成注入到页面中\n * \n * 注意:此文件会被内联注入到页面,不能有外部依赖\n */\n\ninterface UmamiRuntimeConfig {\n shareUrl: string;\n}\n\ninterface StatsResult {\n pageviews: number;\n visitors: number;\n visits?: number;\n _fromCache?: boolean;\n}\n\ninterface ShareData {\n websiteId: string;\n token: string;\n}\n\n// 解析分享 URL\nfunction parseShareUrl(shareUrl: string): { apiBase: string; shareId: string } {\n const url = new URL(shareUrl);\n const pathParts = url.pathname.split('/');\n const shareIndex = pathParts.indexOf('share');\n \n if (shareIndex === -1 || shareIndex === pathParts.length - 1) {\n throw new Error('无效的分享 URL:未找到 share 路径');\n }\n \n const shareId = pathParts[shareIndex + 1];\n \n if (!shareId || shareId.length < 10) {\n throw new Error('无效的分享 ID');\n }\n \n // 构造 apiBase:去掉 /share/{shareId},加上 /api\n const pathBeforeShare = pathParts.slice(0, shareIndex).join('/');\n const apiPath = pathBeforeShare + '/api';\n const apiBase = `${url.protocol}//${url.host}${apiPath}`;\n \n return { apiBase, shareId };\n}\n\n// 简单的缓存管理\nclass SimpleCache {\n private cache = new Map<string, { value: any; timestamp: number }>();\n private storageKey: string;\n private ttl: number;\n\n constructor(storageKey: string, ttl: number) {\n this.storageKey = storageKey;\n this.ttl = ttl;\n this.loadFromStorage();\n }\n\n private loadFromStorage(): void {\n try {\n const stored = localStorage.getItem(this.storageKey);\n if (stored) {\n const parsed = JSON.parse(stored);\n const now = Date.now();\n for (const [key, data] of Object.entries(parsed)) {\n if (now - (data as any).timestamp < this.ttl) {\n this.cache.set(key, data as { value: any; timestamp: number });\n }\n }\n }\n } catch {\n // ignore\n }\n }\n\n private saveToStorage(): void {\n try {\n const obj: Record<string, any> = {};\n this.cache.forEach((value, key) => {\n obj[key] = value;\n });\n localStorage.setItem(this.storageKey, JSON.stringify(obj));\n } catch {\n // ignore\n }\n }\n\n get(key: string): any | null {\n const cached = this.cache.get(key);\n if (cached && Date.now() - cached.timestamp < this.ttl) {\n return cached.value;\n }\n return null;\n }\n\n set(key: string, value: any): void {\n this.cache.set(key, { value, timestamp: Date.now() });\n this.saveToStorage();\n }\n\n clear(): void {\n this.cache.clear();\n try {\n localStorage.removeItem(this.storageKey);\n } catch {\n // ignore\n }\n }\n}\n\n// Umami 运行时客户端\nclass UmamiRuntimeClient {\n private apiBase: string;\n private shareId: string;\n private cache: SimpleCache;\n private shareData: ShareData | null = null;\n private sharePromise: Promise<ShareData> | null = null;\n\n constructor(config: UmamiRuntimeConfig) {\n const { apiBase, shareId } = parseShareUrl(config.shareUrl);\n this.apiBase = apiBase;\n this.shareId = shareId;\n this.cache = new SimpleCache(`umami-runtime-${shareId}`, 3600000);\n }\n\n private async getShareData(): Promise<ShareData> {\n if (this.shareData) {\n return this.shareData;\n }\n\n if (this.sharePromise) {\n return this.sharePromise;\n }\n\n this.sharePromise = (async (): Promise<ShareData> => {\n const res = await fetch(`${this.apiBase}/share/${this.shareId}`);\n if (!res.ok) {\n this.sharePromise = null;\n throw new Error(`获取分享信息失败: ${res.status}`);\n }\n const data = await res.json();\n this.shareData = data;\n return data;\n })();\n\n return this.sharePromise;\n }\n\n async getStats(path?: string): Promise<StatsResult> {\n const cacheKey = path ? `stats-${path}` : 'stats-site';\n \n const cached = this.cache.get(cacheKey);\n if (cached) {\n return { ...cached, _fromCache: true };\n }\n\n const { websiteId, token } = await this.getShareData();\n \n const params = new URLSearchParams({\n startAt: '0',\n endAt: Date.now().toString()\n });\n\n if (path) {\n params.set('path', `eq.${path}`);\n }\n\n const res = await fetch(\n `${this.apiBase}/websites/${websiteId}/stats?${params.toString()}`,\n {\n headers: { 'x-umami-share-token': token }\n }\n );\n\n if (!res.ok) {\n throw new Error(`获取统计失败: ${res.status}`);\n }\n\n const data = await res.json();\n \n const result: StatsResult = {\n pageviews: data.pageviews?.value ?? data.pageviews ?? 0,\n visitors: data.visitors?.value ?? data.visitors ?? 0\n };\n\n this.cache.set(cacheKey, result);\n\n return result;\n }\n\n async getSiteStats(): Promise<StatsResult> {\n return this.getStats();\n }\n\n async getPageStats(path: string): Promise<StatsResult> {\n return this.getStats(path);\n }\n\n clearCache(): void {\n this.cache.clear();\n this.shareData = null;\n this.sharePromise = null;\n }\n}\n\n// 初始化函数 - 挂载到 window.oddmisc\nexport function initUmamiRuntime(config: UmamiRuntimeConfig): void {\n const client = new UmamiRuntimeClient(config);\n \n (window as any).oddmisc = (window as any).oddmisc || {};\n (window as any).oddmisc.umami = client;\n (window as any).oddmisc.getStats = (path?: string) => client.getStats(path);\n (window as any).oddmisc.getSiteStats = () => client.getSiteStats();\n (window as any).oddmisc.getPageStats = (path: string) => client.getPageStats(path);\n (window as any).oddmisc.clearCache = () => client.clearCache();\n \n console.log('[oddmisc] Umami runtime client initialized');\n}\n\nexport type { UmamiRuntimeConfig, StatsResult };\n"],"mappings":"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","names":["index_exports","__export","CacheManager","UmamiClient","UmamiError","VERSION","createUmamiClient","initUmamiRuntime","isValidConfig","isValidShareUrl","parseShareUrl","umami","__toCommonJS","VERSION","isValidConfig","config","UmamiError","message","code","isBrowser","CacheManager","namespace","ttl","key","cached","cachedData","value","cacheObj","UmamiAPI","cacheManager","baseUrl","shareId","err","res","params","cacheKey","cached","websiteId","token","queryParams","statsUrl","data","parseShareUrl","shareUrl","url","pathParts","shareIndex","shareId","apiPath","error","isValidShareUrl","UmamiClient","config","apiBase","shareId","parseShareUrl","CacheManager","UmamiAPI","path","options","data","url","newConfig","createUmamiClient","import_fs","import_url","import_path","import_meta","umami","options","injectScript","runtimeCode","__dirname","runtimePath","initCode","parseShareUrl","shareUrl","url","pathParts","shareIndex","shareId","apiPath","SimpleCache","storageKey","ttl","stored","parsed","now","key","data","obj","value","cached","UmamiRuntimeClient","config","apiBase","res","path","cacheKey","websiteId","token","params","result","initUmamiRuntime","client"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/shared/index.ts","../src/utils/umami/cache.ts","../src/modules/umami/api.ts","../src/utils/umami/url-parser.ts","../src/modules/umami/client.ts","../src/astro/integration.ts","../src/runtime/client.ts"],"sourcesContent":["// 核心导出\nexport { VERSION, isValidConfig, UmamiError } from './shared';\n\n// Umami 模块\nexport { UmamiClient, createUmamiClient } from './modules/umami/client';\nexport type { UmamiConfig, StatsResult, StatsQueryParams } from './modules/umami/types';\n\n// 工具函数\nexport { CacheManager } from './utils/umami/cache';\nexport { parseShareUrl, isValidShareUrl } from './utils/umami/url-parser';\n\n// Astro 集成\nexport { umami } from './astro';\nexport type { UmamiIntegrationOptions } from './astro';\n\n// 运行时客户端(用于手动初始化)\nexport { initUmamiRuntime } from './runtime/client';\nexport type { UmamiRuntimeConfig as RuntimeConfig, StatsResult as RuntimeStatsResult } from './runtime/client';","// 版本信息\nexport const VERSION = '1.0.0';\n\n// 配置验证\nexport function isValidConfig(config: any): boolean {\n return (\n typeof config === 'object' &&\n config !== null &&\n typeof config.baseUrl === 'string' &&\n typeof config.shareId === 'string' &&\n config.baseUrl.length > 0 &&\n config.shareId.length > 0\n );\n}\n\n// 自定义错误类\nexport class UmamiError extends Error {\n constructor(message: string, public code?: string) {\n super(message);\n this.name = 'UmamiError';\n }\n}","// 检测是否在浏览器环境\nconst isBrowser = typeof window !== 'undefined' && typeof localStorage !== 'undefined';\n\n// 通用缓存管理器\nexport class CacheManager<T = any> {\n private memoryCache = new Map<string, T>();\n private readonly CACHE_KEY: string;\n private readonly DEFAULT_TTL: number;\n\n constructor(namespace = 'default', ttl = 3600000) {\n this.CACHE_KEY = `cache-${namespace}`;\n this.DEFAULT_TTL = ttl;\n }\n\n get(key: string): T | null {\n // 内存缓存(SSR 和浏览器都可用)\n if (this.memoryCache.has(key)) {\n return this.memoryCache.get(key)!;\n }\n \n // localStorage 缓存(仅浏览器环境)\n if (isBrowser) {\n try {\n const cached = localStorage.getItem(this.CACHE_KEY);\n if (cached) {\n const parsed = JSON.parse(cached);\n const cachedData = parsed[key];\n if (cachedData && Date.now() - cachedData.timestamp < this.DEFAULT_TTL) {\n // 命中 localStorage 缓存时,同步到内存缓存\n this.memoryCache.set(key, cachedData.value);\n return cachedData.value;\n }\n }\n } catch {\n // localStorage 访问失败时忽略\n }\n }\n \n return null;\n }\n\n set(key: string, value: T): void {\n // 内存缓存(SSR 和浏览器都可用)\n this.memoryCache.set(key, value);\n \n // localStorage 缓存(仅浏览器环境)\n if (isBrowser) {\n try {\n const cached = localStorage.getItem(this.CACHE_KEY);\n const cacheObj = cached ? JSON.parse(cached) : {};\n \n cacheObj[key] = {\n timestamp: Date.now(),\n value: value\n };\n \n localStorage.setItem(this.CACHE_KEY, JSON.stringify(cacheObj));\n } catch {\n // localStorage 访问失败时忽略\n }\n }\n }\n\n clear(): void {\n this.memoryCache.clear();\n if (isBrowser) {\n try {\n localStorage.removeItem(this.CACHE_KEY);\n } catch {\n // localStorage 访问失败时忽略\n }\n }\n }\n\n delete(key: string): void {\n this.memoryCache.delete(key);\n if (isBrowser) {\n try {\n const cached = localStorage.getItem(this.CACHE_KEY);\n if (cached) {\n const cacheObj = JSON.parse(cached);\n delete cacheObj[key];\n localStorage.setItem(this.CACHE_KEY, JSON.stringify(cacheObj));\n }\n } catch {\n // localStorage 访问失败时忽略\n }\n }\n }\n}","import type { ShareData } from './types';\nimport { CacheManager } from '../../utils/umami/cache';\n\nexport class UmamiAPI {\n private cacheManager: CacheManager;\n private sharePromise: Promise<ShareData> | null = null;\n\n constructor(cacheManager: CacheManager) {\n this.cacheManager = cacheManager;\n }\n\n async getShareData(baseUrl: string, shareId: string): Promise<ShareData> {\n if (!this.sharePromise) {\n this.sharePromise = this.fetchShareData(baseUrl, shareId).catch((err) => {\n this.sharePromise = null;\n throw err;\n });\n }\n return this.sharePromise;\n }\n\n private async fetchShareData(baseUrl: string, shareId: string): Promise<ShareData> {\n const res = await fetch(`${baseUrl}/share/${shareId}`);\n if (!res.ok) {\n throw new Error(`获取分享信息失败: ${res.status} ${res.statusText}`);\n }\n return res.json();\n }\n\n async getStats(baseUrl: string, shareId: string, params: any) {\n const cacheKey = `${baseUrl}|${shareId}|${JSON.stringify(params)}`;\n \n const cached = this.cacheManager.get(cacheKey);\n if (cached) {\n return { ...cached, _fromCache: true };\n }\n\n const { websiteId, token } = await this.getShareData(baseUrl, shareId);\n \n const queryParams = new URLSearchParams({\n startAt: '0',\n endAt: Date.now().toString()\n });\n\n // 添加可选的 path 参数\n if (params.path) {\n queryParams.set('path', params.path);\n }\n\n const statsUrl = `${baseUrl}/websites/${websiteId}/stats?${queryParams.toString()}`;\n \n const res = await fetch(statsUrl, {\n headers: { 'x-umami-share-token': token }\n });\n\n if (!res.ok) {\n if (res.status === 401) {\n this.cacheManager.clear();\n throw new Error('认证失败,请检查 shareId');\n }\n throw new Error(`获取统计失败: ${res.status} ${res.statusText}`);\n }\n\n const data = await res.json();\n this.cacheManager.set(cacheKey, data);\n return data;\n }\n\n clearShareCache(): void {\n this.sharePromise = null;\n }\n}","/**\n * 从分享 URL 中提取 apiBase 和 shareId\n * 支持多种 Umami 实例格式:\n * - https://umami.example.com/share/abc123 → apiBase: https://umami.example.com/api\n * - https://cloud.umami.is/analytics/us/share/abc123 → apiBase: https://cloud.umami.is/analytics/us/api\n */\nexport function parseShareUrl(shareUrl: string): { apiBase: string; shareId: string } {\n try {\n const url = new URL(shareUrl);\n \n // 提取 shareId(/share/ 后面的部分)\n const pathParts = url.pathname.split('/');\n const shareIndex = pathParts.indexOf('share');\n \n if (shareIndex === -1 || shareIndex === pathParts.length - 1) {\n throw new Error('无效的分享 URL:未找到 share 路径');\n }\n \n const shareId = pathParts[shareIndex + 1];\n \n if (!shareId || shareId.length < 10) {\n throw new Error('无效的分享 ID');\n }\n \n // 构造 apiBase:去掉 /share/{shareId},加上 /api\n // 例如: /analytics/us/share/abc123 → /analytics/us/api\n const pathBeforeShare = pathParts.slice(0, shareIndex).join('/');\n const apiPath = pathBeforeShare + '/api';\n const apiBase = `${url.protocol}//${url.host}${apiPath}`;\n \n return { apiBase, shareId };\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`URL 解析失败: ${error.message}`);\n }\n throw new Error('URL 解析失败: 无效的 URL 格式');\n }\n}\n\n/**\n * 验证分享 URL 格式\n */\nexport function isValidShareUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return parsed.pathname.includes('/share/');\n } catch {\n return false;\n }\n}","import { CacheManager } from '../../utils/umami/cache';\nimport { UmamiAPI } from './api';\nimport { parseShareUrl } from '../../utils/umami/url-parser';\nimport type { UmamiConfig, StatsResult, StatsQueryParams } from './types';\n\nexport class UmamiClient {\n private config: UmamiConfig;\n private cacheManager: CacheManager;\n private api: UmamiAPI;\n\n constructor(config: UmamiConfig) {\n if (!config.shareUrl) {\n throw new Error('shareUrl 是必需参数');\n }\n \n const { apiBase, shareId } = parseShareUrl(config.shareUrl);\n \n this.config = {\n baseUrl: apiBase,\n shareId,\n ...config\n };\n \n this.cacheManager = new CacheManager('umami', 3600000);\n this.api = new UmamiAPI(this.cacheManager);\n }\n\n async getPageStats(\n path: string, \n options: Partial<StatsQueryParams> = {}\n ): Promise<StatsResult> {\n if (!this.config.baseUrl || !this.config.shareId) {\n throw new Error('客户端未正确初始化');\n }\n \n const data = await this.api.getStats(this.config.baseUrl, this.config.shareId, {\n path: `eq.${path}`,\n ...options\n });\n \n return {\n pageviews: (data.pageviews?.value) || data.pageviews || 0,\n visitors: (data.visitors?.value) || data.visitors || 0,\n _fromCache: data._fromCache\n };\n }\n\n async getPageStatsByUrl(\n url: string,\n options: Partial<StatsQueryParams> = {}\n ): Promise<StatsResult> {\n if (!this.config.baseUrl || !this.config.shareId) {\n throw new Error('客户端未正确初始化');\n }\n \n const data = await this.api.getStats(this.config.baseUrl, this.config.shareId, {\n url: url,\n ...options\n });\n \n return {\n pageviews: (data.pageviews?.value) || data.pageviews || 0,\n visitors: (data.visitors?.value) || data.visitors || 0,\n _fromCache: data._fromCache\n };\n }\n\n async getSiteStats(options: Partial<StatsQueryParams> = {}): Promise<StatsResult> {\n if (!this.config.baseUrl || !this.config.shareId) {\n throw new Error('客户端未正确初始化');\n }\n \n const data = await this.api.getStats(this.config.baseUrl, this.config.shareId, options);\n \n return {\n pageviews: data.pageviews || 0,\n visitors: data.visitors || 0,\n _fromCache: data._fromCache\n };\n }\n\n clearCache(): void {\n this.cacheManager.clear();\n this.api.clearShareCache();\n }\n\n getConfig(): Readonly<UmamiConfig> {\n return { ...this.config };\n }\n\n updateConfig(newConfig: Partial<UmamiConfig>): void {\n this.config = { ...this.config, ...newConfig };\n }\n}\n\nexport function createUmamiClient(config: UmamiConfig): UmamiClient {\n return new UmamiClient(config);\n}","import { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\n\n// Astro 集成配置\nexport interface UmamiIntegrationOptions {\n shareUrl: string; // Umami 分享链接\n}\n\n// Astro 集成函数\nexport function umami(options: UmamiIntegrationOptions) {\n if (!options.shareUrl) {\n throw new Error('[oddmisc] 需要配置 shareUrl');\n }\n\n return {\n name: 'oddmisc-umami-integration',\n hooks: {\n 'astro:config:setup': ({ injectScript }: any) => {\n // 读取运行时代码\n let runtimeCode = '';\n try {\n const __dirname = dirname(fileURLToPath(import.meta.url));\n const runtimePath = join(__dirname, './runtime/client.global.js');\n runtimeCode = readFileSync(runtimePath, 'utf-8');\n } catch {\n console.warn('[oddmisc] 无法读取运行时文件,使用备用方案');\n }\n\n // 注入运行时 + 初始化配置\n const initCode = `\n// oddmisc Umami Runtime\n${runtimeCode}\n\n// 初始化\nif (typeof window !== 'undefined') {\n __oddmiscRuntime.initUmamiRuntime(${JSON.stringify({ shareUrl: options.shareUrl })});\n}\n`;\n\n injectScript('page', initCode);\n }\n }\n };\n}","/**\n * 浏览器运行时客户端\n * 用于 Astro 集成注入到页面中\n * \n * 注意:此文件会被内联注入到页面,不能有外部依赖\n */\n\ninterface UmamiRuntimeConfig {\n shareUrl: string;\n}\n\ninterface StatsResult {\n pageviews: number;\n visitors: number;\n visits?: number;\n _fromCache?: boolean;\n}\n\ninterface ShareData {\n websiteId: string;\n token: string;\n}\n\n// 解析分享 URL\nfunction parseShareUrl(shareUrl: string): { apiBase: string; shareId: string } {\n const url = new URL(shareUrl);\n const pathParts = url.pathname.split('/');\n const shareIndex = pathParts.indexOf('share');\n \n if (shareIndex === -1 || shareIndex === pathParts.length - 1) {\n throw new Error('无效的分享 URL:未找到 share 路径');\n }\n \n const shareId = pathParts[shareIndex + 1];\n \n if (!shareId || shareId.length < 10) {\n throw new Error('无效的分享 ID');\n }\n \n // 构造 apiBase:去掉 /share/{shareId},加上 /api\n const pathBeforeShare = pathParts.slice(0, shareIndex).join('/');\n const apiPath = pathBeforeShare + '/api';\n const apiBase = `${url.protocol}//${url.host}${apiPath}`;\n \n return { apiBase, shareId };\n}\n\n// 简单的缓存管理\nclass SimpleCache {\n private cache = new Map<string, { value: any; timestamp: number }>();\n private storageKey: string;\n private ttl: number;\n\n constructor(storageKey: string, ttl: number) {\n this.storageKey = storageKey;\n this.ttl = ttl;\n this.loadFromStorage();\n }\n\n private loadFromStorage(): void {\n try {\n const stored = localStorage.getItem(this.storageKey);\n if (stored) {\n const parsed = JSON.parse(stored);\n const now = Date.now();\n for (const [key, data] of Object.entries(parsed)) {\n if (now - (data as any).timestamp < this.ttl) {\n this.cache.set(key, data as { value: any; timestamp: number });\n }\n }\n }\n } catch {\n // ignore\n }\n }\n\n private saveToStorage(): void {\n try {\n const obj: Record<string, any> = {};\n this.cache.forEach((value, key) => {\n obj[key] = value;\n });\n localStorage.setItem(this.storageKey, JSON.stringify(obj));\n } catch {\n // ignore\n }\n }\n\n get(key: string): any | null {\n const cached = this.cache.get(key);\n if (cached && Date.now() - cached.timestamp < this.ttl) {\n return cached.value;\n }\n return null;\n }\n\n set(key: string, value: any): void {\n this.cache.set(key, { value, timestamp: Date.now() });\n this.saveToStorage();\n }\n\n clear(): void {\n this.cache.clear();\n try {\n localStorage.removeItem(this.storageKey);\n } catch {\n // ignore\n }\n }\n}\n\n// Umami 运行时客户端\nclass UmamiRuntimeClient {\n private apiBase: string;\n private shareId: string;\n private cache: SimpleCache;\n private shareData: ShareData | null = null;\n private sharePromise: Promise<ShareData> | null = null;\n\n constructor(config: UmamiRuntimeConfig) {\n const { apiBase, shareId } = parseShareUrl(config.shareUrl);\n this.apiBase = apiBase;\n this.shareId = shareId;\n this.cache = new SimpleCache(`umami-runtime-${shareId}`, 3600000);\n }\n\n private async getShareData(): Promise<ShareData> {\n if (this.shareData) {\n return this.shareData;\n }\n\n if (this.sharePromise) {\n return this.sharePromise;\n }\n\n this.sharePromise = (async (): Promise<ShareData> => {\n const res = await fetch(`${this.apiBase}/share/${this.shareId}`);\n if (!res.ok) {\n this.sharePromise = null;\n throw new Error(`获取分享信息失败: ${res.status}`);\n }\n const data = await res.json();\n this.shareData = data;\n return data;\n })();\n\n return this.sharePromise;\n }\n\n async getStats(path?: string): Promise<StatsResult> {\n const cacheKey = path ? `stats-${path}` : 'stats-site';\n \n const cached = this.cache.get(cacheKey);\n if (cached) {\n return { ...cached, _fromCache: true };\n }\n\n const { websiteId, token } = await this.getShareData();\n \n const params = new URLSearchParams({\n startAt: '0',\n endAt: Date.now().toString()\n });\n\n if (path) {\n params.set('path', `eq.${path}`);\n }\n\n const res = await fetch(\n `${this.apiBase}/websites/${websiteId}/stats?${params.toString()}`,\n {\n headers: { 'x-umami-share-token': token }\n }\n );\n\n if (!res.ok) {\n throw new Error(`获取统计失败: ${res.status}`);\n }\n\n const data = await res.json();\n \n const result: StatsResult = {\n pageviews: data.pageviews?.value ?? data.pageviews ?? 0,\n visitors: data.visitors?.value ?? data.visitors ?? 0,\n visits: data.visits?.value ?? data.visits ?? 0\n };\n\n this.cache.set(cacheKey, result);\n\n return result;\n }\n\n async getSiteStats(): Promise<StatsResult> {\n return this.getStats();\n }\n\n async getPageStats(path: string): Promise<StatsResult> {\n return this.getStats(path);\n }\n\n clearCache(): void {\n this.cache.clear();\n this.shareData = null;\n this.sharePromise = null;\n }\n}\n\n// 初始化函数 - 挂载到 window.oddmisc\nexport function initUmamiRuntime(config: UmamiRuntimeConfig): void {\n const client = new UmamiRuntimeClient(config);\n \n (window as any).oddmisc = (window as any).oddmisc || {};\n (window as any).oddmisc.umami = client;\n (window as any).oddmisc.getStats = (path?: string) => client.getStats(path);\n (window as any).oddmisc.getSiteStats = () => client.getSiteStats();\n (window as any).oddmisc.getPageStats = (path: string) => client.getPageStats(path);\n (window as any).oddmisc.clearCache = () => client.clearCache();\n \n console.log('[oddmisc] Umami runtime client initialized');\n}\n\nexport type { UmamiRuntimeConfig, StatsResult };\n"],"mappings":"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","names":["index_exports","__export","CacheManager","UmamiClient","UmamiError","VERSION","createUmamiClient","initUmamiRuntime","isValidConfig","isValidShareUrl","parseShareUrl","umami","__toCommonJS","VERSION","isValidConfig","config","UmamiError","message","code","isBrowser","CacheManager","namespace","ttl","key","cached","cachedData","value","cacheObj","UmamiAPI","cacheManager","baseUrl","shareId","err","res","params","cacheKey","cached","websiteId","token","queryParams","statsUrl","data","parseShareUrl","shareUrl","url","pathParts","shareIndex","shareId","apiPath","error","isValidShareUrl","UmamiClient","config","apiBase","shareId","parseShareUrl","CacheManager","UmamiAPI","path","options","data","url","newConfig","createUmamiClient","import_fs","import_url","import_path","import_meta","umami","options","injectScript","runtimeCode","__dirname","runtimePath","initCode","parseShareUrl","shareUrl","url","pathParts","shareIndex","shareId","apiPath","SimpleCache","storageKey","ttl","stored","parsed","now","key","data","obj","value","cached","UmamiRuntimeClient","config","apiBase","res","path","cacheKey","websiteId","token","params","result","initUmamiRuntime","client"]}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- var C="1.0.0";function U(r){return typeof r=="object"&&r!==null&&typeof r.baseUrl=="string"&&typeof r.shareId=="string"&&r.baseUrl.length>0&&r.shareId.length>0}var f=class extends Error{constructor(e,a){super(e);this.code=a;this.name="UmamiError"}};var g=typeof window<"u"&&typeof localStorage<"u",m=class{constructor(t="default",e=36e5){this.memoryCache=new Map;this.CACHE_KEY=`cache-${t}`,this.DEFAULT_TTL=e}get(t){if(this.memoryCache.has(t))return this.memoryCache.get(t);if(g)try{let e=localStorage.getItem(this.CACHE_KEY);if(e){let s=JSON.parse(e)[t];if(s&&Date.now()-s.timestamp<this.DEFAULT_TTL)return this.memoryCache.set(t,s.value),s.value}}catch{}return null}set(t,e){if(this.memoryCache.set(t,e),g)try{let a=localStorage.getItem(this.CACHE_KEY),s=a?JSON.parse(a):{};s[t]={timestamp:Date.now(),value:e},localStorage.setItem(this.CACHE_KEY,JSON.stringify(s))}catch{}}clear(){if(this.memoryCache.clear(),g)try{localStorage.removeItem(this.CACHE_KEY)}catch{}}delete(t){if(this.memoryCache.delete(t),g)try{let e=localStorage.getItem(this.CACHE_KEY);if(e){let a=JSON.parse(e);delete a[t],localStorage.setItem(this.CACHE_KEY,JSON.stringify(a))}}catch{}}};var p=class{constructor(t){this.sharePromise=null;this.cacheManager=t}async getShareData(t,e){return this.sharePromise||(this.sharePromise=this.fetchShareData(t,e).catch(a=>{throw this.sharePromise=null,a})),this.sharePromise}async fetchShareData(t,e){let a=await fetch(`${t}/share/${e}`);if(!a.ok)throw new Error(`\u83B7\u53D6\u5206\u4EAB\u4FE1\u606F\u5931\u8D25: ${a.status} ${a.statusText}`);return a.json()}async getStats(t,e,a){let s=`${t}|${e}|${JSON.stringify(a)}`,i=this.cacheManager.get(s);if(i)return{...i,_fromCache:!0};let{websiteId:o,token:n}=await this.getShareData(t,e),h=new URLSearchParams({startAt:"0",endAt:Date.now().toString()});a.path&&h.set("path",a.path);let l=`${t}/websites/${o}/stats?${h.toString()}`,c=await fetch(l,{headers:{"x-umami-share-token":n}});if(!c.ok)throw c.status===401?(this.cacheManager.clear(),new Error("\u8BA4\u8BC1\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5 shareId")):new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${c.status} ${c.statusText}`);let y=await c.json();return this.cacheManager.set(s,y),y}clearShareCache(){this.sharePromise=null}};function d(r){try{let t=new URL(r),e=t.pathname.split("/"),a=e.indexOf("share");if(a===-1||a===e.length-1)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB URL\uFF1A\u672A\u627E\u5230 share \u8DEF\u5F84");let s=e[a+1];if(!s||s.length<10)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB ID");let o=e.slice(0,a).join("/")+"/api";return{apiBase:`${t.protocol}//${t.host}${o}`,shareId:s}}catch(t){throw t instanceof Error?new Error(`URL \u89E3\u6790\u5931\u8D25: ${t.message}`):new Error("URL \u89E3\u6790\u5931\u8D25: \u65E0\u6548\u7684 URL \u683C\u5F0F")}}function P(r){try{return new URL(r).pathname.includes("/share/")}catch{return!1}}var u=class{constructor(t){if(!t.shareUrl)throw new Error("shareUrl \u662F\u5FC5\u9700\u53C2\u6570");let{apiBase:e,shareId:a}=d(t.shareUrl);this.config={baseUrl:e,shareId:a,...t},this.cacheManager=new m("umami",36e5),this.api=new p(this.cacheManager)}async getPageStats(t,e={}){if(!this.config.baseUrl||!this.config.shareId)throw new Error("\u5BA2\u6237\u7AEF\u672A\u6B63\u786E\u521D\u59CB\u5316");let a=await this.api.getStats(this.config.baseUrl,this.config.shareId,{path:`eq.${t}`,...e});return{pageviews:a.pageviews?.value||a.pageviews||0,visitors:a.visitors?.value||a.visitors||0,_fromCache:a._fromCache}}async getPageStatsByUrl(t,e={}){if(!this.config.baseUrl||!this.config.shareId)throw new Error("\u5BA2\u6237\u7AEF\u672A\u6B63\u786E\u521D\u59CB\u5316");let a=await this.api.getStats(this.config.baseUrl,this.config.shareId,{url:t,...e});return{pageviews:a.pageviews?.value||a.pageviews||0,visitors:a.visitors?.value||a.visitors||0,_fromCache:a._fromCache}}async getSiteStats(t={}){if(!this.config.baseUrl||!this.config.shareId)throw new Error("\u5BA2\u6237\u7AEF\u672A\u6B63\u786E\u521D\u59CB\u5316");let e=await this.api.getStats(this.config.baseUrl,this.config.shareId,t);return{pageviews:e.pageviews||0,visitors:e.visitors||0,_fromCache:e._fromCache}}clearCache(){this.cacheManager.clear(),this.api.clearShareCache()}getConfig(){return{...this.config}}updateConfig(t){this.config={...this.config,...t}}};function E(r){return new u(r)}import{readFileSync as I}from"fs";import{fileURLToPath as R}from"url";import{dirname as x,join as D}from"path";function v(r){if(!r.shareUrl)throw new Error("[oddmisc] \u9700\u8981\u914D\u7F6E shareUrl");return{name:"oddmisc-umami-integration",hooks:{"astro:config:setup":({injectScript:t})=>{let e="";try{let s=x(R(import.meta.url)),i=D(s,"./runtime/client.global.js");e=I(i,"utf-8")}catch{console.warn("[oddmisc] \u65E0\u6CD5\u8BFB\u53D6\u8FD0\u884C\u65F6\u6587\u4EF6\uFF0C\u4F7F\u7528\u5907\u7528\u65B9\u6848")}let a=`
1
+ var C="1.0.0";function U(r){return typeof r=="object"&&r!==null&&typeof r.baseUrl=="string"&&typeof r.shareId=="string"&&r.baseUrl.length>0&&r.shareId.length>0}var f=class extends Error{constructor(e,a){super(e);this.code=a;this.name="UmamiError"}};var g=typeof window<"u"&&typeof localStorage<"u",m=class{constructor(t="default",e=36e5){this.memoryCache=new Map;this.CACHE_KEY=`cache-${t}`,this.DEFAULT_TTL=e}get(t){if(this.memoryCache.has(t))return this.memoryCache.get(t);if(g)try{let e=localStorage.getItem(this.CACHE_KEY);if(e){let s=JSON.parse(e)[t];if(s&&Date.now()-s.timestamp<this.DEFAULT_TTL)return this.memoryCache.set(t,s.value),s.value}}catch{}return null}set(t,e){if(this.memoryCache.set(t,e),g)try{let a=localStorage.getItem(this.CACHE_KEY),s=a?JSON.parse(a):{};s[t]={timestamp:Date.now(),value:e},localStorage.setItem(this.CACHE_KEY,JSON.stringify(s))}catch{}}clear(){if(this.memoryCache.clear(),g)try{localStorage.removeItem(this.CACHE_KEY)}catch{}}delete(t){if(this.memoryCache.delete(t),g)try{let e=localStorage.getItem(this.CACHE_KEY);if(e){let a=JSON.parse(e);delete a[t],localStorage.setItem(this.CACHE_KEY,JSON.stringify(a))}}catch{}}};var p=class{constructor(t){this.sharePromise=null;this.cacheManager=t}async getShareData(t,e){return this.sharePromise||(this.sharePromise=this.fetchShareData(t,e).catch(a=>{throw this.sharePromise=null,a})),this.sharePromise}async fetchShareData(t,e){let a=await fetch(`${t}/share/${e}`);if(!a.ok)throw new Error(`\u83B7\u53D6\u5206\u4EAB\u4FE1\u606F\u5931\u8D25: ${a.status} ${a.statusText}`);return a.json()}async getStats(t,e,a){let s=`${t}|${e}|${JSON.stringify(a)}`,i=this.cacheManager.get(s);if(i)return{...i,_fromCache:!0};let{websiteId:n,token:h}=await this.getShareData(t,e),o=new URLSearchParams({startAt:"0",endAt:Date.now().toString()});a.path&&o.set("path",a.path);let l=`${t}/websites/${n}/stats?${o.toString()}`,c=await fetch(l,{headers:{"x-umami-share-token":h}});if(!c.ok)throw c.status===401?(this.cacheManager.clear(),new Error("\u8BA4\u8BC1\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5 shareId")):new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${c.status} ${c.statusText}`);let y=await c.json();return this.cacheManager.set(s,y),y}clearShareCache(){this.sharePromise=null}};function d(r){try{let t=new URL(r),e=t.pathname.split("/"),a=e.indexOf("share");if(a===-1||a===e.length-1)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB URL\uFF1A\u672A\u627E\u5230 share \u8DEF\u5F84");let s=e[a+1];if(!s||s.length<10)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB ID");let n=e.slice(0,a).join("/")+"/api";return{apiBase:`${t.protocol}//${t.host}${n}`,shareId:s}}catch(t){throw t instanceof Error?new Error(`URL \u89E3\u6790\u5931\u8D25: ${t.message}`):new Error("URL \u89E3\u6790\u5931\u8D25: \u65E0\u6548\u7684 URL \u683C\u5F0F")}}function P(r){try{return new URL(r).pathname.includes("/share/")}catch{return!1}}var u=class{constructor(t){if(!t.shareUrl)throw new Error("shareUrl \u662F\u5FC5\u9700\u53C2\u6570");let{apiBase:e,shareId:a}=d(t.shareUrl);this.config={baseUrl:e,shareId:a,...t},this.cacheManager=new m("umami",36e5),this.api=new p(this.cacheManager)}async getPageStats(t,e={}){if(!this.config.baseUrl||!this.config.shareId)throw new Error("\u5BA2\u6237\u7AEF\u672A\u6B63\u786E\u521D\u59CB\u5316");let a=await this.api.getStats(this.config.baseUrl,this.config.shareId,{path:`eq.${t}`,...e});return{pageviews:a.pageviews?.value||a.pageviews||0,visitors:a.visitors?.value||a.visitors||0,_fromCache:a._fromCache}}async getPageStatsByUrl(t,e={}){if(!this.config.baseUrl||!this.config.shareId)throw new Error("\u5BA2\u6237\u7AEF\u672A\u6B63\u786E\u521D\u59CB\u5316");let a=await this.api.getStats(this.config.baseUrl,this.config.shareId,{url:t,...e});return{pageviews:a.pageviews?.value||a.pageviews||0,visitors:a.visitors?.value||a.visitors||0,_fromCache:a._fromCache}}async getSiteStats(t={}){if(!this.config.baseUrl||!this.config.shareId)throw new Error("\u5BA2\u6237\u7AEF\u672A\u6B63\u786E\u521D\u59CB\u5316");let e=await this.api.getStats(this.config.baseUrl,this.config.shareId,t);return{pageviews:e.pageviews||0,visitors:e.visitors||0,_fromCache:e._fromCache}}clearCache(){this.cacheManager.clear(),this.api.clearShareCache()}getConfig(){return{...this.config}}updateConfig(t){this.config={...this.config,...t}}};function E(r){return new u(r)}import{readFileSync as I}from"fs";import{fileURLToPath as R}from"url";import{dirname as x,join as D}from"path";function v(r){if(!r.shareUrl)throw new Error("[oddmisc] \u9700\u8981\u914D\u7F6E shareUrl");return{name:"oddmisc-umami-integration",hooks:{"astro:config:setup":({injectScript:t})=>{let e="";try{let s=x(R(import.meta.url)),i=D(s,"./runtime/client.global.js");e=I(i,"utf-8")}catch{console.warn("[oddmisc] \u65E0\u6CD5\u8BFB\u53D6\u8FD0\u884C\u65F6\u6587\u4EF6\uFF0C\u4F7F\u7528\u5907\u7528\u65B9\u6848")}let a=`
2
2
  // oddmisc Umami Runtime
3
3
  ${e}
4
4
 
@@ -6,5 +6,5 @@ ${e}
6
6
  if (typeof window !== 'undefined') {
7
7
  __oddmiscRuntime.initUmamiRuntime(${JSON.stringify({shareUrl:r.shareUrl})});
8
8
  }
9
- `;t("page",a)}}}}function $(r){let t=new URL(r),e=t.pathname.split("/"),a=e.indexOf("share");if(a===-1||a===e.length-1)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB URL\uFF1A\u672A\u627E\u5230 share \u8DEF\u5F84");let s=e[a+1];if(!s||s.length<10)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB ID");let o=e.slice(0,a).join("/")+"/api";return{apiBase:`${t.protocol}//${t.host}${o}`,shareId:s}}var w=class{constructor(t,e){this.cache=new Map;this.storageKey=t,this.ttl=e,this.loadFromStorage()}loadFromStorage(){try{let t=localStorage.getItem(this.storageKey);if(t){let e=JSON.parse(t),a=Date.now();for(let[s,i]of Object.entries(e))a-i.timestamp<this.ttl&&this.cache.set(s,i)}}catch{}}saveToStorage(){try{let t={};this.cache.forEach((e,a)=>{t[a]=e}),localStorage.setItem(this.storageKey,JSON.stringify(t))}catch{}}get(t){let e=this.cache.get(t);return e&&Date.now()-e.timestamp<this.ttl?e.value:null}set(t,e){this.cache.set(t,{value:e,timestamp:Date.now()}),this.saveToStorage()}clear(){this.cache.clear();try{localStorage.removeItem(this.storageKey)}catch{}}},S=class{constructor(t){this.shareData=null;this.sharePromise=null;let{apiBase:e,shareId:a}=$(t.shareUrl);this.apiBase=e,this.shareId=a,this.cache=new w(`umami-runtime-${a}`,36e5)}async getShareData(){return this.shareData?this.shareData:this.sharePromise?this.sharePromise:(this.sharePromise=(async()=>{let t=await fetch(`${this.apiBase}/share/${this.shareId}`);if(!t.ok)throw this.sharePromise=null,new Error(`\u83B7\u53D6\u5206\u4EAB\u4FE1\u606F\u5931\u8D25: ${t.status}`);let e=await t.json();return this.shareData=e,e})(),this.sharePromise)}async getStats(t){let e=t?`stats-${t}`:"stats-site",a=this.cache.get(e);if(a)return{...a,_fromCache:!0};let{websiteId:s,token:i}=await this.getShareData(),o=new URLSearchParams({startAt:"0",endAt:Date.now().toString()});t&&o.set("path",`eq.${t}`);let n=await fetch(`${this.apiBase}/websites/${s}/stats?${o.toString()}`,{headers:{"x-umami-share-token":i}});if(!n.ok)throw new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${n.status}`);let h=await n.json(),l={pageviews:h.pageviews?.value??h.pageviews??0,visitors:h.visitors?.value??h.visitors??0};return this.cache.set(e,l),l}async getSiteStats(){return this.getStats()}async getPageStats(t){return this.getStats(t)}clearCache(){this.cache.clear(),this.shareData=null,this.sharePromise=null}};function b(r){let t=new S(r);window.oddmisc=window.oddmisc||{},window.oddmisc.umami=t,window.oddmisc.getStats=e=>t.getStats(e),window.oddmisc.getSiteStats=()=>t.getSiteStats(),window.oddmisc.getPageStats=e=>t.getPageStats(e),window.oddmisc.clearCache=()=>t.clearCache(),console.log("[oddmisc] Umami runtime client initialized")}export{m as CacheManager,u as UmamiClient,f as UmamiError,C as VERSION,E as createUmamiClient,b as initUmamiRuntime,U as isValidConfig,P as isValidShareUrl,d as parseShareUrl,v as umami};
9
+ `;t("page",a)}}}}function $(r){let t=new URL(r),e=t.pathname.split("/"),a=e.indexOf("share");if(a===-1||a===e.length-1)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB URL\uFF1A\u672A\u627E\u5230 share \u8DEF\u5F84");let s=e[a+1];if(!s||s.length<10)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB ID");let n=e.slice(0,a).join("/")+"/api";return{apiBase:`${t.protocol}//${t.host}${n}`,shareId:s}}var w=class{constructor(t,e){this.cache=new Map;this.storageKey=t,this.ttl=e,this.loadFromStorage()}loadFromStorage(){try{let t=localStorage.getItem(this.storageKey);if(t){let e=JSON.parse(t),a=Date.now();for(let[s,i]of Object.entries(e))a-i.timestamp<this.ttl&&this.cache.set(s,i)}}catch{}}saveToStorage(){try{let t={};this.cache.forEach((e,a)=>{t[a]=e}),localStorage.setItem(this.storageKey,JSON.stringify(t))}catch{}}get(t){let e=this.cache.get(t);return e&&Date.now()-e.timestamp<this.ttl?e.value:null}set(t,e){this.cache.set(t,{value:e,timestamp:Date.now()}),this.saveToStorage()}clear(){this.cache.clear();try{localStorage.removeItem(this.storageKey)}catch{}}},S=class{constructor(t){this.shareData=null;this.sharePromise=null;let{apiBase:e,shareId:a}=$(t.shareUrl);this.apiBase=e,this.shareId=a,this.cache=new w(`umami-runtime-${a}`,36e5)}async getShareData(){return this.shareData?this.shareData:this.sharePromise?this.sharePromise:(this.sharePromise=(async()=>{let t=await fetch(`${this.apiBase}/share/${this.shareId}`);if(!t.ok)throw this.sharePromise=null,new Error(`\u83B7\u53D6\u5206\u4EAB\u4FE1\u606F\u5931\u8D25: ${t.status}`);let e=await t.json();return this.shareData=e,e})(),this.sharePromise)}async getStats(t){let e=t?`stats-${t}`:"stats-site",a=this.cache.get(e);if(a)return{...a,_fromCache:!0};let{websiteId:s,token:i}=await this.getShareData(),n=new URLSearchParams({startAt:"0",endAt:Date.now().toString()});t&&n.set("path",`eq.${t}`);let h=await fetch(`${this.apiBase}/websites/${s}/stats?${n.toString()}`,{headers:{"x-umami-share-token":i}});if(!h.ok)throw new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${h.status}`);let o=await h.json(),l={pageviews:o.pageviews?.value??o.pageviews??0,visitors:o.visitors?.value??o.visitors??0,visits:o.visits?.value??o.visits??0};return this.cache.set(e,l),l}async getSiteStats(){return this.getStats()}async getPageStats(t){return this.getStats(t)}clearCache(){this.cache.clear(),this.shareData=null,this.sharePromise=null}};function b(r){let t=new S(r);window.oddmisc=window.oddmisc||{},window.oddmisc.umami=t,window.oddmisc.getStats=e=>t.getStats(e),window.oddmisc.getSiteStats=()=>t.getSiteStats(),window.oddmisc.getPageStats=e=>t.getPageStats(e),window.oddmisc.clearCache=()=>t.clearCache(),console.log("[oddmisc] Umami runtime client initialized")}export{m as CacheManager,u as UmamiClient,f as UmamiError,C as VERSION,E as createUmamiClient,b as initUmamiRuntime,U as isValidConfig,P as isValidShareUrl,d as parseShareUrl,v as umami};
10
10
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/index.ts","../src/utils/umami/cache.ts","../src/modules/umami/api.ts","../src/utils/umami/url-parser.ts","../src/modules/umami/client.ts","../src/astro/integration.ts","../src/runtime/client.ts"],"sourcesContent":["// 版本信息\nexport const VERSION = '1.0.0';\n\n// 配置验证\nexport function isValidConfig(config: any): boolean {\n return (\n typeof config === 'object' &&\n config !== null &&\n typeof config.baseUrl === 'string' &&\n typeof config.shareId === 'string' &&\n config.baseUrl.length > 0 &&\n config.shareId.length > 0\n );\n}\n\n// 自定义错误类\nexport class UmamiError extends Error {\n constructor(message: string, public code?: string) {\n super(message);\n this.name = 'UmamiError';\n }\n}","// 检测是否在浏览器环境\nconst isBrowser = typeof window !== 'undefined' && typeof localStorage !== 'undefined';\n\n// 通用缓存管理器\nexport class CacheManager<T = any> {\n private memoryCache = new Map<string, T>();\n private readonly CACHE_KEY: string;\n private readonly DEFAULT_TTL: number;\n\n constructor(namespace = 'default', ttl = 3600000) {\n this.CACHE_KEY = `cache-${namespace}`;\n this.DEFAULT_TTL = ttl;\n }\n\n get(key: string): T | null {\n // 内存缓存(SSR 和浏览器都可用)\n if (this.memoryCache.has(key)) {\n return this.memoryCache.get(key)!;\n }\n \n // localStorage 缓存(仅浏览器环境)\n if (isBrowser) {\n try {\n const cached = localStorage.getItem(this.CACHE_KEY);\n if (cached) {\n const parsed = JSON.parse(cached);\n const cachedData = parsed[key];\n if (cachedData && Date.now() - cachedData.timestamp < this.DEFAULT_TTL) {\n // 命中 localStorage 缓存时,同步到内存缓存\n this.memoryCache.set(key, cachedData.value);\n return cachedData.value;\n }\n }\n } catch {\n // localStorage 访问失败时忽略\n }\n }\n \n return null;\n }\n\n set(key: string, value: T): void {\n // 内存缓存(SSR 和浏览器都可用)\n this.memoryCache.set(key, value);\n \n // localStorage 缓存(仅浏览器环境)\n if (isBrowser) {\n try {\n const cached = localStorage.getItem(this.CACHE_KEY);\n const cacheObj = cached ? JSON.parse(cached) : {};\n \n cacheObj[key] = {\n timestamp: Date.now(),\n value: value\n };\n \n localStorage.setItem(this.CACHE_KEY, JSON.stringify(cacheObj));\n } catch {\n // localStorage 访问失败时忽略\n }\n }\n }\n\n clear(): void {\n this.memoryCache.clear();\n if (isBrowser) {\n try {\n localStorage.removeItem(this.CACHE_KEY);\n } catch {\n // localStorage 访问失败时忽略\n }\n }\n }\n\n delete(key: string): void {\n this.memoryCache.delete(key);\n if (isBrowser) {\n try {\n const cached = localStorage.getItem(this.CACHE_KEY);\n if (cached) {\n const cacheObj = JSON.parse(cached);\n delete cacheObj[key];\n localStorage.setItem(this.CACHE_KEY, JSON.stringify(cacheObj));\n }\n } catch {\n // localStorage 访问失败时忽略\n }\n }\n }\n}","import type { ShareData } from './types';\nimport { CacheManager } from '../../utils/umami/cache';\n\nexport class UmamiAPI {\n private cacheManager: CacheManager;\n private sharePromise: Promise<ShareData> | null = null;\n\n constructor(cacheManager: CacheManager) {\n this.cacheManager = cacheManager;\n }\n\n async getShareData(baseUrl: string, shareId: string): Promise<ShareData> {\n if (!this.sharePromise) {\n this.sharePromise = this.fetchShareData(baseUrl, shareId).catch((err) => {\n this.sharePromise = null;\n throw err;\n });\n }\n return this.sharePromise;\n }\n\n private async fetchShareData(baseUrl: string, shareId: string): Promise<ShareData> {\n const res = await fetch(`${baseUrl}/share/${shareId}`);\n if (!res.ok) {\n throw new Error(`获取分享信息失败: ${res.status} ${res.statusText}`);\n }\n return res.json();\n }\n\n async getStats(baseUrl: string, shareId: string, params: any) {\n const cacheKey = `${baseUrl}|${shareId}|${JSON.stringify(params)}`;\n \n const cached = this.cacheManager.get(cacheKey);\n if (cached) {\n return { ...cached, _fromCache: true };\n }\n\n const { websiteId, token } = await this.getShareData(baseUrl, shareId);\n \n const queryParams = new URLSearchParams({\n startAt: '0',\n endAt: Date.now().toString()\n });\n\n // 添加可选的 path 参数\n if (params.path) {\n queryParams.set('path', params.path);\n }\n\n const statsUrl = `${baseUrl}/websites/${websiteId}/stats?${queryParams.toString()}`;\n \n const res = await fetch(statsUrl, {\n headers: { 'x-umami-share-token': token }\n });\n\n if (!res.ok) {\n if (res.status === 401) {\n this.cacheManager.clear();\n throw new Error('认证失败,请检查 shareId');\n }\n throw new Error(`获取统计失败: ${res.status} ${res.statusText}`);\n }\n\n const data = await res.json();\n this.cacheManager.set(cacheKey, data);\n return data;\n }\n\n clearShareCache(): void {\n this.sharePromise = null;\n }\n}","/**\n * 从分享 URL 中提取 apiBase 和 shareId\n * 支持多种 Umami 实例格式:\n * - https://umami.example.com/share/abc123 → apiBase: https://umami.example.com/api\n * - https://cloud.umami.is/analytics/us/share/abc123 → apiBase: https://cloud.umami.is/analytics/us/api\n */\nexport function parseShareUrl(shareUrl: string): { apiBase: string; shareId: string } {\n try {\n const url = new URL(shareUrl);\n \n // 提取 shareId(/share/ 后面的部分)\n const pathParts = url.pathname.split('/');\n const shareIndex = pathParts.indexOf('share');\n \n if (shareIndex === -1 || shareIndex === pathParts.length - 1) {\n throw new Error('无效的分享 URL:未找到 share 路径');\n }\n \n const shareId = pathParts[shareIndex + 1];\n \n if (!shareId || shareId.length < 10) {\n throw new Error('无效的分享 ID');\n }\n \n // 构造 apiBase:去掉 /share/{shareId},加上 /api\n // 例如: /analytics/us/share/abc123 → /analytics/us/api\n const pathBeforeShare = pathParts.slice(0, shareIndex).join('/');\n const apiPath = pathBeforeShare + '/api';\n const apiBase = `${url.protocol}//${url.host}${apiPath}`;\n \n return { apiBase, shareId };\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`URL 解析失败: ${error.message}`);\n }\n throw new Error('URL 解析失败: 无效的 URL 格式');\n }\n}\n\n/**\n * 验证分享 URL 格式\n */\nexport function isValidShareUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return parsed.pathname.includes('/share/');\n } catch {\n return false;\n }\n}","import { CacheManager } from '../../utils/umami/cache';\nimport { UmamiAPI } from './api';\nimport { parseShareUrl } from '../../utils/umami/url-parser';\nimport type { UmamiConfig, StatsResult, StatsQueryParams } from './types';\n\nexport class UmamiClient {\n private config: UmamiConfig;\n private cacheManager: CacheManager;\n private api: UmamiAPI;\n\n constructor(config: UmamiConfig) {\n if (!config.shareUrl) {\n throw new Error('shareUrl 是必需参数');\n }\n \n const { apiBase, shareId } = parseShareUrl(config.shareUrl);\n \n this.config = {\n baseUrl: apiBase,\n shareId,\n ...config\n };\n \n this.cacheManager = new CacheManager('umami', 3600000);\n this.api = new UmamiAPI(this.cacheManager);\n }\n\n async getPageStats(\n path: string, \n options: Partial<StatsQueryParams> = {}\n ): Promise<StatsResult> {\n if (!this.config.baseUrl || !this.config.shareId) {\n throw new Error('客户端未正确初始化');\n }\n \n const data = await this.api.getStats(this.config.baseUrl, this.config.shareId, {\n path: `eq.${path}`,\n ...options\n });\n \n return {\n pageviews: (data.pageviews?.value) || data.pageviews || 0,\n visitors: (data.visitors?.value) || data.visitors || 0,\n _fromCache: data._fromCache\n };\n }\n\n async getPageStatsByUrl(\n url: string,\n options: Partial<StatsQueryParams> = {}\n ): Promise<StatsResult> {\n if (!this.config.baseUrl || !this.config.shareId) {\n throw new Error('客户端未正确初始化');\n }\n \n const data = await this.api.getStats(this.config.baseUrl, this.config.shareId, {\n url: url,\n ...options\n });\n \n return {\n pageviews: (data.pageviews?.value) || data.pageviews || 0,\n visitors: (data.visitors?.value) || data.visitors || 0,\n _fromCache: data._fromCache\n };\n }\n\n async getSiteStats(options: Partial<StatsQueryParams> = {}): Promise<StatsResult> {\n if (!this.config.baseUrl || !this.config.shareId) {\n throw new Error('客户端未正确初始化');\n }\n \n const data = await this.api.getStats(this.config.baseUrl, this.config.shareId, options);\n \n return {\n pageviews: data.pageviews || 0,\n visitors: data.visitors || 0,\n _fromCache: data._fromCache\n };\n }\n\n clearCache(): void {\n this.cacheManager.clear();\n this.api.clearShareCache();\n }\n\n getConfig(): Readonly<UmamiConfig> {\n return { ...this.config };\n }\n\n updateConfig(newConfig: Partial<UmamiConfig>): void {\n this.config = { ...this.config, ...newConfig };\n }\n}\n\nexport function createUmamiClient(config: UmamiConfig): UmamiClient {\n return new UmamiClient(config);\n}","import { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\n\n// Astro 集成配置\nexport interface UmamiIntegrationOptions {\n shareUrl: string; // Umami 分享链接\n}\n\n// Astro 集成函数\nexport function umami(options: UmamiIntegrationOptions) {\n if (!options.shareUrl) {\n throw new Error('[oddmisc] 需要配置 shareUrl');\n }\n\n return {\n name: 'oddmisc-umami-integration',\n hooks: {\n 'astro:config:setup': ({ injectScript }: any) => {\n // 读取运行时代码\n let runtimeCode = '';\n try {\n const __dirname = dirname(fileURLToPath(import.meta.url));\n const runtimePath = join(__dirname, './runtime/client.global.js');\n runtimeCode = readFileSync(runtimePath, 'utf-8');\n } catch {\n console.warn('[oddmisc] 无法读取运行时文件,使用备用方案');\n }\n\n // 注入运行时 + 初始化配置\n const initCode = `\n// oddmisc Umami Runtime\n${runtimeCode}\n\n// 初始化\nif (typeof window !== 'undefined') {\n __oddmiscRuntime.initUmamiRuntime(${JSON.stringify({ shareUrl: options.shareUrl })});\n}\n`;\n\n injectScript('page', initCode);\n }\n }\n };\n}","/**\n * 浏览器运行时客户端\n * 用于 Astro 集成注入到页面中\n * \n * 注意:此文件会被内联注入到页面,不能有外部依赖\n */\n\ninterface UmamiRuntimeConfig {\n shareUrl: string;\n}\n\ninterface StatsResult {\n pageviews: number;\n visitors: number;\n visits?: number;\n _fromCache?: boolean;\n}\n\ninterface ShareData {\n websiteId: string;\n token: string;\n}\n\n// 解析分享 URL\nfunction parseShareUrl(shareUrl: string): { apiBase: string; shareId: string } {\n const url = new URL(shareUrl);\n const pathParts = url.pathname.split('/');\n const shareIndex = pathParts.indexOf('share');\n \n if (shareIndex === -1 || shareIndex === pathParts.length - 1) {\n throw new Error('无效的分享 URL:未找到 share 路径');\n }\n \n const shareId = pathParts[shareIndex + 1];\n \n if (!shareId || shareId.length < 10) {\n throw new Error('无效的分享 ID');\n }\n \n // 构造 apiBase:去掉 /share/{shareId},加上 /api\n const pathBeforeShare = pathParts.slice(0, shareIndex).join('/');\n const apiPath = pathBeforeShare + '/api';\n const apiBase = `${url.protocol}//${url.host}${apiPath}`;\n \n return { apiBase, shareId };\n}\n\n// 简单的缓存管理\nclass SimpleCache {\n private cache = new Map<string, { value: any; timestamp: number }>();\n private storageKey: string;\n private ttl: number;\n\n constructor(storageKey: string, ttl: number) {\n this.storageKey = storageKey;\n this.ttl = ttl;\n this.loadFromStorage();\n }\n\n private loadFromStorage(): void {\n try {\n const stored = localStorage.getItem(this.storageKey);\n if (stored) {\n const parsed = JSON.parse(stored);\n const now = Date.now();\n for (const [key, data] of Object.entries(parsed)) {\n if (now - (data as any).timestamp < this.ttl) {\n this.cache.set(key, data as { value: any; timestamp: number });\n }\n }\n }\n } catch {\n // ignore\n }\n }\n\n private saveToStorage(): void {\n try {\n const obj: Record<string, any> = {};\n this.cache.forEach((value, key) => {\n obj[key] = value;\n });\n localStorage.setItem(this.storageKey, JSON.stringify(obj));\n } catch {\n // ignore\n }\n }\n\n get(key: string): any | null {\n const cached = this.cache.get(key);\n if (cached && Date.now() - cached.timestamp < this.ttl) {\n return cached.value;\n }\n return null;\n }\n\n set(key: string, value: any): void {\n this.cache.set(key, { value, timestamp: Date.now() });\n this.saveToStorage();\n }\n\n clear(): void {\n this.cache.clear();\n try {\n localStorage.removeItem(this.storageKey);\n } catch {\n // ignore\n }\n }\n}\n\n// Umami 运行时客户端\nclass UmamiRuntimeClient {\n private apiBase: string;\n private shareId: string;\n private cache: SimpleCache;\n private shareData: ShareData | null = null;\n private sharePromise: Promise<ShareData> | null = null;\n\n constructor(config: UmamiRuntimeConfig) {\n const { apiBase, shareId } = parseShareUrl(config.shareUrl);\n this.apiBase = apiBase;\n this.shareId = shareId;\n this.cache = new SimpleCache(`umami-runtime-${shareId}`, 3600000);\n }\n\n private async getShareData(): Promise<ShareData> {\n if (this.shareData) {\n return this.shareData;\n }\n\n if (this.sharePromise) {\n return this.sharePromise;\n }\n\n this.sharePromise = (async (): Promise<ShareData> => {\n const res = await fetch(`${this.apiBase}/share/${this.shareId}`);\n if (!res.ok) {\n this.sharePromise = null;\n throw new Error(`获取分享信息失败: ${res.status}`);\n }\n const data = await res.json();\n this.shareData = data;\n return data;\n })();\n\n return this.sharePromise;\n }\n\n async getStats(path?: string): Promise<StatsResult> {\n const cacheKey = path ? `stats-${path}` : 'stats-site';\n \n const cached = this.cache.get(cacheKey);\n if (cached) {\n return { ...cached, _fromCache: true };\n }\n\n const { websiteId, token } = await this.getShareData();\n \n const params = new URLSearchParams({\n startAt: '0',\n endAt: Date.now().toString()\n });\n\n if (path) {\n params.set('path', `eq.${path}`);\n }\n\n const res = await fetch(\n `${this.apiBase}/websites/${websiteId}/stats?${params.toString()}`,\n {\n headers: { 'x-umami-share-token': token }\n }\n );\n\n if (!res.ok) {\n throw new Error(`获取统计失败: ${res.status}`);\n }\n\n const data = await res.json();\n \n const result: StatsResult = {\n pageviews: data.pageviews?.value ?? data.pageviews ?? 0,\n visitors: data.visitors?.value ?? data.visitors ?? 0\n };\n\n this.cache.set(cacheKey, result);\n\n return result;\n }\n\n async getSiteStats(): Promise<StatsResult> {\n return this.getStats();\n }\n\n async getPageStats(path: string): Promise<StatsResult> {\n return this.getStats(path);\n }\n\n clearCache(): void {\n this.cache.clear();\n this.shareData = null;\n this.sharePromise = null;\n }\n}\n\n// 初始化函数 - 挂载到 window.oddmisc\nexport function initUmamiRuntime(config: UmamiRuntimeConfig): void {\n const client = new UmamiRuntimeClient(config);\n \n (window as any).oddmisc = (window as any).oddmisc || {};\n (window as any).oddmisc.umami = client;\n (window as any).oddmisc.getStats = (path?: string) => client.getStats(path);\n (window as any).oddmisc.getSiteStats = () => client.getSiteStats();\n (window as any).oddmisc.getPageStats = (path: string) => client.getPageStats(path);\n (window as any).oddmisc.clearCache = () => client.clearCache();\n \n console.log('[oddmisc] Umami runtime client initialized');\n}\n\nexport type { UmamiRuntimeConfig, StatsResult };\n"],"mappings":"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","names":["VERSION","isValidConfig","config","UmamiError","message","code","isBrowser","CacheManager","namespace","ttl","key","cached","cachedData","value","cacheObj","UmamiAPI","cacheManager","baseUrl","shareId","err","res","params","cacheKey","cached","websiteId","token","queryParams","statsUrl","data","parseShareUrl","shareUrl","url","pathParts","shareIndex","shareId","apiPath","error","isValidShareUrl","UmamiClient","config","apiBase","shareId","parseShareUrl","CacheManager","UmamiAPI","path","options","data","url","newConfig","createUmamiClient","readFileSync","fileURLToPath","dirname","join","umami","options","injectScript","runtimeCode","__dirname","runtimePath","initCode","parseShareUrl","shareUrl","url","pathParts","shareIndex","shareId","apiPath","SimpleCache","storageKey","ttl","stored","parsed","now","key","data","obj","value","cached","UmamiRuntimeClient","config","apiBase","res","path","cacheKey","websiteId","token","params","result","initUmamiRuntime","client"]}
1
+ {"version":3,"sources":["../src/shared/index.ts","../src/utils/umami/cache.ts","../src/modules/umami/api.ts","../src/utils/umami/url-parser.ts","../src/modules/umami/client.ts","../src/astro/integration.ts","../src/runtime/client.ts"],"sourcesContent":["// 版本信息\nexport const VERSION = '1.0.0';\n\n// 配置验证\nexport function isValidConfig(config: any): boolean {\n return (\n typeof config === 'object' &&\n config !== null &&\n typeof config.baseUrl === 'string' &&\n typeof config.shareId === 'string' &&\n config.baseUrl.length > 0 &&\n config.shareId.length > 0\n );\n}\n\n// 自定义错误类\nexport class UmamiError extends Error {\n constructor(message: string, public code?: string) {\n super(message);\n this.name = 'UmamiError';\n }\n}","// 检测是否在浏览器环境\nconst isBrowser = typeof window !== 'undefined' && typeof localStorage !== 'undefined';\n\n// 通用缓存管理器\nexport class CacheManager<T = any> {\n private memoryCache = new Map<string, T>();\n private readonly CACHE_KEY: string;\n private readonly DEFAULT_TTL: number;\n\n constructor(namespace = 'default', ttl = 3600000) {\n this.CACHE_KEY = `cache-${namespace}`;\n this.DEFAULT_TTL = ttl;\n }\n\n get(key: string): T | null {\n // 内存缓存(SSR 和浏览器都可用)\n if (this.memoryCache.has(key)) {\n return this.memoryCache.get(key)!;\n }\n \n // localStorage 缓存(仅浏览器环境)\n if (isBrowser) {\n try {\n const cached = localStorage.getItem(this.CACHE_KEY);\n if (cached) {\n const parsed = JSON.parse(cached);\n const cachedData = parsed[key];\n if (cachedData && Date.now() - cachedData.timestamp < this.DEFAULT_TTL) {\n // 命中 localStorage 缓存时,同步到内存缓存\n this.memoryCache.set(key, cachedData.value);\n return cachedData.value;\n }\n }\n } catch {\n // localStorage 访问失败时忽略\n }\n }\n \n return null;\n }\n\n set(key: string, value: T): void {\n // 内存缓存(SSR 和浏览器都可用)\n this.memoryCache.set(key, value);\n \n // localStorage 缓存(仅浏览器环境)\n if (isBrowser) {\n try {\n const cached = localStorage.getItem(this.CACHE_KEY);\n const cacheObj = cached ? JSON.parse(cached) : {};\n \n cacheObj[key] = {\n timestamp: Date.now(),\n value: value\n };\n \n localStorage.setItem(this.CACHE_KEY, JSON.stringify(cacheObj));\n } catch {\n // localStorage 访问失败时忽略\n }\n }\n }\n\n clear(): void {\n this.memoryCache.clear();\n if (isBrowser) {\n try {\n localStorage.removeItem(this.CACHE_KEY);\n } catch {\n // localStorage 访问失败时忽略\n }\n }\n }\n\n delete(key: string): void {\n this.memoryCache.delete(key);\n if (isBrowser) {\n try {\n const cached = localStorage.getItem(this.CACHE_KEY);\n if (cached) {\n const cacheObj = JSON.parse(cached);\n delete cacheObj[key];\n localStorage.setItem(this.CACHE_KEY, JSON.stringify(cacheObj));\n }\n } catch {\n // localStorage 访问失败时忽略\n }\n }\n }\n}","import type { ShareData } from './types';\nimport { CacheManager } from '../../utils/umami/cache';\n\nexport class UmamiAPI {\n private cacheManager: CacheManager;\n private sharePromise: Promise<ShareData> | null = null;\n\n constructor(cacheManager: CacheManager) {\n this.cacheManager = cacheManager;\n }\n\n async getShareData(baseUrl: string, shareId: string): Promise<ShareData> {\n if (!this.sharePromise) {\n this.sharePromise = this.fetchShareData(baseUrl, shareId).catch((err) => {\n this.sharePromise = null;\n throw err;\n });\n }\n return this.sharePromise;\n }\n\n private async fetchShareData(baseUrl: string, shareId: string): Promise<ShareData> {\n const res = await fetch(`${baseUrl}/share/${shareId}`);\n if (!res.ok) {\n throw new Error(`获取分享信息失败: ${res.status} ${res.statusText}`);\n }\n return res.json();\n }\n\n async getStats(baseUrl: string, shareId: string, params: any) {\n const cacheKey = `${baseUrl}|${shareId}|${JSON.stringify(params)}`;\n \n const cached = this.cacheManager.get(cacheKey);\n if (cached) {\n return { ...cached, _fromCache: true };\n }\n\n const { websiteId, token } = await this.getShareData(baseUrl, shareId);\n \n const queryParams = new URLSearchParams({\n startAt: '0',\n endAt: Date.now().toString()\n });\n\n // 添加可选的 path 参数\n if (params.path) {\n queryParams.set('path', params.path);\n }\n\n const statsUrl = `${baseUrl}/websites/${websiteId}/stats?${queryParams.toString()}`;\n \n const res = await fetch(statsUrl, {\n headers: { 'x-umami-share-token': token }\n });\n\n if (!res.ok) {\n if (res.status === 401) {\n this.cacheManager.clear();\n throw new Error('认证失败,请检查 shareId');\n }\n throw new Error(`获取统计失败: ${res.status} ${res.statusText}`);\n }\n\n const data = await res.json();\n this.cacheManager.set(cacheKey, data);\n return data;\n }\n\n clearShareCache(): void {\n this.sharePromise = null;\n }\n}","/**\n * 从分享 URL 中提取 apiBase 和 shareId\n * 支持多种 Umami 实例格式:\n * - https://umami.example.com/share/abc123 → apiBase: https://umami.example.com/api\n * - https://cloud.umami.is/analytics/us/share/abc123 → apiBase: https://cloud.umami.is/analytics/us/api\n */\nexport function parseShareUrl(shareUrl: string): { apiBase: string; shareId: string } {\n try {\n const url = new URL(shareUrl);\n \n // 提取 shareId(/share/ 后面的部分)\n const pathParts = url.pathname.split('/');\n const shareIndex = pathParts.indexOf('share');\n \n if (shareIndex === -1 || shareIndex === pathParts.length - 1) {\n throw new Error('无效的分享 URL:未找到 share 路径');\n }\n \n const shareId = pathParts[shareIndex + 1];\n \n if (!shareId || shareId.length < 10) {\n throw new Error('无效的分享 ID');\n }\n \n // 构造 apiBase:去掉 /share/{shareId},加上 /api\n // 例如: /analytics/us/share/abc123 → /analytics/us/api\n const pathBeforeShare = pathParts.slice(0, shareIndex).join('/');\n const apiPath = pathBeforeShare + '/api';\n const apiBase = `${url.protocol}//${url.host}${apiPath}`;\n \n return { apiBase, shareId };\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`URL 解析失败: ${error.message}`);\n }\n throw new Error('URL 解析失败: 无效的 URL 格式');\n }\n}\n\n/**\n * 验证分享 URL 格式\n */\nexport function isValidShareUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return parsed.pathname.includes('/share/');\n } catch {\n return false;\n }\n}","import { CacheManager } from '../../utils/umami/cache';\nimport { UmamiAPI } from './api';\nimport { parseShareUrl } from '../../utils/umami/url-parser';\nimport type { UmamiConfig, StatsResult, StatsQueryParams } from './types';\n\nexport class UmamiClient {\n private config: UmamiConfig;\n private cacheManager: CacheManager;\n private api: UmamiAPI;\n\n constructor(config: UmamiConfig) {\n if (!config.shareUrl) {\n throw new Error('shareUrl 是必需参数');\n }\n \n const { apiBase, shareId } = parseShareUrl(config.shareUrl);\n \n this.config = {\n baseUrl: apiBase,\n shareId,\n ...config\n };\n \n this.cacheManager = new CacheManager('umami', 3600000);\n this.api = new UmamiAPI(this.cacheManager);\n }\n\n async getPageStats(\n path: string, \n options: Partial<StatsQueryParams> = {}\n ): Promise<StatsResult> {\n if (!this.config.baseUrl || !this.config.shareId) {\n throw new Error('客户端未正确初始化');\n }\n \n const data = await this.api.getStats(this.config.baseUrl, this.config.shareId, {\n path: `eq.${path}`,\n ...options\n });\n \n return {\n pageviews: (data.pageviews?.value) || data.pageviews || 0,\n visitors: (data.visitors?.value) || data.visitors || 0,\n _fromCache: data._fromCache\n };\n }\n\n async getPageStatsByUrl(\n url: string,\n options: Partial<StatsQueryParams> = {}\n ): Promise<StatsResult> {\n if (!this.config.baseUrl || !this.config.shareId) {\n throw new Error('客户端未正确初始化');\n }\n \n const data = await this.api.getStats(this.config.baseUrl, this.config.shareId, {\n url: url,\n ...options\n });\n \n return {\n pageviews: (data.pageviews?.value) || data.pageviews || 0,\n visitors: (data.visitors?.value) || data.visitors || 0,\n _fromCache: data._fromCache\n };\n }\n\n async getSiteStats(options: Partial<StatsQueryParams> = {}): Promise<StatsResult> {\n if (!this.config.baseUrl || !this.config.shareId) {\n throw new Error('客户端未正确初始化');\n }\n \n const data = await this.api.getStats(this.config.baseUrl, this.config.shareId, options);\n \n return {\n pageviews: data.pageviews || 0,\n visitors: data.visitors || 0,\n _fromCache: data._fromCache\n };\n }\n\n clearCache(): void {\n this.cacheManager.clear();\n this.api.clearShareCache();\n }\n\n getConfig(): Readonly<UmamiConfig> {\n return { ...this.config };\n }\n\n updateConfig(newConfig: Partial<UmamiConfig>): void {\n this.config = { ...this.config, ...newConfig };\n }\n}\n\nexport function createUmamiClient(config: UmamiConfig): UmamiClient {\n return new UmamiClient(config);\n}","import { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\n\n// Astro 集成配置\nexport interface UmamiIntegrationOptions {\n shareUrl: string; // Umami 分享链接\n}\n\n// Astro 集成函数\nexport function umami(options: UmamiIntegrationOptions) {\n if (!options.shareUrl) {\n throw new Error('[oddmisc] 需要配置 shareUrl');\n }\n\n return {\n name: 'oddmisc-umami-integration',\n hooks: {\n 'astro:config:setup': ({ injectScript }: any) => {\n // 读取运行时代码\n let runtimeCode = '';\n try {\n const __dirname = dirname(fileURLToPath(import.meta.url));\n const runtimePath = join(__dirname, './runtime/client.global.js');\n runtimeCode = readFileSync(runtimePath, 'utf-8');\n } catch {\n console.warn('[oddmisc] 无法读取运行时文件,使用备用方案');\n }\n\n // 注入运行时 + 初始化配置\n const initCode = `\n// oddmisc Umami Runtime\n${runtimeCode}\n\n// 初始化\nif (typeof window !== 'undefined') {\n __oddmiscRuntime.initUmamiRuntime(${JSON.stringify({ shareUrl: options.shareUrl })});\n}\n`;\n\n injectScript('page', initCode);\n }\n }\n };\n}","/**\n * 浏览器运行时客户端\n * 用于 Astro 集成注入到页面中\n * \n * 注意:此文件会被内联注入到页面,不能有外部依赖\n */\n\ninterface UmamiRuntimeConfig {\n shareUrl: string;\n}\n\ninterface StatsResult {\n pageviews: number;\n visitors: number;\n visits?: number;\n _fromCache?: boolean;\n}\n\ninterface ShareData {\n websiteId: string;\n token: string;\n}\n\n// 解析分享 URL\nfunction parseShareUrl(shareUrl: string): { apiBase: string; shareId: string } {\n const url = new URL(shareUrl);\n const pathParts = url.pathname.split('/');\n const shareIndex = pathParts.indexOf('share');\n \n if (shareIndex === -1 || shareIndex === pathParts.length - 1) {\n throw new Error('无效的分享 URL:未找到 share 路径');\n }\n \n const shareId = pathParts[shareIndex + 1];\n \n if (!shareId || shareId.length < 10) {\n throw new Error('无效的分享 ID');\n }\n \n // 构造 apiBase:去掉 /share/{shareId},加上 /api\n const pathBeforeShare = pathParts.slice(0, shareIndex).join('/');\n const apiPath = pathBeforeShare + '/api';\n const apiBase = `${url.protocol}//${url.host}${apiPath}`;\n \n return { apiBase, shareId };\n}\n\n// 简单的缓存管理\nclass SimpleCache {\n private cache = new Map<string, { value: any; timestamp: number }>();\n private storageKey: string;\n private ttl: number;\n\n constructor(storageKey: string, ttl: number) {\n this.storageKey = storageKey;\n this.ttl = ttl;\n this.loadFromStorage();\n }\n\n private loadFromStorage(): void {\n try {\n const stored = localStorage.getItem(this.storageKey);\n if (stored) {\n const parsed = JSON.parse(stored);\n const now = Date.now();\n for (const [key, data] of Object.entries(parsed)) {\n if (now - (data as any).timestamp < this.ttl) {\n this.cache.set(key, data as { value: any; timestamp: number });\n }\n }\n }\n } catch {\n // ignore\n }\n }\n\n private saveToStorage(): void {\n try {\n const obj: Record<string, any> = {};\n this.cache.forEach((value, key) => {\n obj[key] = value;\n });\n localStorage.setItem(this.storageKey, JSON.stringify(obj));\n } catch {\n // ignore\n }\n }\n\n get(key: string): any | null {\n const cached = this.cache.get(key);\n if (cached && Date.now() - cached.timestamp < this.ttl) {\n return cached.value;\n }\n return null;\n }\n\n set(key: string, value: any): void {\n this.cache.set(key, { value, timestamp: Date.now() });\n this.saveToStorage();\n }\n\n clear(): void {\n this.cache.clear();\n try {\n localStorage.removeItem(this.storageKey);\n } catch {\n // ignore\n }\n }\n}\n\n// Umami 运行时客户端\nclass UmamiRuntimeClient {\n private apiBase: string;\n private shareId: string;\n private cache: SimpleCache;\n private shareData: ShareData | null = null;\n private sharePromise: Promise<ShareData> | null = null;\n\n constructor(config: UmamiRuntimeConfig) {\n const { apiBase, shareId } = parseShareUrl(config.shareUrl);\n this.apiBase = apiBase;\n this.shareId = shareId;\n this.cache = new SimpleCache(`umami-runtime-${shareId}`, 3600000);\n }\n\n private async getShareData(): Promise<ShareData> {\n if (this.shareData) {\n return this.shareData;\n }\n\n if (this.sharePromise) {\n return this.sharePromise;\n }\n\n this.sharePromise = (async (): Promise<ShareData> => {\n const res = await fetch(`${this.apiBase}/share/${this.shareId}`);\n if (!res.ok) {\n this.sharePromise = null;\n throw new Error(`获取分享信息失败: ${res.status}`);\n }\n const data = await res.json();\n this.shareData = data;\n return data;\n })();\n\n return this.sharePromise;\n }\n\n async getStats(path?: string): Promise<StatsResult> {\n const cacheKey = path ? `stats-${path}` : 'stats-site';\n \n const cached = this.cache.get(cacheKey);\n if (cached) {\n return { ...cached, _fromCache: true };\n }\n\n const { websiteId, token } = await this.getShareData();\n \n const params = new URLSearchParams({\n startAt: '0',\n endAt: Date.now().toString()\n });\n\n if (path) {\n params.set('path', `eq.${path}`);\n }\n\n const res = await fetch(\n `${this.apiBase}/websites/${websiteId}/stats?${params.toString()}`,\n {\n headers: { 'x-umami-share-token': token }\n }\n );\n\n if (!res.ok) {\n throw new Error(`获取统计失败: ${res.status}`);\n }\n\n const data = await res.json();\n \n const result: StatsResult = {\n pageviews: data.pageviews?.value ?? data.pageviews ?? 0,\n visitors: data.visitors?.value ?? data.visitors ?? 0,\n visits: data.visits?.value ?? data.visits ?? 0\n };\n\n this.cache.set(cacheKey, result);\n\n return result;\n }\n\n async getSiteStats(): Promise<StatsResult> {\n return this.getStats();\n }\n\n async getPageStats(path: string): Promise<StatsResult> {\n return this.getStats(path);\n }\n\n clearCache(): void {\n this.cache.clear();\n this.shareData = null;\n this.sharePromise = null;\n }\n}\n\n// 初始化函数 - 挂载到 window.oddmisc\nexport function initUmamiRuntime(config: UmamiRuntimeConfig): void {\n const client = new UmamiRuntimeClient(config);\n \n (window as any).oddmisc = (window as any).oddmisc || {};\n (window as any).oddmisc.umami = client;\n (window as any).oddmisc.getStats = (path?: string) => client.getStats(path);\n (window as any).oddmisc.getSiteStats = () => client.getSiteStats();\n (window as any).oddmisc.getPageStats = (path: string) => client.getPageStats(path);\n (window as any).oddmisc.clearCache = () => client.clearCache();\n \n console.log('[oddmisc] Umami runtime client initialized');\n}\n\nexport type { UmamiRuntimeConfig, StatsResult };\n"],"mappings":"AACO,IAAMA,EAAU,QAGhB,SAASC,EAAcC,EAAsB,CAClD,OACE,OAAOA,GAAW,UAClBA,IAAW,MACX,OAAOA,EAAO,SAAY,UAC1B,OAAOA,EAAO,SAAY,UAC1BA,EAAO,QAAQ,OAAS,GACxBA,EAAO,QAAQ,OAAS,CAE5B,CAGO,IAAMC,EAAN,cAAyB,KAAM,CACpC,YAAYC,EAAwBC,EAAe,CACjD,MAAMD,CAAO,EADqB,UAAAC,EAElC,KAAK,KAAO,YACd,CACF,ECpBA,IAAMC,EAAY,OAAO,OAAW,KAAe,OAAO,aAAiB,IAG9DC,EAAN,KAA4B,CAKjC,YAAYC,EAAY,UAAWC,EAAM,KAAS,CAJlD,KAAQ,YAAc,IAAI,IAKxB,KAAK,UAAY,SAASD,CAAS,GACnC,KAAK,YAAcC,CACrB,CAEA,IAAIC,EAAuB,CAEzB,GAAI,KAAK,YAAY,IAAIA,CAAG,EAC1B,OAAO,KAAK,YAAY,IAAIA,CAAG,EAIjC,GAAIJ,EACF,GAAI,CACF,IAAMK,EAAS,aAAa,QAAQ,KAAK,SAAS,EAClD,GAAIA,EAAQ,CAEV,IAAMC,EADS,KAAK,MAAMD,CAAM,EACND,CAAG,EAC7B,GAAIE,GAAc,KAAK,IAAI,EAAIA,EAAW,UAAY,KAAK,YAEzD,YAAK,YAAY,IAAIF,EAAKE,EAAW,KAAK,EACnCA,EAAW,KAEtB,CACF,MAAQ,CAER,CAGF,OAAO,IACT,CAEA,IAAIF,EAAaG,EAAgB,CAK/B,GAHA,KAAK,YAAY,IAAIH,EAAKG,CAAK,EAG3BP,EACF,GAAI,CACF,IAAMK,EAAS,aAAa,QAAQ,KAAK,SAAS,EAC5CG,EAAWH,EAAS,KAAK,MAAMA,CAAM,EAAI,CAAC,EAEhDG,EAASJ,CAAG,EAAI,CACd,UAAW,KAAK,IAAI,EACpB,MAAOG,CACT,EAEA,aAAa,QAAQ,KAAK,UAAW,KAAK,UAAUC,CAAQ,CAAC,CAC/D,MAAQ,CAER,CAEJ,CAEA,OAAc,CAEZ,GADA,KAAK,YAAY,MAAM,EACnBR,EACF,GAAI,CACF,aAAa,WAAW,KAAK,SAAS,CACxC,MAAQ,CAER,CAEJ,CAEA,OAAOI,EAAmB,CAExB,GADA,KAAK,YAAY,OAAOA,CAAG,EACvBJ,EACF,GAAI,CACF,IAAMK,EAAS,aAAa,QAAQ,KAAK,SAAS,EAClD,GAAIA,EAAQ,CACV,IAAMG,EAAW,KAAK,MAAMH,CAAM,EAClC,OAAOG,EAASJ,CAAG,EACnB,aAAa,QAAQ,KAAK,UAAW,KAAK,UAAUI,CAAQ,CAAC,CAC/D,CACF,MAAQ,CAER,CAEJ,CACF,ECtFO,IAAMC,EAAN,KAAe,CAIpB,YAAYC,EAA4B,CAFxC,KAAQ,aAA0C,KAGhD,KAAK,aAAeA,CACtB,CAEA,MAAM,aAAaC,EAAiBC,EAAqC,CACvE,OAAK,KAAK,eACR,KAAK,aAAe,KAAK,eAAeD,EAASC,CAAO,EAAE,MAAOC,GAAQ,CACvE,WAAK,aAAe,KACdA,CACR,CAAC,GAEI,KAAK,YACd,CAEA,MAAc,eAAeF,EAAiBC,EAAqC,CACjF,IAAME,EAAM,MAAM,MAAM,GAAGH,CAAO,UAAUC,CAAO,EAAE,EACrD,GAAI,CAACE,EAAI,GACP,MAAM,IAAI,MAAM,qDAAaA,EAAI,MAAM,IAAIA,EAAI,UAAU,EAAE,EAE7D,OAAOA,EAAI,KAAK,CAClB,CAEA,MAAM,SAASH,EAAiBC,EAAiBG,EAAa,CAC5D,IAAMC,EAAW,GAAGL,CAAO,IAAIC,CAAO,IAAI,KAAK,UAAUG,CAAM,CAAC,GAE1DE,EAAS,KAAK,aAAa,IAAID,CAAQ,EAC7C,GAAIC,EACF,MAAO,CAAE,GAAGA,EAAQ,WAAY,EAAK,EAGvC,GAAM,CAAE,UAAAC,EAAW,MAAAC,CAAM,EAAI,MAAM,KAAK,aAAaR,EAASC,CAAO,EAE/DQ,EAAc,IAAI,gBAAgB,CACtC,QAAS,IACT,MAAO,KAAK,IAAI,EAAE,SAAS,CAC7B,CAAC,EAGGL,EAAO,MACTK,EAAY,IAAI,OAAQL,EAAO,IAAI,EAGrC,IAAMM,EAAW,GAAGV,CAAO,aAAaO,CAAS,UAAUE,EAAY,SAAS,CAAC,GAE3EN,EAAM,MAAM,MAAMO,EAAU,CAChC,QAAS,CAAE,sBAAuBF,CAAM,CAC1C,CAAC,EAED,GAAI,CAACL,EAAI,GACP,MAAIA,EAAI,SAAW,KACjB,KAAK,aAAa,MAAM,EAClB,IAAI,MAAM,0DAAkB,GAE9B,IAAI,MAAM,yCAAWA,EAAI,MAAM,IAAIA,EAAI,UAAU,EAAE,EAG3D,IAAMQ,EAAO,MAAMR,EAAI,KAAK,EAC5B,YAAK,aAAa,IAAIE,EAAUM,CAAI,EAC7BA,CACT,CAEA,iBAAwB,CACtB,KAAK,aAAe,IACtB,CACF,ECjEO,SAASC,EAAcC,EAAwD,CACpF,GAAI,CACF,IAAMC,EAAM,IAAI,IAAID,CAAQ,EAGtBE,EAAYD,EAAI,SAAS,MAAM,GAAG,EAClCE,EAAaD,EAAU,QAAQ,OAAO,EAE5C,GAAIC,IAAe,IAAMA,IAAeD,EAAU,OAAS,EACzD,MAAM,IAAI,MAAM,+EAAwB,EAG1C,IAAME,EAAUF,EAAUC,EAAa,CAAC,EAExC,GAAI,CAACC,GAAWA,EAAQ,OAAS,GAC/B,MAAM,IAAI,MAAM,mCAAU,EAM5B,IAAMC,EADkBH,EAAU,MAAM,EAAGC,CAAU,EAAE,KAAK,GAAG,EAC7B,OAGlC,MAAO,CAAE,QAFO,GAAGF,EAAI,QAAQ,KAAKA,EAAI,IAAI,GAAGI,CAAO,GAEpC,QAAAD,CAAQ,CAC5B,OAASE,EAAO,CACd,MAAIA,aAAiB,MACb,IAAI,MAAM,iCAAaA,EAAM,OAAO,EAAE,EAExC,IAAI,MAAM,mEAAsB,CACxC,CACF,CAKO,SAASC,EAAgBN,EAAsB,CACpD,GAAI,CAEF,OADe,IAAI,IAAIA,CAAG,EACZ,SAAS,SAAS,SAAS,CAC3C,MAAQ,CACN,MAAO,EACT,CACF,CC5CO,IAAMO,EAAN,KAAkB,CAKvB,YAAYC,EAAqB,CAC/B,GAAI,CAACA,EAAO,SACV,MAAM,IAAI,MAAM,yCAAgB,EAGlC,GAAM,CAAE,QAAAC,EAAS,QAAAC,CAAQ,EAAIC,EAAcH,EAAO,QAAQ,EAE1D,KAAK,OAAS,CACZ,QAASC,EACT,QAAAC,EACA,GAAGF,CACL,EAEA,KAAK,aAAe,IAAII,EAAa,QAAS,IAAO,EACrD,KAAK,IAAM,IAAIC,EAAS,KAAK,YAAY,CAC3C,CAEA,MAAM,aACJC,EACAC,EAAqC,CAAC,EAChB,CACtB,GAAI,CAAC,KAAK,OAAO,SAAW,CAAC,KAAK,OAAO,QACvC,MAAM,IAAI,MAAM,wDAAW,EAG7B,IAAMC,EAAO,MAAM,KAAK,IAAI,SAAS,KAAK,OAAO,QAAS,KAAK,OAAO,QAAS,CAC7E,KAAM,MAAMF,CAAI,GAChB,GAAGC,CACL,CAAC,EAED,MAAO,CACL,UAAYC,EAAK,WAAW,OAAUA,EAAK,WAAa,EACxD,SAAWA,EAAK,UAAU,OAAUA,EAAK,UAAY,EACrD,WAAYA,EAAK,UACnB,CACF,CAEA,MAAM,kBACJC,EACAF,EAAqC,CAAC,EAChB,CACtB,GAAI,CAAC,KAAK,OAAO,SAAW,CAAC,KAAK,OAAO,QACvC,MAAM,IAAI,MAAM,wDAAW,EAG7B,IAAMC,EAAO,MAAM,KAAK,IAAI,SAAS,KAAK,OAAO,QAAS,KAAK,OAAO,QAAS,CAC7E,IAAKC,EACL,GAAGF,CACL,CAAC,EAED,MAAO,CACL,UAAYC,EAAK,WAAW,OAAUA,EAAK,WAAa,EACxD,SAAWA,EAAK,UAAU,OAAUA,EAAK,UAAY,EACrD,WAAYA,EAAK,UACnB,CACF,CAEA,MAAM,aAAaD,EAAqC,CAAC,EAAyB,CAChF,GAAI,CAAC,KAAK,OAAO,SAAW,CAAC,KAAK,OAAO,QACvC,MAAM,IAAI,MAAM,wDAAW,EAG7B,IAAMC,EAAO,MAAM,KAAK,IAAI,SAAS,KAAK,OAAO,QAAS,KAAK,OAAO,QAASD,CAAO,EAEtF,MAAO,CACL,UAAWC,EAAK,WAAa,EAC7B,SAAUA,EAAK,UAAY,EAC3B,WAAYA,EAAK,UACnB,CACF,CAEA,YAAmB,CACjB,KAAK,aAAa,MAAM,EACxB,KAAK,IAAI,gBAAgB,CAC3B,CAEA,WAAmC,CACjC,MAAO,CAAE,GAAG,KAAK,MAAO,CAC1B,CAEA,aAAaE,EAAuC,CAClD,KAAK,OAAS,CAAE,GAAG,KAAK,OAAQ,GAAGA,CAAU,CAC/C,CACF,EAEO,SAASC,EAAkBX,EAAkC,CAClE,OAAO,IAAID,EAAYC,CAAM,CAC/B,CCjGA,OAAS,gBAAAY,MAAoB,KAC7B,OAAS,iBAAAC,MAAqB,MAC9B,OAAS,WAAAC,EAAS,QAAAC,MAAY,OAQvB,SAASC,EAAMC,EAAkC,CACtD,GAAI,CAACA,EAAQ,SACX,MAAM,IAAI,MAAM,6CAAyB,EAG3C,MAAO,CACL,KAAM,4BACN,MAAO,CACL,qBAAsB,CAAC,CAAE,aAAAC,CAAa,IAAW,CAE/C,IAAIC,EAAc,GAClB,GAAI,CACF,IAAMC,EAAYN,EAAQD,EAAc,YAAY,GAAG,CAAC,EAClDQ,EAAcN,EAAKK,EAAW,4BAA4B,EAChED,EAAcP,EAAaS,EAAa,OAAO,CACjD,MAAQ,CACN,QAAQ,KAAK,4GAA4B,CAC3C,CAGA,IAAMC,EAAW;AAAA;AAAA,EAEvBH,CAAW;AAAA;AAAA;AAAA;AAAA,sCAIyB,KAAK,UAAU,CAAE,SAAUF,EAAQ,QAAS,CAAC,CAAC;AAAA;AAAA,EAI5EC,EAAa,OAAQI,CAAQ,CAC/B,CACF,CACF,CACF,CCpBA,SAASC,EAAcC,EAAwD,CAC7E,IAAMC,EAAM,IAAI,IAAID,CAAQ,EACtBE,EAAYD,EAAI,SAAS,MAAM,GAAG,EAClCE,EAAaD,EAAU,QAAQ,OAAO,EAE5C,GAAIC,IAAe,IAAMA,IAAeD,EAAU,OAAS,EACzD,MAAM,IAAI,MAAM,+EAAwB,EAG1C,IAAME,EAAUF,EAAUC,EAAa,CAAC,EAExC,GAAI,CAACC,GAAWA,EAAQ,OAAS,GAC/B,MAAM,IAAI,MAAM,mCAAU,EAK5B,IAAMC,EADkBH,EAAU,MAAM,EAAGC,CAAU,EAAE,KAAK,GAAG,EAC7B,OAGlC,MAAO,CAAE,QAFO,GAAGF,EAAI,QAAQ,KAAKA,EAAI,IAAI,GAAGI,CAAO,GAEpC,QAAAD,CAAQ,CAC5B,CAGA,IAAME,EAAN,KAAkB,CAKhB,YAAYC,EAAoBC,EAAa,CAJ7C,KAAQ,MAAQ,IAAI,IAKlB,KAAK,WAAaD,EAClB,KAAK,IAAMC,EACX,KAAK,gBAAgB,CACvB,CAEQ,iBAAwB,CAC9B,GAAI,CACF,IAAMC,EAAS,aAAa,QAAQ,KAAK,UAAU,EACnD,GAAIA,EAAQ,CACV,IAAMC,EAAS,KAAK,MAAMD,CAAM,EAC1BE,EAAM,KAAK,IAAI,EACrB,OAAW,CAACC,EAAKC,CAAI,IAAK,OAAO,QAAQH,CAAM,EACzCC,EAAOE,EAAa,UAAY,KAAK,KACvC,KAAK,MAAM,IAAID,EAAKC,CAAyC,CAGnE,CACF,MAAQ,CAER,CACF,CAEQ,eAAsB,CAC5B,GAAI,CACF,IAAMC,EAA2B,CAAC,EAClC,KAAK,MAAM,QAAQ,CAACC,EAAOH,IAAQ,CACjCE,EAAIF,CAAG,EAAIG,CACb,CAAC,EACD,aAAa,QAAQ,KAAK,WAAY,KAAK,UAAUD,CAAG,CAAC,CAC3D,MAAQ,CAER,CACF,CAEA,IAAIF,EAAyB,CAC3B,IAAMI,EAAS,KAAK,MAAM,IAAIJ,CAAG,EACjC,OAAII,GAAU,KAAK,IAAI,EAAIA,EAAO,UAAY,KAAK,IAC1CA,EAAO,MAET,IACT,CAEA,IAAIJ,EAAaG,EAAkB,CACjC,KAAK,MAAM,IAAIH,EAAK,CAAE,MAAAG,EAAO,UAAW,KAAK,IAAI,CAAE,CAAC,EACpD,KAAK,cAAc,CACrB,CAEA,OAAc,CACZ,KAAK,MAAM,MAAM,EACjB,GAAI,CACF,aAAa,WAAW,KAAK,UAAU,CACzC,MAAQ,CAER,CACF,CACF,EAGME,EAAN,KAAyB,CAOvB,YAAYC,EAA4B,CAHxC,KAAQ,UAA8B,KACtC,KAAQ,aAA0C,KAGhD,GAAM,CAAE,QAAAC,EAAS,QAAAf,CAAQ,EAAIL,EAAcmB,EAAO,QAAQ,EAC1D,KAAK,QAAUC,EACf,KAAK,QAAUf,EACf,KAAK,MAAQ,IAAIE,EAAY,iBAAiBF,CAAO,GAAI,IAAO,CAClE,CAEA,MAAc,cAAmC,CAC/C,OAAI,KAAK,UACA,KAAK,UAGV,KAAK,aACA,KAAK,cAGd,KAAK,cAAgB,SAAgC,CACnD,IAAMgB,EAAM,MAAM,MAAM,GAAG,KAAK,OAAO,UAAU,KAAK,OAAO,EAAE,EAC/D,GAAI,CAACA,EAAI,GACP,WAAK,aAAe,KACd,IAAI,MAAM,qDAAaA,EAAI,MAAM,EAAE,EAE3C,IAAMP,EAAO,MAAMO,EAAI,KAAK,EAC5B,YAAK,UAAYP,EACVA,CACT,GAAG,EAEI,KAAK,aACd,CAEA,MAAM,SAASQ,EAAqC,CAClD,IAAMC,EAAWD,EAAO,SAASA,CAAI,GAAK,aAEpCL,EAAS,KAAK,MAAM,IAAIM,CAAQ,EACtC,GAAIN,EACF,MAAO,CAAE,GAAGA,EAAQ,WAAY,EAAK,EAGvC,GAAM,CAAE,UAAAO,EAAW,MAAAC,CAAM,EAAI,MAAM,KAAK,aAAa,EAE/CC,EAAS,IAAI,gBAAgB,CACjC,QAAS,IACT,MAAO,KAAK,IAAI,EAAE,SAAS,CAC7B,CAAC,EAEGJ,GACFI,EAAO,IAAI,OAAQ,MAAMJ,CAAI,EAAE,EAGjC,IAAMD,EAAM,MAAM,MAChB,GAAG,KAAK,OAAO,aAAaG,CAAS,UAAUE,EAAO,SAAS,CAAC,GAChE,CACE,QAAS,CAAE,sBAAuBD,CAAM,CAC1C,CACF,EAEA,GAAI,CAACJ,EAAI,GACP,MAAM,IAAI,MAAM,yCAAWA,EAAI,MAAM,EAAE,EAGzC,IAAMP,EAAO,MAAMO,EAAI,KAAK,EAEtBM,EAAsB,CAC1B,UAAWb,EAAK,WAAW,OAASA,EAAK,WAAa,EACtD,SAAUA,EAAK,UAAU,OAASA,EAAK,UAAY,EACnD,OAAQA,EAAK,QAAQ,OAASA,EAAK,QAAU,CAC/C,EAEA,YAAK,MAAM,IAAIS,EAAUI,CAAM,EAExBA,CACT,CAEA,MAAM,cAAqC,CACzC,OAAO,KAAK,SAAS,CACvB,CAEA,MAAM,aAAaL,EAAoC,CACrD,OAAO,KAAK,SAASA,CAAI,CAC3B,CAEA,YAAmB,CACjB,KAAK,MAAM,MAAM,EACjB,KAAK,UAAY,KACjB,KAAK,aAAe,IACtB,CACF,EAGO,SAASM,EAAiBT,EAAkC,CACjE,IAAMU,EAAS,IAAIX,EAAmBC,CAAM,EAE3C,OAAe,QAAW,OAAe,SAAW,CAAC,EACrD,OAAe,QAAQ,MAAQU,EAC/B,OAAe,QAAQ,SAAYP,GAAkBO,EAAO,SAASP,CAAI,EACzE,OAAe,QAAQ,aAAe,IAAMO,EAAO,aAAa,EAChE,OAAe,QAAQ,aAAgBP,GAAiBO,EAAO,aAAaP,CAAI,EAChF,OAAe,QAAQ,WAAa,IAAMO,EAAO,WAAW,EAE7D,QAAQ,IAAI,4CAA4C,CAC1D","names":["VERSION","isValidConfig","config","UmamiError","message","code","isBrowser","CacheManager","namespace","ttl","key","cached","cachedData","value","cacheObj","UmamiAPI","cacheManager","baseUrl","shareId","err","res","params","cacheKey","cached","websiteId","token","queryParams","statsUrl","data","parseShareUrl","shareUrl","url","pathParts","shareIndex","shareId","apiPath","error","isValidShareUrl","UmamiClient","config","apiBase","shareId","parseShareUrl","CacheManager","UmamiAPI","path","options","data","url","newConfig","createUmamiClient","readFileSync","fileURLToPath","dirname","join","umami","options","injectScript","runtimeCode","__dirname","runtimePath","initCode","parseShareUrl","shareUrl","url","pathParts","shareIndex","shareId","apiPath","SimpleCache","storageKey","ttl","stored","parsed","now","key","data","obj","value","cached","UmamiRuntimeClient","config","apiBase","res","path","cacheKey","websiteId","token","params","result","initUmamiRuntime","client"]}
@@ -1 +1 @@
1
- "use strict";var __oddmiscRuntime=(()=>{var c=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var p=(s,t)=>{for(var e in t)c(s,e,{get:t[e],enumerable:!0})},S=(s,t,e,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of d(t))!w.call(s,r)&&r!==e&&c(s,r,{get:()=>t[r],enumerable:!(a=u(t,r))||a.enumerable});return s};var v=s=>S(c({},"__esModule",{value:!0}),s);var P={};p(P,{initUmamiRuntime:()=>y});function f(s){let t=new URL(s),e=t.pathname.split("/"),a=e.indexOf("share");if(a===-1||a===e.length-1)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB URL\uFF1A\u672A\u627E\u5230 share \u8DEF\u5F84");let r=e[a+1];if(!r||r.length<10)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB ID");let n=e.slice(0,a).join("/")+"/api";return{apiBase:`${t.protocol}//${t.host}${n}`,shareId:r}}var m=class{constructor(t,e){this.cache=new Map;this.storageKey=t,this.ttl=e,this.loadFromStorage()}loadFromStorage(){try{let t=localStorage.getItem(this.storageKey);if(t){let e=JSON.parse(t),a=Date.now();for(let[r,i]of Object.entries(e))a-i.timestamp<this.ttl&&this.cache.set(r,i)}}catch{}}saveToStorage(){try{let t={};this.cache.forEach((e,a)=>{t[a]=e}),localStorage.setItem(this.storageKey,JSON.stringify(t))}catch{}}get(t){let e=this.cache.get(t);return e&&Date.now()-e.timestamp<this.ttl?e.value:null}set(t,e){this.cache.set(t,{value:e,timestamp:Date.now()}),this.saveToStorage()}clear(){this.cache.clear();try{localStorage.removeItem(this.storageKey)}catch{}}},g=class{constructor(t){this.shareData=null;this.sharePromise=null;let{apiBase:e,shareId:a}=f(t.shareUrl);this.apiBase=e,this.shareId=a,this.cache=new m(`umami-runtime-${a}`,36e5)}async getShareData(){return this.shareData?this.shareData:this.sharePromise?this.sharePromise:(this.sharePromise=(async()=>{let t=await fetch(`${this.apiBase}/share/${this.shareId}`);if(!t.ok)throw this.sharePromise=null,new Error(`\u83B7\u53D6\u5206\u4EAB\u4FE1\u606F\u5931\u8D25: ${t.status}`);let e=await t.json();return this.shareData=e,e})(),this.sharePromise)}async getStats(t){let e=t?`stats-${t}`:"stats-site",a=this.cache.get(e);if(a)return{...a,_fromCache:!0};let{websiteId:r,token:i}=await this.getShareData(),n=new URLSearchParams({startAt:"0",endAt:Date.now().toString()});t&&n.set("path",`eq.${t}`);let o=await fetch(`${this.apiBase}/websites/${r}/stats?${n.toString()}`,{headers:{"x-umami-share-token":i}});if(!o.ok)throw new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${o.status}`);let h=await o.json(),l={pageviews:h.pageviews?.value??h.pageviews??0,visitors:h.visitors?.value??h.visitors??0};return this.cache.set(e,l),l}async getSiteStats(){return this.getStats()}async getPageStats(t){return this.getStats(t)}clearCache(){this.cache.clear(),this.shareData=null,this.sharePromise=null}};function y(s){let t=new g(s);window.oddmisc=window.oddmisc||{},window.oddmisc.umami=t,window.oddmisc.getStats=e=>t.getStats(e),window.oddmisc.getSiteStats=()=>t.getSiteStats(),window.oddmisc.getPageStats=e=>t.getPageStats(e),window.oddmisc.clearCache=()=>t.clearCache(),console.log("[oddmisc] Umami runtime client initialized")}return v(P);})();
1
+ "use strict";var __oddmiscRuntime=(()=>{var c=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var p=(s,t)=>{for(var e in t)c(s,e,{get:t[e],enumerable:!0})},S=(s,t,e,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of d(t))!w.call(s,i)&&i!==e&&c(s,i,{get:()=>t[i],enumerable:!(a=u(t,i))||a.enumerable});return s};var v=s=>S(c({},"__esModule",{value:!0}),s);var P={};p(P,{initUmamiRuntime:()=>y});function f(s){let t=new URL(s),e=t.pathname.split("/"),a=e.indexOf("share");if(a===-1||a===e.length-1)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB URL\uFF1A\u672A\u627E\u5230 share \u8DEF\u5F84");let i=e[a+1];if(!i||i.length<10)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB ID");let o=e.slice(0,a).join("/")+"/api";return{apiBase:`${t.protocol}//${t.host}${o}`,shareId:i}}var m=class{constructor(t,e){this.cache=new Map;this.storageKey=t,this.ttl=e,this.loadFromStorage()}loadFromStorage(){try{let t=localStorage.getItem(this.storageKey);if(t){let e=JSON.parse(t),a=Date.now();for(let[i,n]of Object.entries(e))a-n.timestamp<this.ttl&&this.cache.set(i,n)}}catch{}}saveToStorage(){try{let t={};this.cache.forEach((e,a)=>{t[a]=e}),localStorage.setItem(this.storageKey,JSON.stringify(t))}catch{}}get(t){let e=this.cache.get(t);return e&&Date.now()-e.timestamp<this.ttl?e.value:null}set(t,e){this.cache.set(t,{value:e,timestamp:Date.now()}),this.saveToStorage()}clear(){this.cache.clear();try{localStorage.removeItem(this.storageKey)}catch{}}},l=class{constructor(t){this.shareData=null;this.sharePromise=null;let{apiBase:e,shareId:a}=f(t.shareUrl);this.apiBase=e,this.shareId=a,this.cache=new m(`umami-runtime-${a}`,36e5)}async getShareData(){return this.shareData?this.shareData:this.sharePromise?this.sharePromise:(this.sharePromise=(async()=>{let t=await fetch(`${this.apiBase}/share/${this.shareId}`);if(!t.ok)throw this.sharePromise=null,new Error(`\u83B7\u53D6\u5206\u4EAB\u4FE1\u606F\u5931\u8D25: ${t.status}`);let e=await t.json();return this.shareData=e,e})(),this.sharePromise)}async getStats(t){let e=t?`stats-${t}`:"stats-site",a=this.cache.get(e);if(a)return{...a,_fromCache:!0};let{websiteId:i,token:n}=await this.getShareData(),o=new URLSearchParams({startAt:"0",endAt:Date.now().toString()});t&&o.set("path",`eq.${t}`);let h=await fetch(`${this.apiBase}/websites/${i}/stats?${o.toString()}`,{headers:{"x-umami-share-token":n}});if(!h.ok)throw new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${h.status}`);let r=await h.json(),g={pageviews:r.pageviews?.value??r.pageviews??0,visitors:r.visitors?.value??r.visitors??0,visits:r.visits?.value??r.visits??0};return this.cache.set(e,g),g}async getSiteStats(){return this.getStats()}async getPageStats(t){return this.getStats(t)}clearCache(){this.cache.clear(),this.shareData=null,this.sharePromise=null}};function y(s){let t=new l(s);window.oddmisc=window.oddmisc||{},window.oddmisc.umami=t,window.oddmisc.getStats=e=>t.getStats(e),window.oddmisc.getSiteStats=()=>t.getSiteStats(),window.oddmisc.getPageStats=e=>t.getPageStats(e),window.oddmisc.clearCache=()=>t.clearCache(),console.log("[oddmisc] Umami runtime client initialized")}return v(P);})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oddmisc",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "杂七杂八奇怪小工具 npm 包",
5
5
  "homepage": "https://github.com/yCENzh/oddmisc#readme",
6
6
  "bugs": {