@stacksjs/commerce 0.75.468 → 0.75.475

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.
@@ -17,10 +17,16 @@ export declare function fetchGrouped(): Promise<GroupedError[]>;
17
17
  export declare function fetchByGroup(type: string, message: string): Promise<ErrorRecord[]>;
18
18
  /**
19
19
  * Fetch error statistics
20
+ *
21
+ * Counted by the database rather than by loading every error: the Errors page
22
+ * asks for these on every visit and again after each resolve, ignore or delete
23
+ * (stacksjs/stacks#3181).
20
24
  */
21
25
  export declare function fetchStats(): Promise<ErrorStats>;
22
26
  /**
23
27
  * Fetch error timeline (hourly counts for the last 24 hours)
28
+ *
29
+ * Reads only the last 24 hours, and only their timestamps.
24
30
  */
25
31
  export declare function fetchTimeline(): Promise<{ hour: string, count: number }[]>;
26
32
  export declare interface ErrorRecord {
@@ -54,3 +60,4 @@ export declare interface ErrorStats {
54
60
  trend: number
55
61
  }
56
62
  export type ErrorResolution = 'unresolved' | 'resolved' | 'ignored';
63
+ declare type GroupingRow = Pick<ErrorRecord, 'id' | 'type' | 'message' | 'resolution' | 'created_at'>;
@@ -1 +1 @@
1
- import{db}from"@stacksjs/database/runtime";export async function fetchById(id){return await db.selectFrom("errors").where("id","=",id).selectAll().executeTakeFirst()}export async function fetchAll(){return await db.selectFrom("errors").selectAll().orderBy("created_at","desc").execute()}export async function fetchGrouped(){const errors=await db.selectFrom("errors").selectAll().orderBy("created_at","desc").execute(),groupMap=new Map;for(const error of errors){const key=`${error.type}:${error.message}`;if(!groupMap.has(key))groupMap.set(key,{errors:[],firstSeen:new Date(error.created_at),lastSeen:new Date(error.created_at)});const group=groupMap.get(key);group.errors.push(error);const errorDate=new Date(error.created_at);if(errorDate<group.firstSeen)group.firstSeen=errorDate;if(errorDate>group.lastSeen)group.lastSeen=errorDate}const groupedErrors=[];for(const[_,group]of groupMap){const representative=group.errors[0];if(!representative)continue;const statusLabel=representative.resolution??"unresolved";groupedErrors.push({id:representative.id,type:representative.type,message:representative.message,stack:representative.stack,status:statusLabel,count:group.errors.length,first_seen:group.firstSeen.toISOString(),last_seen:group.lastSeen.toISOString(),error_ids:group.errors.map((e)=>e.id)})}groupedErrors.sort((a,b)=>new Date(b.last_seen).getTime()-new Date(a.last_seen).getTime());return groupedErrors}export async function fetchByGroup(type,message){return await db.selectFrom("errors").where("type","=",type).where("message","=",message).selectAll().orderBy("created_at","desc").execute()}export async function fetchStats(){const errors=await fetchAll(),now=new Date,yesterday=new Date(now.getTime()-86400000),twoDaysAgo=new Date(now.getTime()-172800000),last24h=errors.filter((e)=>new Date(e.created_at)>=yesterday).length,previous24h=errors.filter((e)=>{const date=new Date(e.created_at);return date>=twoDaysAgo&&date<yesterday}).length,trend=previous24h===0?last24h>0?100:0:Math.round((last24h-previous24h)/previous24h*100);return{total:errors.length,unresolved:errors.filter((e)=>(e.resolution??"unresolved")==="unresolved").length,resolved:errors.filter((e)=>e.resolution==="resolved").length,ignored:errors.filter((e)=>e.resolution==="ignored").length,last_24h:last24h,trend}}export async function fetchTimeline(){const errors=await fetchAll(),now=new Date,timeline=[];for(let i=23;i>=0;i--){const hourStart=new Date(now.getTime()-i*60*60*1000);hourStart.setMinutes(0,0,0);const hourEnd=new Date(hourStart.getTime()+3600000),count=errors.filter((e)=>{const date=new Date(e.created_at);return date>=hourStart&&date<hourEnd}).length;timeline.push({hour:hourStart.toISOString(),count})}return timeline}
1
+ import{db}from"@stacksjs/database/runtime";import{formatDate}from"@stacksjs/orm";export async function fetchById(id){return await db.selectFrom("errors").where("id","=",id).selectAll().executeTakeFirst()}export async function fetchAll(){return await db.selectFrom("errors").selectAll().orderBy("created_at","desc").execute()}export async function fetchGrouped(){const errors=await db.selectFrom("errors").select(["id","type","message","resolution","created_at"]).orderBy("created_at","desc").execute(),groupMap=new Map;for(const error of errors){const key=`${error.type}:${error.message}`;if(!groupMap.has(key))groupMap.set(key,{errors:[],firstSeen:new Date(error.created_at),lastSeen:new Date(error.created_at)});const group=groupMap.get(key);group.errors.push(error);const errorDate=new Date(error.created_at);if(errorDate<group.firstSeen)group.firstSeen=errorDate;if(errorDate>group.lastSeen)group.lastSeen=errorDate}const stacks=await fetchStacks([...groupMap.values()].flatMap((group)=>group.errors[0]?[group.errors[0].id]:[])),groupedErrors=[];for(const[_,group]of groupMap){const representative=group.errors[0];if(!representative)continue;const statusLabel=representative.resolution??"unresolved";groupedErrors.push({id:representative.id,type:representative.type,message:representative.message,stack:stacks.get(Number(representative.id))??null,status:statusLabel,count:group.errors.length,first_seen:group.firstSeen.toISOString(),last_seen:group.lastSeen.toISOString(),error_ids:group.errors.map((e)=>e.id)})}groupedErrors.sort((a,b)=>new Date(b.last_seen).getTime()-new Date(a.last_seen).getTime());return groupedErrors}const STACK_BATCH=500;async function fetchStacks(ids){const stacks=new Map;for(let start=0;start<ids.length;start+=STACK_BATCH){const rows=await db.selectFrom("errors").select(["id","stack"]).where("id","in",ids.slice(start,start+STACK_BATCH)).execute();for(const row of rows)stacks.set(Number(row.id),row.stack??null)}return stacks}export async function fetchByGroup(type,message){return await db.selectFrom("errors").where("type","=",type).where("message","=",message).selectAll().orderBy("created_at","desc").execute()}async function countSince(since){const row=await db.selectFrom("errors").where("created_at",">=",formatDate(since)).select(db.fn.count("id").as("count")).executeTakeFirst();return Number(row?.count??0)}export async function fetchStats(){const now=new Date,yesterday=new Date(now.getTime()-86400000),twoDaysAgo=new Date(now.getTime()-172800000),byResolution=await db.selectFrom("errors").select(["resolution",db.fn.count("id").as("count")]).groupBy("resolution").execute();let total=0,resolved=0,ignored=0;for(const row of byResolution){const count=Number(row.count);total+=count;if(row.resolution==="resolved")resolved+=count;else if(row.resolution==="ignored")ignored+=count}const last24h=await countSince(yesterday),previous24h=await countSince(twoDaysAgo)-last24h,trend=previous24h===0?last24h>0?100:0:Math.round((last24h-previous24h)/previous24h*100);return{total,unresolved:total-resolved-ignored,resolved,ignored,last_24h:last24h,trend}}export async function fetchTimeline(){const now=new Date,firstHour=new Date(now.getTime()-82800000);firstHour.setMinutes(0,0,0);const errors=await db.selectFrom("errors").select(["created_at"]).where("created_at",">=",formatDate(firstHour)).execute(),timeline=[];for(let i=23;i>=0;i--){const hourStart=new Date(now.getTime()-i*60*60*1000);hourStart.setMinutes(0,0,0);const hourEnd=new Date(hourStart.getTime()+3600000),count=errors.filter((e)=>{const date=new Date(e.created_at);return date>=hourStart&&date<hourEnd}).length;timeline.push({hour:hourStart.toISOString(),count})}return timeline}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/commerce",
3
3
  "type": "module",
4
- "version": "0.75.468",
4
+ "version": "0.75.475",
5
5
  "description": "Stacks commerce utilities.",
6
6
  "author": "Chris Breuer",
7
7
  "contributors": [
@@ -57,19 +57,19 @@
57
57
  "prepublishOnly": "bun run build"
58
58
  },
59
59
  "dependencies": {
60
- "@stacksjs/cache": "0.75.468",
61
- "@stacksjs/config": "0.75.468",
62
- "@stacksjs/database": "0.75.468",
63
- "@stacksjs/env": "0.75.468",
64
- "@stacksjs/error-handling": "0.75.468",
65
- "@stacksjs/events": "0.75.468",
66
- "@stacksjs/logging": "0.75.468",
67
- "@stacksjs/orm": "0.75.468",
68
- "@stacksjs/payments": "0.75.468",
69
- "@stacksjs/realtime": "0.75.468",
70
- "@stacksjs/slug": "0.75.468",
71
- "@stacksjs/strings": "0.75.468",
72
- "@stacksjs/validation": "0.75.468",
60
+ "@stacksjs/cache": "0.75.475",
61
+ "@stacksjs/config": "0.75.475",
62
+ "@stacksjs/database": "0.75.475",
63
+ "@stacksjs/env": "0.75.475",
64
+ "@stacksjs/error-handling": "0.75.475",
65
+ "@stacksjs/events": "0.75.475",
66
+ "@stacksjs/logging": "0.75.475",
67
+ "@stacksjs/orm": "0.75.475",
68
+ "@stacksjs/payments": "0.75.475",
69
+ "@stacksjs/realtime": "0.75.475",
70
+ "@stacksjs/slug": "0.75.475",
71
+ "@stacksjs/strings": "0.75.475",
72
+ "@stacksjs/validation": "0.75.475",
73
73
  "ts-spreadsheets": "^0.2.0"
74
74
  },
75
75
  "devDependencies": {