oddmisc 1.0.4 → 1.1.0

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/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var S=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var _=Object.prototype.hasOwnProperty;var A=(r,t)=>{for(var e in t)S(r,e,{get:t[e],enumerable:!0})},O=(r,t,e,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of L(t))!_.call(r,s)&&s!==e&&S(r,s,{get:()=>t[s],enumerable:!(a=$(t,s))||a.enumerable});return r};var z=r=>O(S({},"__esModule",{value:!0}),r);var N={};A(N,{CacheManager:()=>h,DEFAULT_CONFIG:()=>P,UmamiClient:()=>m,UmamiError:()=>u,VERSION:()=>b,createUmamiClient:()=>R,initUmamiRuntime:()=>x,isValidConfig:()=>E,isValidShareUrl:()=>I,parseShareUrl:()=>d,umami:()=>y});module.exports=z(N);var b="1.0.0",P={timezone:"Asia/Shanghai",enableCache:!0,cacheTTL:36e5};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 u=class extends Error{constructor(e,a){super(e);this.code=a;this.name="UmamiError"}};var f=typeof window<"u"&&typeof localStorage<"u",h=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(f)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),f)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(),f)try{localStorage.removeItem(this.CACHE_KEY)}catch{}}delete(t){if(this.memoryCache.delete(t),f)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}/api/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:o}=await this.getShareData(t,e),l=new URLSearchParams({startAt:"0",endAt:Date.now().toString(),unit:"hour",timezone:a.timezone||"Asia/Shanghai",compare:"false",...a}),g=`${t}/api/websites/${n}/stats?${l.toString()}`,c=await fetch(g,{headers:{"x-umami-share-token":o}});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 C=await c.json();return this.cacheManager.set(s,C),C}clearShareCache(){this.sharePromise=null}};function d(r){try{let t=new URL(r),e=`${t.protocol}//${t.host}`,a=t.pathname.split("/"),s=a.indexOf("share");if(s===-1||s===a.length-1)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB URL\uFF1A\u672A\u627E\u5230 share \u8DEF\u5F84");let i=a[s+1];if(!i||i.length<10)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB ID");return{baseUrl:e,shareId:i}}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 m=class{constructor(t){if(!t.shareUrl)throw new Error("shareUrl \u662F\u5FC5\u9700\u53C2\u6570");let{baseUrl:e,shareId:a}=d(t.shareUrl);this.config={timezone:"Asia/Shanghai",enableCache:!0,cacheTTL:36e5,baseUrl:e,shareId:a,...t},this.cacheManager=new h("umami",this.config.cacheTTL),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}`,timezone:this.config.timezone,...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,timezone:this.config.timezone,...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,{timezone:this.config.timezone,...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 m(r)}var T=require("fs"),D=require("url"),w=require("path"),M={};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={shareUrl:r.shareUrl,timezone:r.timezone||"Asia/Shanghai",enableCache:r.enableCache!==!1,cacheTTL:r.cacheTTL||36e5},a="";try{let i=(0,w.dirname)((0,D.fileURLToPath)(M.url)),n=(0,w.join)(i,"./runtime/client.global.js");a=(0,T.readFileSync)(n,"utf-8")}catch{console.warn("[oddmisc] \u65E0\u6CD5\u8BFB\u53D6\u8FD0\u884C\u65F6\u6587\u4EF6\uFF0C\u4F7F\u7528\u5907\u7528\u65B9\u6848")}let s=`
1
+ "use strict";var S=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var _=Object.getOwnPropertyNames;var L=Object.prototype.hasOwnProperty;var A=(r,t)=>{for(var e in t)S(r,e,{get:t[e],enumerable:!0})},O=(r,t,e,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of _(t))!L.call(r,s)&&s!==e&&S(r,s,{get:()=>t[s],enumerable:!(a=$(t,s))||a.enumerable});return r};var z=r=>O(S({},"__esModule",{value:!0}),r);var M={};A(M,{CacheManager:()=>c,DEFAULT_CONFIG:()=>E,UmamiClient:()=>m,UmamiError:()=>u,VERSION:()=>P,createUmamiClient:()=>R,initUmamiRuntime:()=>x,isValidConfig:()=>I,isValidShareUrl:()=>b,parseShareUrl:()=>d,umami:()=>y});module.exports=z(M);var P="1.0.0",E={timezone:"Asia/Hong_Kong",enableCache:!0,cacheTTL:36e5};function I(r){return typeof r=="object"&&r!==null&&typeof r.baseUrl=="string"&&typeof r.shareId=="string"&&r.baseUrl.length>0&&r.shareId.length>0}var u=class extends Error{constructor(e,a){super(e);this.code=a;this.name="UmamiError"}};var p=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(p)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),p)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(),p)try{localStorage.removeItem(this.CACHE_KEY)}catch{}}delete(t){if(this.memoryCache.delete(t),p)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),l=new URLSearchParams({startAt:"0",endAt:Date.now().toString(),unit:"hour",timezone:a.timezone||"Asia/Hong_Kong",compare:"false",...a}),g=`${t}/websites/${o}/stats?${l.toString()}`,h=await fetch(g,{headers:{"x-umami-share-token":n}});if(!h.ok)throw h.status===401?(this.cacheManager.clear(),new Error("\u8BA4\u8BC1\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5 shareId")):new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${h.status} ${h.statusText}`);let U=await h.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 b(r){try{return new URL(r).pathname.includes("/share/")}catch{return!1}}var m=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={timezone:"Asia/Hong_Kong",enableCache:!0,cacheTTL:36e5,baseUrl:e,shareId:a,...t},this.cacheManager=new c("umami",this.config.cacheTTL),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}`,timezone:this.config.timezone,...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,timezone:this.config.timezone,...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,{timezone:this.config.timezone,...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 m(r)}var T=require("fs"),D=require("url"),w=require("path"),K={};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={shareUrl:r.shareUrl,timezone:r.timezone||"Asia/Hong_Kong",enableCache:r.enableCache!==!1,cacheTTL:r.cacheTTL||36e5},a="";try{let i=(0,w.dirname)((0,D.fileURLToPath)(K.url)),o=(0,w.join)(i,"./runtime/client.global.js");a=(0,T.readFileSync)(o,"utf-8")}catch{console.warn("[oddmisc] \u65E0\u6CD5\u8BFB\u53D6\u8FD0\u884C\u65F6\u6587\u4EF6\uFF0C\u4F7F\u7528\u5907\u7528\u65B9\u6848")}let s=`
2
2
  // oddmisc Umami Runtime
3
3
  ${a}
4
4
 
@@ -6,5 +6,5 @@ ${a}
6
6
  if (typeof window !== 'undefined') {
7
7
  __oddmiscRuntime.initUmamiRuntime(${JSON.stringify(e)});
8
8
  }
9
- `;t("page",s)}}}}function K(r){let t=new URL(r),e=`${t.protocol}//${t.host}`,a=t.pathname.split("/"),s=a.indexOf("share");if(s===-1||s===a.length-1)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB URL\uFF1A\u672A\u627E\u5230 share \u8DEF\u5F84");let i=a[s+1];if(!i||i.length<10)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB ID");return{baseUrl:e,shareId:i}}var U=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{}}},v=class{constructor(t){this.shareData=null;this.sharePromise=null;let{baseUrl:e,shareId:a}=K(t.shareUrl);this.baseUrl=e,this.shareId=a,this.timezone=t.timezone||"Asia/Shanghai",t.enableCache!==!1?this.cache=new U(`umami-runtime-${a}`,t.cacheTTL||36e5):this.cache=null}async getShareData(){return this.shareData?this.shareData:this.sharePromise?this.sharePromise:(this.sharePromise=(async()=>{let t=await fetch(`${this.baseUrl}/api/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";if(this.cache){let g=this.cache.get(e);if(g)return{...g,_fromCache:!0}}let{websiteId:a,token:s}=await this.getShareData(),i=new URLSearchParams({startAt:"0",endAt:Date.now().toString(),unit:"hour",timezone:this.timezone,compare:"false"});t&&i.set("path",`eq.${t}`);let n=await fetch(`${this.baseUrl}/api/websites/${a}/stats?${i.toString()}`,{headers:{"x-umami-share-token":s}});if(!n.ok)throw new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${n.status}`);let o=await n.json(),l={pageviews:o.pageviews?.value??o.pageviews??0,visitors:o.visitors?.value??o.visitors??0};return this.cache&&this.cache.set(e,l),l}async getSiteStats(){return this.getStats()}async getPageStats(t){return this.getStats(t)}clearCache(){this.cache&&this.cache.clear(),this.shareData=null,this.sharePromise=null}};function x(r){let t=new v(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,DEFAULT_CONFIG,UmamiClient,UmamiError,VERSION,createUmamiClient,initUmamiRuntime,isValidConfig,isValidShareUrl,parseShareUrl,umami});
9
+ `;t("page",s)}}}}function B(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}=B(t.shareUrl);this.apiBase=e,this.shareId=a,this.timezone=t.timezone||"Asia/Hong_Kong",t.enableCache!==!1?this.cache=new v(`umami-runtime-${a}`,t.cacheTTL||36e5):this.cache=null}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";if(this.cache){let g=this.cache.get(e);if(g)return{...g,_fromCache:!0}}let{websiteId:a,token:s}=await this.getShareData(),i=new URLSearchParams({startAt:"0",endAt:Date.now().toString(),unit:"hour",timezone:this.timezone,compare:"false"});t&&i.set("path",`eq.${t}`);let o=await fetch(`${this.apiBase}/websites/${a}/stats?${i.toString()}`,{headers:{"x-umami-share-token":s}});if(!o.ok)throw new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${o.status}`);let n=await o.json(),l={pageviews:n.pageviews?.value??n.pageviews??0,visitors:n.visitors?.value??n.visitors??0};return this.cache&&this.cache.set(e,l),l}async getSiteStats(){return this.getStats()}async getPageStats(t){return this.getStats(t)}clearCache(){this.cache&&this.cache.clear(),this.shareData=null,this.sharePromise=null}};function x(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,DEFAULT_CONFIG,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, DEFAULT_CONFIG, 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';","// 版本信息\r\nexport const VERSION = '1.0.0';\r\n\r\n// 默认配置\r\nexport const DEFAULT_CONFIG = {\r\n timezone: 'Asia/Shanghai',\r\n enableCache: true,\r\n cacheTTL: 3600000\r\n} as const;\r\n\r\n// 配置验证\r\nexport function isValidConfig(config: any): boolean {\r\n return (\r\n typeof config === 'object' &&\r\n config !== null &&\r\n typeof config.baseUrl === 'string' &&\r\n typeof config.shareId === 'string' &&\r\n config.baseUrl.length > 0 &&\r\n config.shareId.length > 0\r\n );\r\n}\r\n\r\n// 自定义错误类\r\nexport class UmamiError extends Error {\r\n constructor(message: string, public code?: string) {\r\n super(message);\r\n this.name = 'UmamiError';\r\n }\r\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';\r\nimport { CacheManager } from '../../utils/umami/cache';\r\n\r\nexport class UmamiAPI {\r\n private cacheManager: CacheManager;\r\n private sharePromise: Promise<ShareData> | null = null;\r\n\r\n constructor(cacheManager: CacheManager) {\r\n this.cacheManager = cacheManager;\r\n }\r\n\r\n async getShareData(baseUrl: string, shareId: string): Promise<ShareData> {\r\n if (!this.sharePromise) {\r\n this.sharePromise = this.fetchShareData(baseUrl, shareId).catch((err) => {\r\n this.sharePromise = null;\r\n throw err;\r\n });\r\n }\r\n return this.sharePromise;\r\n }\r\n\r\n private async fetchShareData(baseUrl: string, shareId: string): Promise<ShareData> {\r\n const res = await fetch(`${baseUrl}/api/share/${shareId}`);\r\n if (!res.ok) {\r\n throw new Error(`获取分享信息失败: ${res.status} ${res.statusText}`);\r\n }\r\n return res.json();\r\n }\r\n\r\n async getStats(baseUrl: string, shareId: string, params: any) {\r\n const cacheKey = `${baseUrl}|${shareId}|${JSON.stringify(params)}`;\r\n \r\n const cached = this.cacheManager.get(cacheKey);\r\n if (cached) {\r\n return { ...cached, _fromCache: true };\r\n }\r\n\r\n const { websiteId, token } = await this.getShareData(baseUrl, shareId);\r\n \r\n const queryParams = new URLSearchParams({\r\n startAt: '0',\r\n endAt: Date.now().toString(),\r\n unit: 'hour',\r\n timezone: params.timezone || 'Asia/Shanghai',\r\n compare: 'false',\r\n ...params\r\n });\r\n\r\n const statsUrl = `${baseUrl}/api/websites/${websiteId}/stats?${queryParams.toString()}`;\r\n \r\n const res = await fetch(statsUrl, {\r\n headers: { 'x-umami-share-token': token }\r\n });\r\n\r\n if (!res.ok) {\r\n if (res.status === 401) {\r\n this.cacheManager.clear();\r\n throw new Error('认证失败,请检查 shareId');\r\n }\r\n throw new Error(`获取统计失败: ${res.status} ${res.statusText}`);\r\n }\r\n\r\n const data = await res.json();\r\n this.cacheManager.set(cacheKey, data);\r\n return data;\r\n }\r\n\r\n clearShareCache(): void {\r\n this.sharePromise = null;\r\n }\r\n}","/**\r\n * 从分享 URL 中提取 baseUrl 和 shareId\r\n * 支持多种 Umami 实例格式:\r\n * - https://umami.example.com/share/abc123\r\n * - https://cloud.umami.is/analytics/us/share/abc123\r\n * - https://umami.example.com/analytics/share/abc123\r\n */\r\nexport function parseShareUrl(shareUrl: string): { baseUrl: string; shareId: string } {\r\n try {\r\n const url = new URL(shareUrl);\r\n \r\n // 提取 baseUrl(协议 + 主机 + 端口)\r\n const baseUrl = `${url.protocol}//${url.host}`;\r\n \r\n // 提取 shareId(/share/ 后面的部分)\r\n const pathParts = url.pathname.split('/');\r\n const shareIndex = pathParts.indexOf('share');\r\n \r\n if (shareIndex === -1 || shareIndex === pathParts.length - 1) {\r\n throw new Error('无效的分享 URL:未找到 share 路径');\r\n }\r\n \r\n const shareId = pathParts[shareIndex + 1];\r\n \r\n if (!shareId || shareId.length < 10) {\r\n throw new Error('无效的分享 ID');\r\n }\r\n \r\n return { baseUrl, shareId };\r\n } catch (error) {\r\n if (error instanceof Error) {\r\n throw new Error(`URL 解析失败: ${error.message}`);\r\n }\r\n throw new Error('URL 解析失败: 无效的 URL 格式');\r\n }\r\n}\r\n\r\n/**\r\n * 验证分享 URL 格式\r\n */\r\nexport function isValidShareUrl(url: string): boolean {\r\n try {\r\n const parsed = new URL(url);\r\n return parsed.pathname.includes('/share/');\r\n } catch {\r\n return false;\r\n }\r\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 // 验证必需参数\n if (!config.shareUrl) {\n throw new Error('shareUrl 是必需参数');\n }\n \n // 自动解析 shareUrl\n const { baseUrl, shareId } = parseShareUrl(config.shareUrl);\n \n this.config = {\n timezone: 'Asia/Shanghai',\n enableCache: true,\n cacheTTL: 3600000,\n baseUrl,\n shareId,\n ...config\n };\n \n this.cacheManager = new CacheManager('umami', this.config.cacheTTL);\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 timezone: this.config.timezone,\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 timezone: this.config.timezone,\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, {\n timezone: this.config.timezone,\n ...options\n });\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 timezone?: string; // 时区,默认 'Asia/Shanghai'\n enableCache?: boolean; // 启用缓存,默认 true\n cacheTTL?: number; // 缓存时间,单位毫秒\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 const config = {\n shareUrl: options.shareUrl,\n timezone: options.timezone || 'Asia/Shanghai',\n enableCache: options.enableCache !== false,\n cacheTTL: options.cacheTTL || 3600000\n };\n\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 // 如果读取失败,使用内联代码\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(config)});\n}\n`;\n\n injectScript('page', initCode);\n }\n }\n };\n}","/**\n * 浏览器运行时客户端\n * 用于 Astro 集成注入到页面中\n * \n * 注意:此文件会被内联注入到页面,不能有外部依赖\n */\n\ninterface UmamiRuntimeConfig {\n shareUrl: string;\n timezone?: string;\n enableCache?: boolean;\n cacheTTL?: number;\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): { baseUrl: string; shareId: string } {\n const url = new URL(shareUrl);\n const baseUrl = `${url.protocol}//${url.host}`;\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 return { baseUrl, 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 baseUrl: string;\n private shareId: string;\n private timezone: string;\n private cache: SimpleCache | null;\n private shareData: ShareData | null = null;\n private sharePromise: Promise<ShareData> | null = null;\n\n constructor(config: UmamiRuntimeConfig) {\n const { baseUrl, shareId } = parseShareUrl(config.shareUrl);\n this.baseUrl = baseUrl;\n this.shareId = shareId;\n this.timezone = config.timezone || 'Asia/Shanghai';\n \n if (config.enableCache !== false) {\n this.cache = new SimpleCache(\n `umami-runtime-${shareId}`,\n config.cacheTTL || 3600000\n );\n } else {\n this.cache = null;\n }\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.baseUrl}/api/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 // 检查缓存\n if (this.cache) {\n const cached = this.cache.get(cacheKey);\n if (cached) {\n return { ...cached, _fromCache: true };\n }\n }\n\n const { websiteId, token } = await this.getShareData();\n \n const params = new URLSearchParams({\n startAt: '0',\n endAt: Date.now().toString(),\n unit: 'hour',\n timezone: this.timezone,\n compare: 'false'\n });\n\n if (path) {\n params.set('path', `eq.${path}`);\n }\n\n const res = await fetch(\n `${this.baseUrl}/api/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 // 缓存结果\n if (this.cache) {\n this.cache.set(cacheKey, result);\n }\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 if (this.cache) {\n this.cache.clear();\n }\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 // 创建命名空间\n (window as any).oddmisc = (window as any).oddmisc || {};\n \n // 挂载客户端实例\n (window as any).oddmisc.umami = client;\n \n // 快捷方法\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\n// 导出类型\nexport type { UmamiRuntimeConfig, StatsResult };\n"],"mappings":"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","names":["index_exports","__export","CacheManager","DEFAULT_CONFIG","UmamiClient","UmamiError","VERSION","createUmamiClient","initUmamiRuntime","isValidConfig","isValidShareUrl","parseShareUrl","umami","__toCommonJS","VERSION","DEFAULT_CONFIG","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","baseUrl","pathParts","shareIndex","shareId","error","isValidShareUrl","UmamiClient","config","baseUrl","shareId","parseShareUrl","CacheManager","UmamiAPI","path","options","data","url","newConfig","createUmamiClient","import_fs","import_url","import_path","import_meta","umami","options","injectScript","config","runtimeCode","__dirname","runtimePath","initCode","parseShareUrl","shareUrl","url","baseUrl","pathParts","shareIndex","shareId","SimpleCache","storageKey","ttl","stored","parsed","now","key","data","obj","value","cached","UmamiRuntimeClient","config","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, DEFAULT_CONFIG, 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 const DEFAULT_CONFIG = {\n timezone: 'Asia/Hong_Kong',\n enableCache: true,\n cacheTTL: 3600000\n} as const;\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 unit: 'hour',\n timezone: params.timezone || 'Asia/Hong_Kong',\n compare: 'false',\n ...params\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 // 验证必需参数\n if (!config.shareUrl) {\n throw new Error('shareUrl 是必需参数');\n }\n \n // 自动解析 shareUrl\n const { apiBase, shareId } = parseShareUrl(config.shareUrl);\n \n this.config = {\n timezone: 'Asia/Hong_Kong',\n enableCache: true,\n cacheTTL: 3600000,\n baseUrl: apiBase,\n shareId,\n ...config\n };\n \n this.cacheManager = new CacheManager('umami', this.config.cacheTTL);\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 timezone: this.config.timezone,\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 timezone: this.config.timezone,\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, {\n timezone: this.config.timezone,\n ...options\n });\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 timezone?: string; // 时区,默认 'Asia/Hong_Kong'\n enableCache?: boolean; // 启用缓存,默认 true\n cacheTTL?: number; // 缓存时间,单位毫秒\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 const config = {\n shareUrl: options.shareUrl,\n timezone: options.timezone || 'Asia/Hong_Kong',\n enableCache: options.enableCache !== false,\n cacheTTL: options.cacheTTL || 3600000\n };\n\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 // 如果读取失败,使用内联代码\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(config)});\n}\n`;\n\n injectScript('page', initCode);\n }\n }\n };\n}","/**\n * 浏览器运行时客户端\n * 用于 Astro 集成注入到页面中\n * \n * 注意:此文件会被内联注入到页面,不能有外部依赖\n */\n\ninterface UmamiRuntimeConfig {\n shareUrl: string;\n timezone?: string;\n enableCache?: boolean;\n cacheTTL?: number;\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 timezone: string;\n private cache: SimpleCache | null;\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.timezone = config.timezone || 'Asia/Hong_Kong';\n \n if (config.enableCache !== false) {\n this.cache = new SimpleCache(\n `umami-runtime-${shareId}`,\n config.cacheTTL || 3600000\n );\n } else {\n this.cache = null;\n }\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 // 检查缓存\n if (this.cache) {\n const cached = this.cache.get(cacheKey);\n if (cached) {\n return { ...cached, _fromCache: true };\n }\n }\n\n const { websiteId, token } = await this.getShareData();\n \n const params = new URLSearchParams({\n startAt: '0',\n endAt: Date.now().toString(),\n unit: 'hour',\n timezone: this.timezone,\n compare: 'false'\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 // 缓存结果\n if (this.cache) {\n this.cache.set(cacheKey, result);\n }\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 if (this.cache) {\n this.cache.clear();\n }\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 // 创建命名空间\n (window as any).oddmisc = (window as any).oddmisc || {};\n \n // 挂载客户端实例\n (window as any).oddmisc.umami = client;\n \n // 快捷方法\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\n// 导出类型\nexport type { UmamiRuntimeConfig, StatsResult };\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,kBAAAE,EAAA,mBAAAC,EAAA,gBAAAC,EAAA,eAAAC,EAAA,YAAAC,EAAA,sBAAAC,EAAA,qBAAAC,EAAA,kBAAAC,EAAA,oBAAAC,EAAA,kBAAAC,EAAA,UAAAC,IAAA,eAAAC,EAAAb,GCCO,IAAMc,EAAU,QAGVC,EAAiB,CAC5B,SAAU,iBACV,YAAa,GACb,SAAU,IACZ,EAGO,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,EC3BA,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,EAC3B,KAAM,OACN,SAAUL,EAAO,UAAY,iBAC7B,QAAS,QACT,GAAGA,CACL,CAAC,EAEKM,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,EChEO,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,CAE/B,GAAI,CAACA,EAAO,SACV,MAAM,IAAI,MAAM,yCAAgB,EAIlC,GAAM,CAAE,QAAAC,EAAS,QAAAC,CAAQ,EAAIC,EAAcH,EAAO,QAAQ,EAE1D,KAAK,OAAS,CACZ,SAAU,iBACV,YAAa,GACb,SAAU,KACV,QAASC,EACT,QAAAC,EACA,GAAGF,CACL,EAEA,KAAK,aAAe,IAAII,EAAa,QAAS,KAAK,OAAO,QAAQ,EAClE,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,SAAU,KAAK,OAAO,SACtB,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,SAAU,KAAK,OAAO,SACtB,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,QAAS,CAC7E,SAAU,KAAK,OAAO,SACtB,GAAGD,CACL,CAAC,EAED,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,CC3GA,IAAAY,EAA6B,cAC7BC,EAA8B,eAC9BC,EAA8B,gBAF9BC,EAAA,GAaO,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,CAC/C,IAAMC,EAAS,CACb,SAAUF,EAAQ,SAClB,SAAUA,EAAQ,UAAY,iBAC9B,YAAaA,EAAQ,cAAgB,GACrC,SAAUA,EAAQ,UAAY,IAChC,EAGIG,EAAc,GAClB,GAAI,CACF,IAAMC,KAAY,cAAQ,iBAAcN,EAAY,GAAG,CAAC,EAClDO,KAAc,QAAKD,EAAW,4BAA4B,EAChED,KAAc,gBAAaE,EAAa,OAAO,CACjD,MAAQ,CAEN,QAAQ,KAAK,4GAA4B,CAC3C,CAGA,IAAMC,EAAW;AAAA;AAAA,EAEvBH,CAAW;AAAA;AAAA;AAAA;AAAA,sCAIyB,KAAK,UAAUD,CAAM,CAAC;AAAA;AAAA,EAIpDD,EAAa,OAAQK,CAAQ,CAC/B,CACF,CACF,CACF,CC5BA,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,CAQvB,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,SAAWc,EAAO,UAAY,iBAE/BA,EAAO,cAAgB,GACzB,KAAK,MAAQ,IAAIZ,EACf,iBAAiBF,CAAO,GACxBc,EAAO,UAAY,IACrB,EAEA,KAAK,MAAQ,IAEjB,CAEA,MAAc,cAAmC,CAC/C,OAAI,KAAK,UACA,KAAK,UAGV,KAAK,aACA,KAAK,cAGd,KAAK,cAAgB,SAAgC,CACnD,IAAME,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,aAG1C,GAAI,KAAK,MAAO,CACd,IAAML,EAAS,KAAK,MAAM,IAAIM,CAAQ,EACtC,GAAIN,EACF,MAAO,CAAE,GAAGA,EAAQ,WAAY,EAAK,CAEzC,CAEA,GAAM,CAAE,UAAAO,EAAW,MAAAC,CAAM,EAAI,MAAM,KAAK,aAAa,EAE/CC,EAAS,IAAI,gBAAgB,CACjC,QAAS,IACT,MAAO,KAAK,IAAI,EAAE,SAAS,EAC3B,KAAM,OACN,SAAU,KAAK,SACf,QAAS,OACX,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,CACrD,EAGA,OAAI,KAAK,OACP,KAAK,MAAM,IAAIS,EAAUI,CAAM,EAG1BA,CACT,CAEA,MAAM,cAAqC,CACzC,OAAO,KAAK,SAAS,CACvB,CAEA,MAAM,aAAaL,EAAoC,CACrD,OAAO,KAAK,SAASA,CAAI,CAC3B,CAEA,YAAmB,CACb,KAAK,OACP,KAAK,MAAM,MAAM,EAEnB,KAAK,UAAY,KACjB,KAAK,aAAe,IACtB,CACF,EAGO,SAASM,EAAiBT,EAAkC,CACjE,IAAMU,EAAS,IAAIX,EAAmBC,CAAM,EAG3C,OAAe,QAAW,OAAe,SAAW,CAAC,EAGrD,OAAe,QAAQ,MAAQU,EAG/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":["index_exports","__export","CacheManager","DEFAULT_CONFIG","UmamiClient","UmamiError","VERSION","createUmamiClient","initUmamiRuntime","isValidConfig","isValidShareUrl","parseShareUrl","umami","__toCommonJS","VERSION","DEFAULT_CONFIG","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","config","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.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  declare const VERSION = "1.0.0";
2
2
  declare const DEFAULT_CONFIG: {
3
- readonly timezone: "Asia/Shanghai";
3
+ readonly timezone: "Asia/Hong_Kong";
4
4
  readonly enableCache: true;
5
5
  readonly cacheTTL: 3600000;
6
6
  };
@@ -13,7 +13,7 @@ declare class UmamiError extends Error {
13
13
  interface UmamiConfig {
14
14
  /** 分享 URL,如: https://umami.example.com/share/abc123 */
15
15
  shareUrl: string;
16
- /** 时区,默认 'Asia/Shanghai' */
16
+ /** 时区,默认 'Asia/Hong_Kong' */
17
17
  timezone?: string;
18
18
  /** 是否启用缓存,默认 true */
19
19
  enableCache?: boolean;
@@ -63,14 +63,13 @@ declare class CacheManager<T = any> {
63
63
  }
64
64
 
65
65
  /**
66
- * 从分享 URL 中提取 baseUrl 和 shareId
66
+ * 从分享 URL 中提取 apiBase 和 shareId
67
67
  * 支持多种 Umami 实例格式:
68
- * - https://umami.example.com/share/abc123
69
- * - https://cloud.umami.is/analytics/us/share/abc123
70
- * - https://umami.example.com/analytics/share/abc123
68
+ * - https://umami.example.com/share/abc123 → apiBase: https://umami.example.com/api
69
+ * - https://cloud.umami.is/analytics/us/share/abc123 → apiBase: https://cloud.umami.is/analytics/us/api
71
70
  */
72
71
  declare function parseShareUrl(shareUrl: string): {
73
- baseUrl: string;
72
+ apiBase: string;
74
73
  shareId: string;
75
74
  };
76
75
  /**
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  declare const VERSION = "1.0.0";
2
2
  declare const DEFAULT_CONFIG: {
3
- readonly timezone: "Asia/Shanghai";
3
+ readonly timezone: "Asia/Hong_Kong";
4
4
  readonly enableCache: true;
5
5
  readonly cacheTTL: 3600000;
6
6
  };
@@ -13,7 +13,7 @@ declare class UmamiError extends Error {
13
13
  interface UmamiConfig {
14
14
  /** 分享 URL,如: https://umami.example.com/share/abc123 */
15
15
  shareUrl: string;
16
- /** 时区,默认 'Asia/Shanghai' */
16
+ /** 时区,默认 'Asia/Hong_Kong' */
17
17
  timezone?: string;
18
18
  /** 是否启用缓存,默认 true */
19
19
  enableCache?: boolean;
@@ -63,14 +63,13 @@ declare class CacheManager<T = any> {
63
63
  }
64
64
 
65
65
  /**
66
- * 从分享 URL 中提取 baseUrl 和 shareId
66
+ * 从分享 URL 中提取 apiBase 和 shareId
67
67
  * 支持多种 Umami 实例格式:
68
- * - https://umami.example.com/share/abc123
69
- * - https://cloud.umami.is/analytics/us/share/abc123
70
- * - https://umami.example.com/analytics/share/abc123
68
+ * - https://umami.example.com/share/abc123 → apiBase: https://umami.example.com/api
69
+ * - https://cloud.umami.is/analytics/us/share/abc123 → apiBase: https://cloud.umami.is/analytics/us/api
71
70
  */
72
71
  declare function parseShareUrl(shareUrl: string): {
73
- baseUrl: string;
72
+ apiBase: string;
74
73
  shareId: string;
75
74
  };
76
75
  /**
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- var v="1.0.0",C={timezone:"Asia/Shanghai",enableCache:!0,cacheTTL:36e5};function b(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 g=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(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 u=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}/api/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:o}=await this.getShareData(t,e),m=new URLSearchParams({startAt:"0",endAt:Date.now().toString(),unit:"hour",timezone:a.timezone||"Asia/Shanghai",compare:"false",...a}),l=`${t}/api/websites/${n}/stats?${m.toString()}`,h=await fetch(l,{headers:{"x-umami-share-token":o}});if(!h.ok)throw h.status===401?(this.cacheManager.clear(),new Error("\u8BA4\u8BC1\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5 shareId")):new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${h.status} ${h.statusText}`);let y=await h.json();return this.cacheManager.set(s,y),y}clearShareCache(){this.sharePromise=null}};function d(r){try{let t=new URL(r),e=`${t.protocol}//${t.host}`,a=t.pathname.split("/"),s=a.indexOf("share");if(s===-1||s===a.length-1)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB URL\uFF1A\u672A\u627E\u5230 share \u8DEF\u5F84");let i=a[s+1];if(!i||i.length<10)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB ID");return{baseUrl:e,shareId:i}}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 f=class{constructor(t){if(!t.shareUrl)throw new Error("shareUrl \u662F\u5FC5\u9700\u53C2\u6570");let{baseUrl:e,shareId:a}=d(t.shareUrl);this.config={timezone:"Asia/Shanghai",enableCache:!0,cacheTTL:36e5,baseUrl:e,shareId:a,...t},this.cacheManager=new c("umami",this.config.cacheTTL),this.api=new u(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}`,timezone:this.config.timezone,...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,timezone:this.config.timezone,...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,{timezone:this.config.timezone,...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 f(r)}import{readFileSync as I}from"fs";import{fileURLToPath as R}from"url";import{dirname as T,join as D}from"path";function U(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={shareUrl:r.shareUrl,timezone:r.timezone||"Asia/Shanghai",enableCache:r.enableCache!==!1,cacheTTL:r.cacheTTL||36e5},a="";try{let i=T(R(import.meta.url)),n=D(i,"./runtime/client.global.js");a=I(n,"utf-8")}catch{console.warn("[oddmisc] \u65E0\u6CD5\u8BFB\u53D6\u8FD0\u884C\u65F6\u6587\u4EF6\uFF0C\u4F7F\u7528\u5907\u7528\u65B9\u6848")}let s=`
1
+ var C="1.0.0",U={timezone:"Asia/Hong_Kong",enableCache:!0,cacheTTL:36e5};function P(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",h=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 u=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),m=new URLSearchParams({startAt:"0",endAt:Date.now().toString(),unit:"hour",timezone:a.timezone||"Asia/Hong_Kong",compare:"false",...a}),l=`${t}/websites/${o}/stats?${m.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 E(r){try{return new URL(r).pathname.includes("/share/")}catch{return!1}}var p=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={timezone:"Asia/Hong_Kong",enableCache:!0,cacheTTL:36e5,baseUrl:e,shareId:a,...t},this.cacheManager=new h("umami",this.config.cacheTTL),this.api=new u(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}`,timezone:this.config.timezone,...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,timezone:this.config.timezone,...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,{timezone:this.config.timezone,...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 I(r){return new p(r)}import{readFileSync as b}from"fs";import{fileURLToPath as R}from"url";import{dirname as T,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={shareUrl:r.shareUrl,timezone:r.timezone||"Asia/Hong_Kong",enableCache:r.enableCache!==!1,cacheTTL:r.cacheTTL||36e5},a="";try{let i=T(R(import.meta.url)),o=D(i,"./runtime/client.global.js");a=b(o,"utf-8")}catch{console.warn("[oddmisc] \u65E0\u6CD5\u8BFB\u53D6\u8FD0\u884C\u65F6\u6587\u4EF6\uFF0C\u4F7F\u7528\u5907\u7528\u65B9\u6848")}let s=`
2
2
  // oddmisc Umami Runtime
3
3
  ${a}
4
4
 
@@ -6,5 +6,5 @@ ${a}
6
6
  if (typeof window !== 'undefined') {
7
7
  __oddmiscRuntime.initUmamiRuntime(${JSON.stringify(e)});
8
8
  }
9
- `;t("page",s)}}}}function x(r){let t=new URL(r),e=`${t.protocol}//${t.host}`,a=t.pathname.split("/"),s=a.indexOf("share");if(s===-1||s===a.length-1)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB URL\uFF1A\u672A\u627E\u5230 share \u8DEF\u5F84");let i=a[s+1];if(!i||i.length<10)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB ID");return{baseUrl:e,shareId:i}}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{baseUrl:e,shareId:a}=x(t.shareUrl);this.baseUrl=e,this.shareId=a,this.timezone=t.timezone||"Asia/Shanghai",t.enableCache!==!1?this.cache=new w(`umami-runtime-${a}`,t.cacheTTL||36e5):this.cache=null}async getShareData(){return this.shareData?this.shareData:this.sharePromise?this.sharePromise:(this.sharePromise=(async()=>{let t=await fetch(`${this.baseUrl}/api/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";if(this.cache){let l=this.cache.get(e);if(l)return{...l,_fromCache:!0}}let{websiteId:a,token:s}=await this.getShareData(),i=new URLSearchParams({startAt:"0",endAt:Date.now().toString(),unit:"hour",timezone:this.timezone,compare:"false"});t&&i.set("path",`eq.${t}`);let n=await fetch(`${this.baseUrl}/api/websites/${a}/stats?${i.toString()}`,{headers:{"x-umami-share-token":s}});if(!n.ok)throw new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${n.status}`);let o=await n.json(),m={pageviews:o.pageviews?.value??o.pageviews??0,visitors:o.visitors?.value??o.visitors??0};return this.cache&&this.cache.set(e,m),m}async getSiteStats(){return this.getStats()}async getPageStats(t){return this.getStats(t)}clearCache(){this.cache&&this.cache.clear(),this.shareData=null,this.sharePromise=null}};function $(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{c as CacheManager,C as DEFAULT_CONFIG,f as UmamiClient,p as UmamiError,v as VERSION,E as createUmamiClient,$ as initUmamiRuntime,b as isValidConfig,P as isValidShareUrl,d as parseShareUrl,U as umami};
9
+ `;t("page",s)}}}}function x(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}=x(t.shareUrl);this.apiBase=e,this.shareId=a,this.timezone=t.timezone||"Asia/Hong_Kong",t.enableCache!==!1?this.cache=new w(`umami-runtime-${a}`,t.cacheTTL||36e5):this.cache=null}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";if(this.cache){let l=this.cache.get(e);if(l)return{...l,_fromCache:!0}}let{websiteId:a,token:s}=await this.getShareData(),i=new URLSearchParams({startAt:"0",endAt:Date.now().toString(),unit:"hour",timezone:this.timezone,compare:"false"});t&&i.set("path",`eq.${t}`);let o=await fetch(`${this.apiBase}/websites/${a}/stats?${i.toString()}`,{headers:{"x-umami-share-token":s}});if(!o.ok)throw new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${o.status}`);let n=await o.json(),m={pageviews:n.pageviews?.value??n.pageviews??0,visitors:n.visitors?.value??n.visitors??0};return this.cache&&this.cache.set(e,m),m}async getSiteStats(){return this.getStats()}async getPageStats(t){return this.getStats(t)}clearCache(){this.cache&&this.cache.clear(),this.shareData=null,this.sharePromise=null}};function $(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{h as CacheManager,U as DEFAULT_CONFIG,p as UmamiClient,f as UmamiError,C as VERSION,I as createUmamiClient,$ as initUmamiRuntime,P as isValidConfig,E 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":["// 版本信息\r\nexport const VERSION = '1.0.0';\r\n\r\n// 默认配置\r\nexport const DEFAULT_CONFIG = {\r\n timezone: 'Asia/Shanghai',\r\n enableCache: true,\r\n cacheTTL: 3600000\r\n} as const;\r\n\r\n// 配置验证\r\nexport function isValidConfig(config: any): boolean {\r\n return (\r\n typeof config === 'object' &&\r\n config !== null &&\r\n typeof config.baseUrl === 'string' &&\r\n typeof config.shareId === 'string' &&\r\n config.baseUrl.length > 0 &&\r\n config.shareId.length > 0\r\n );\r\n}\r\n\r\n// 自定义错误类\r\nexport class UmamiError extends Error {\r\n constructor(message: string, public code?: string) {\r\n super(message);\r\n this.name = 'UmamiError';\r\n }\r\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';\r\nimport { CacheManager } from '../../utils/umami/cache';\r\n\r\nexport class UmamiAPI {\r\n private cacheManager: CacheManager;\r\n private sharePromise: Promise<ShareData> | null = null;\r\n\r\n constructor(cacheManager: CacheManager) {\r\n this.cacheManager = cacheManager;\r\n }\r\n\r\n async getShareData(baseUrl: string, shareId: string): Promise<ShareData> {\r\n if (!this.sharePromise) {\r\n this.sharePromise = this.fetchShareData(baseUrl, shareId).catch((err) => {\r\n this.sharePromise = null;\r\n throw err;\r\n });\r\n }\r\n return this.sharePromise;\r\n }\r\n\r\n private async fetchShareData(baseUrl: string, shareId: string): Promise<ShareData> {\r\n const res = await fetch(`${baseUrl}/api/share/${shareId}`);\r\n if (!res.ok) {\r\n throw new Error(`获取分享信息失败: ${res.status} ${res.statusText}`);\r\n }\r\n return res.json();\r\n }\r\n\r\n async getStats(baseUrl: string, shareId: string, params: any) {\r\n const cacheKey = `${baseUrl}|${shareId}|${JSON.stringify(params)}`;\r\n \r\n const cached = this.cacheManager.get(cacheKey);\r\n if (cached) {\r\n return { ...cached, _fromCache: true };\r\n }\r\n\r\n const { websiteId, token } = await this.getShareData(baseUrl, shareId);\r\n \r\n const queryParams = new URLSearchParams({\r\n startAt: '0',\r\n endAt: Date.now().toString(),\r\n unit: 'hour',\r\n timezone: params.timezone || 'Asia/Shanghai',\r\n compare: 'false',\r\n ...params\r\n });\r\n\r\n const statsUrl = `${baseUrl}/api/websites/${websiteId}/stats?${queryParams.toString()}`;\r\n \r\n const res = await fetch(statsUrl, {\r\n headers: { 'x-umami-share-token': token }\r\n });\r\n\r\n if (!res.ok) {\r\n if (res.status === 401) {\r\n this.cacheManager.clear();\r\n throw new Error('认证失败,请检查 shareId');\r\n }\r\n throw new Error(`获取统计失败: ${res.status} ${res.statusText}`);\r\n }\r\n\r\n const data = await res.json();\r\n this.cacheManager.set(cacheKey, data);\r\n return data;\r\n }\r\n\r\n clearShareCache(): void {\r\n this.sharePromise = null;\r\n }\r\n}","/**\r\n * 从分享 URL 中提取 baseUrl 和 shareId\r\n * 支持多种 Umami 实例格式:\r\n * - https://umami.example.com/share/abc123\r\n * - https://cloud.umami.is/analytics/us/share/abc123\r\n * - https://umami.example.com/analytics/share/abc123\r\n */\r\nexport function parseShareUrl(shareUrl: string): { baseUrl: string; shareId: string } {\r\n try {\r\n const url = new URL(shareUrl);\r\n \r\n // 提取 baseUrl(协议 + 主机 + 端口)\r\n const baseUrl = `${url.protocol}//${url.host}`;\r\n \r\n // 提取 shareId(/share/ 后面的部分)\r\n const pathParts = url.pathname.split('/');\r\n const shareIndex = pathParts.indexOf('share');\r\n \r\n if (shareIndex === -1 || shareIndex === pathParts.length - 1) {\r\n throw new Error('无效的分享 URL:未找到 share 路径');\r\n }\r\n \r\n const shareId = pathParts[shareIndex + 1];\r\n \r\n if (!shareId || shareId.length < 10) {\r\n throw new Error('无效的分享 ID');\r\n }\r\n \r\n return { baseUrl, shareId };\r\n } catch (error) {\r\n if (error instanceof Error) {\r\n throw new Error(`URL 解析失败: ${error.message}`);\r\n }\r\n throw new Error('URL 解析失败: 无效的 URL 格式');\r\n }\r\n}\r\n\r\n/**\r\n * 验证分享 URL 格式\r\n */\r\nexport function isValidShareUrl(url: string): boolean {\r\n try {\r\n const parsed = new URL(url);\r\n return parsed.pathname.includes('/share/');\r\n } catch {\r\n return false;\r\n }\r\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 // 验证必需参数\n if (!config.shareUrl) {\n throw new Error('shareUrl 是必需参数');\n }\n \n // 自动解析 shareUrl\n const { baseUrl, shareId } = parseShareUrl(config.shareUrl);\n \n this.config = {\n timezone: 'Asia/Shanghai',\n enableCache: true,\n cacheTTL: 3600000,\n baseUrl,\n shareId,\n ...config\n };\n \n this.cacheManager = new CacheManager('umami', this.config.cacheTTL);\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 timezone: this.config.timezone,\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 timezone: this.config.timezone,\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, {\n timezone: this.config.timezone,\n ...options\n });\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 timezone?: string; // 时区,默认 'Asia/Shanghai'\n enableCache?: boolean; // 启用缓存,默认 true\n cacheTTL?: number; // 缓存时间,单位毫秒\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 const config = {\n shareUrl: options.shareUrl,\n timezone: options.timezone || 'Asia/Shanghai',\n enableCache: options.enableCache !== false,\n cacheTTL: options.cacheTTL || 3600000\n };\n\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 // 如果读取失败,使用内联代码\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(config)});\n}\n`;\n\n injectScript('page', initCode);\n }\n }\n };\n}","/**\n * 浏览器运行时客户端\n * 用于 Astro 集成注入到页面中\n * \n * 注意:此文件会被内联注入到页面,不能有外部依赖\n */\n\ninterface UmamiRuntimeConfig {\n shareUrl: string;\n timezone?: string;\n enableCache?: boolean;\n cacheTTL?: number;\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): { baseUrl: string; shareId: string } {\n const url = new URL(shareUrl);\n const baseUrl = `${url.protocol}//${url.host}`;\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 return { baseUrl, 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 baseUrl: string;\n private shareId: string;\n private timezone: string;\n private cache: SimpleCache | null;\n private shareData: ShareData | null = null;\n private sharePromise: Promise<ShareData> | null = null;\n\n constructor(config: UmamiRuntimeConfig) {\n const { baseUrl, shareId } = parseShareUrl(config.shareUrl);\n this.baseUrl = baseUrl;\n this.shareId = shareId;\n this.timezone = config.timezone || 'Asia/Shanghai';\n \n if (config.enableCache !== false) {\n this.cache = new SimpleCache(\n `umami-runtime-${shareId}`,\n config.cacheTTL || 3600000\n );\n } else {\n this.cache = null;\n }\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.baseUrl}/api/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 // 检查缓存\n if (this.cache) {\n const cached = this.cache.get(cacheKey);\n if (cached) {\n return { ...cached, _fromCache: true };\n }\n }\n\n const { websiteId, token } = await this.getShareData();\n \n const params = new URLSearchParams({\n startAt: '0',\n endAt: Date.now().toString(),\n unit: 'hour',\n timezone: this.timezone,\n compare: 'false'\n });\n\n if (path) {\n params.set('path', `eq.${path}`);\n }\n\n const res = await fetch(\n `${this.baseUrl}/api/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 // 缓存结果\n if (this.cache) {\n this.cache.set(cacheKey, result);\n }\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 if (this.cache) {\n this.cache.clear();\n }\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 // 创建命名空间\n (window as any).oddmisc = (window as any).oddmisc || {};\n \n // 挂载客户端实例\n (window as any).oddmisc.umami = client;\n \n // 快捷方法\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\n// 导出类型\nexport type { UmamiRuntimeConfig, StatsResult };\n"],"mappings":"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","names":["VERSION","DEFAULT_CONFIG","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","baseUrl","pathParts","shareIndex","shareId","error","isValidShareUrl","UmamiClient","config","baseUrl","shareId","parseShareUrl","CacheManager","UmamiAPI","path","options","data","url","newConfig","createUmamiClient","readFileSync","fileURLToPath","dirname","join","umami","options","injectScript","config","runtimeCode","__dirname","runtimePath","initCode","parseShareUrl","shareUrl","url","baseUrl","pathParts","shareIndex","shareId","SimpleCache","storageKey","ttl","stored","parsed","now","key","data","obj","value","cached","UmamiRuntimeClient","config","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 const DEFAULT_CONFIG = {\n timezone: 'Asia/Hong_Kong',\n enableCache: true,\n cacheTTL: 3600000\n} as const;\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 unit: 'hour',\n timezone: params.timezone || 'Asia/Hong_Kong',\n compare: 'false',\n ...params\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 // 验证必需参数\n if (!config.shareUrl) {\n throw new Error('shareUrl 是必需参数');\n }\n \n // 自动解析 shareUrl\n const { apiBase, shareId } = parseShareUrl(config.shareUrl);\n \n this.config = {\n timezone: 'Asia/Hong_Kong',\n enableCache: true,\n cacheTTL: 3600000,\n baseUrl: apiBase,\n shareId,\n ...config\n };\n \n this.cacheManager = new CacheManager('umami', this.config.cacheTTL);\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 timezone: this.config.timezone,\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 timezone: this.config.timezone,\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, {\n timezone: this.config.timezone,\n ...options\n });\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 timezone?: string; // 时区,默认 'Asia/Hong_Kong'\n enableCache?: boolean; // 启用缓存,默认 true\n cacheTTL?: number; // 缓存时间,单位毫秒\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 const config = {\n shareUrl: options.shareUrl,\n timezone: options.timezone || 'Asia/Hong_Kong',\n enableCache: options.enableCache !== false,\n cacheTTL: options.cacheTTL || 3600000\n };\n\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 // 如果读取失败,使用内联代码\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(config)});\n}\n`;\n\n injectScript('page', initCode);\n }\n }\n };\n}","/**\n * 浏览器运行时客户端\n * 用于 Astro 集成注入到页面中\n * \n * 注意:此文件会被内联注入到页面,不能有外部依赖\n */\n\ninterface UmamiRuntimeConfig {\n shareUrl: string;\n timezone?: string;\n enableCache?: boolean;\n cacheTTL?: number;\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 timezone: string;\n private cache: SimpleCache | null;\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.timezone = config.timezone || 'Asia/Hong_Kong';\n \n if (config.enableCache !== false) {\n this.cache = new SimpleCache(\n `umami-runtime-${shareId}`,\n config.cacheTTL || 3600000\n );\n } else {\n this.cache = null;\n }\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 // 检查缓存\n if (this.cache) {\n const cached = this.cache.get(cacheKey);\n if (cached) {\n return { ...cached, _fromCache: true };\n }\n }\n\n const { websiteId, token } = await this.getShareData();\n \n const params = new URLSearchParams({\n startAt: '0',\n endAt: Date.now().toString(),\n unit: 'hour',\n timezone: this.timezone,\n compare: 'false'\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 // 缓存结果\n if (this.cache) {\n this.cache.set(cacheKey, result);\n }\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 if (this.cache) {\n this.cache.clear();\n }\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 // 创建命名空间\n (window as any).oddmisc = (window as any).oddmisc || {};\n \n // 挂载客户端实例\n (window as any).oddmisc.umami = client;\n \n // 快捷方法\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\n// 导出类型\nexport type { UmamiRuntimeConfig, StatsResult };\n"],"mappings":"AACO,IAAMA,EAAU,QAGVC,EAAiB,CAC5B,SAAU,iBACV,YAAa,GACb,SAAU,IACZ,EAGO,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,EC3BA,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,EAC3B,KAAM,OACN,SAAUL,EAAO,UAAY,iBAC7B,QAAS,QACT,GAAGA,CACL,CAAC,EAEKM,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,EChEO,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,CAE/B,GAAI,CAACA,EAAO,SACV,MAAM,IAAI,MAAM,yCAAgB,EAIlC,GAAM,CAAE,QAAAC,EAAS,QAAAC,CAAQ,EAAIC,EAAcH,EAAO,QAAQ,EAE1D,KAAK,OAAS,CACZ,SAAU,iBACV,YAAa,GACb,SAAU,KACV,QAASC,EACT,QAAAC,EACA,GAAGF,CACL,EAEA,KAAK,aAAe,IAAII,EAAa,QAAS,KAAK,OAAO,QAAQ,EAClE,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,SAAU,KAAK,OAAO,SACtB,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,SAAU,KAAK,OAAO,SACtB,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,QAAS,CAC7E,SAAU,KAAK,OAAO,SACtB,GAAGD,CACL,CAAC,EAED,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,CC3GA,OAAS,gBAAAY,MAAoB,KAC7B,OAAS,iBAAAC,MAAqB,MAC9B,OAAS,WAAAC,EAAS,QAAAC,MAAY,OAWvB,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,CAC/C,IAAMC,EAAS,CACb,SAAUF,EAAQ,SAClB,SAAUA,EAAQ,UAAY,iBAC9B,YAAaA,EAAQ,cAAgB,GACrC,SAAUA,EAAQ,UAAY,IAChC,EAGIG,EAAc,GAClB,GAAI,CACF,IAAMC,EAAYP,EAAQD,EAAc,YAAY,GAAG,CAAC,EAClDS,EAAcP,EAAKM,EAAW,4BAA4B,EAChED,EAAcR,EAAaU,EAAa,OAAO,CACjD,MAAQ,CAEN,QAAQ,KAAK,4GAA4B,CAC3C,CAGA,IAAMC,EAAW;AAAA;AAAA,EAEvBH,CAAW;AAAA;AAAA;AAAA;AAAA,sCAIyB,KAAK,UAAUD,CAAM,CAAC;AAAA;AAAA,EAIpDD,EAAa,OAAQK,CAAQ,CAC/B,CACF,CACF,CACF,CC5BA,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,CAQvB,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,SAAWc,EAAO,UAAY,iBAE/BA,EAAO,cAAgB,GACzB,KAAK,MAAQ,IAAIZ,EACf,iBAAiBF,CAAO,GACxBc,EAAO,UAAY,IACrB,EAEA,KAAK,MAAQ,IAEjB,CAEA,MAAc,cAAmC,CAC/C,OAAI,KAAK,UACA,KAAK,UAGV,KAAK,aACA,KAAK,cAGd,KAAK,cAAgB,SAAgC,CACnD,IAAME,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,aAG1C,GAAI,KAAK,MAAO,CACd,IAAML,EAAS,KAAK,MAAM,IAAIM,CAAQ,EACtC,GAAIN,EACF,MAAO,CAAE,GAAGA,EAAQ,WAAY,EAAK,CAEzC,CAEA,GAAM,CAAE,UAAAO,EAAW,MAAAC,CAAM,EAAI,MAAM,KAAK,aAAa,EAE/CC,EAAS,IAAI,gBAAgB,CACjC,QAAS,IACT,MAAO,KAAK,IAAI,EAAE,SAAS,EAC3B,KAAM,OACN,SAAU,KAAK,SACf,QAAS,OACX,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,CACrD,EAGA,OAAI,KAAK,OACP,KAAK,MAAM,IAAIS,EAAUI,CAAM,EAG1BA,CACT,CAEA,MAAM,cAAqC,CACzC,OAAO,KAAK,SAAS,CACvB,CAEA,MAAM,aAAaL,EAAoC,CACrD,OAAO,KAAK,SAASA,CAAI,CAC3B,CAEA,YAAmB,CACb,KAAK,OACP,KAAK,MAAM,MAAM,EAEnB,KAAK,UAAY,KACjB,KAAK,aAAe,IACtB,CACF,EAGO,SAASM,EAAiBT,EAAkC,CACjE,IAAMU,EAAS,IAAIX,EAAmBC,CAAM,EAG3C,OAAe,QAAW,OAAe,SAAW,CAAC,EAGrD,OAAe,QAAQ,MAAQU,EAG/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","DEFAULT_CONFIG","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","config","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 h=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var S=(s,t)=>{for(var e in t)h(s,e,{get:t[e],enumerable:!0})},v=(s,t,e,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of d(t))!w.call(s,r)&&r!==e&&h(s,r,{get:()=>t[r],enumerable:!(a=u(t,r))||a.enumerable});return s};var p=s=>v(h({},"__esModule",{value:!0}),s);var b={};S(b,{initUmamiRuntime:()=>y});function f(s){let t=new URL(s),e=`${t.protocol}//${t.host}`,a=t.pathname.split("/"),r=a.indexOf("share");if(r===-1||r===a.length-1)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB URL\uFF1A\u672A\u627E\u5230 share \u8DEF\u5F84");let i=a[r+1];if(!i||i.length<10)throw new Error("\u65E0\u6548\u7684\u5206\u4EAB ID");return{baseUrl:e,shareId:i}}var c=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{}}},l=class{constructor(t){this.shareData=null;this.sharePromise=null;let{baseUrl:e,shareId:a}=f(t.shareUrl);this.baseUrl=e,this.shareId=a,this.timezone=t.timezone||"Asia/Shanghai",t.enableCache!==!1?this.cache=new c(`umami-runtime-${a}`,t.cacheTTL||36e5):this.cache=null}async getShareData(){return this.shareData?this.shareData:this.sharePromise?this.sharePromise:(this.sharePromise=(async()=>{let t=await fetch(`${this.baseUrl}/api/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";if(this.cache){let g=this.cache.get(e);if(g)return{...g,_fromCache:!0}}let{websiteId:a,token:r}=await this.getShareData(),i=new URLSearchParams({startAt:"0",endAt:Date.now().toString(),unit:"hour",timezone:this.timezone,compare:"false"});t&&i.set("path",`eq.${t}`);let o=await fetch(`${this.baseUrl}/api/websites/${a}/stats?${i.toString()}`,{headers:{"x-umami-share-token":r}});if(!o.ok)throw new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${o.status}`);let n=await o.json(),m={pageviews:n.pageviews?.value??n.pageviews??0,visitors:n.visitors?.value??n.visitors??0};return this.cache&&this.cache.set(e,m),m}async getSiteStats(){return this.getStats()}async getPageStats(t){return this.getStats(t)}clearCache(){this.cache&&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 p(b);})();
1
+ "use strict";var __oddmiscRuntime=(()=>{var h=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var p=Object.prototype.hasOwnProperty;var w=(s,t)=>{for(var e in t)h(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))!p.call(s,i)&&i!==e&&h(s,i,{get:()=>t[i],enumerable:!(a=u(t,i))||a.enumerable});return s};var v=s=>S(h({},"__esModule",{value:!0}),s);var P={};w(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 n=e.slice(0,a).join("/")+"/api";return{apiBase:`${t.protocol}//${t.host}${n}`,shareId:i}}var c=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,r]of Object.entries(e))a-r.timestamp<this.ttl&&this.cache.set(i,r)}}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{}}},m=class{constructor(t){this.shareData=null;this.sharePromise=null;let{apiBase:e,shareId:a}=f(t.shareUrl);this.apiBase=e,this.shareId=a,this.timezone=t.timezone||"Asia/Hong_Kong",t.enableCache!==!1?this.cache=new c(`umami-runtime-${a}`,t.cacheTTL||36e5):this.cache=null}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";if(this.cache){let g=this.cache.get(e);if(g)return{...g,_fromCache:!0}}let{websiteId:a,token:i}=await this.getShareData(),r=new URLSearchParams({startAt:"0",endAt:Date.now().toString(),unit:"hour",timezone:this.timezone,compare:"false"});t&&r.set("path",`eq.${t}`);let n=await fetch(`${this.apiBase}/websites/${a}/stats?${r.toString()}`,{headers:{"x-umami-share-token":i}});if(!n.ok)throw new Error(`\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25: ${n.status}`);let o=await n.json(),l={pageviews:o.pageviews?.value??o.pageviews??0,visitors:o.visitors?.value??o.visitors??0};return this.cache&&this.cache.set(e,l),l}async getSiteStats(){return this.getStats()}async getPageStats(t){return this.getStats(t)}clearCache(){this.cache&&this.cache.clear(),this.shareData=null,this.sharePromise=null}};function y(s){let t=new m(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.0.4",
3
+ "version": "1.1.0",
4
4
  "description": "杂七杂八奇怪小工具 npm 包",
5
5
  "homepage": "https://github.com/yCENzh/oddmisc#readme",
6
6
  "bugs": {