pi-mega-compact 0.8.16 → 0.8.18

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.
Files changed (98) hide show
  1. package/README.md +1 -1
  2. package/dist/extensions/dashboard-server/api-contracts/endpoints.js +12 -0
  3. package/dist/extensions/dashboard-server/api-contracts.test.js +19 -6
  4. package/dist/extensions/dashboard-server/helpers.js +37 -0
  5. package/dist/extensions/dashboard-server/html/all-repos-tab.js +26 -0
  6. package/dist/extensions/dashboard-server/html/body-open.js +23 -0
  7. package/dist/extensions/dashboard-server/html/current-repo-tab.js +130 -0
  8. package/dist/extensions/dashboard-server/html/head-open.js +16 -0
  9. package/dist/extensions/dashboard-server/html/high-score-tab.js +25 -0
  10. package/dist/extensions/dashboard-server/html/repo-detail-modal.js +26 -0
  11. package/dist/extensions/dashboard-server/html/script.js +259 -0
  12. package/dist/extensions/dashboard-server/html/styles.js +103 -0
  13. package/dist/extensions/dashboard-server/html/summary-tab.js +19 -0
  14. package/dist/extensions/dashboard-server/html-template.js +41 -0
  15. package/dist/extensions/dashboard-server/server.js +83 -0
  16. package/dist/extensions/mega-compact-s38.test.js +33 -0
  17. package/dist/extensions/mega-events/agent-handlers.js +51 -3
  18. package/dist/extensions/mega-events/compact-handlers.js +10 -4
  19. package/dist/extensions/mega-events/error-classifier.js +24 -1
  20. package/dist/extensions/mega-events/send-safe.js +12 -0
  21. package/dist/extensions/mega-events.js +1 -0
  22. package/dist/extensions/mega-runtime/state.js +17 -1
  23. package/dist/src/store/sqlite/connection.js +35 -0
  24. package/dist/src/store/sqlite/global-index.js +199 -1
  25. package/dist/src/store/sqlite/global-index.test.js +136 -0
  26. package/dist/src/store/sqlite/index-store.js +167 -0
  27. package/dist/src/store/sqlite/memory.js +54 -0
  28. package/dist/src/store/sqlite/minhash-lsh.js +47 -0
  29. package/dist/src/store/sqlite/sessions.js +39 -0
  30. package/dist/src/store/sqlite/transaction.js +19 -0
  31. package/dist/src/vectorStore/add.js +260 -0
  32. package/dist/src/vectorStore/dedup.js +52 -0
  33. package/dist/src/vectorStore/index.js +10 -0
  34. package/dist/src/vectorStore/queries.js +83 -0
  35. package/dist/src/vectorStore/search.js +95 -0
  36. package/dist/src/vectorStore/session.js +19 -0
  37. package/dist/src/vectorStore/store.js +105 -0
  38. package/dist/src/vectorStore/types.js +6 -0
  39. package/dist/src/vectorStore/utils.js +23 -0
  40. package/extensions/dashboard-client/dist/assets/{AchievementTiles-BozP_h3c.js → AchievementTiles-CNWZ5NjP.js} +2 -2
  41. package/extensions/dashboard-client/dist/assets/{AchievementTiles-BozP_h3c.js.map → AchievementTiles-CNWZ5NjP.js.map} +1 -1
  42. package/extensions/dashboard-client/dist/assets/{AchievementsTab-DKgqx1RQ.js → AchievementsTab-BKPifNKj.js} +2 -2
  43. package/extensions/dashboard-client/dist/assets/{AchievementsTab-DKgqx1RQ.js.map → AchievementsTab-BKPifNKj.js.map} +1 -1
  44. package/extensions/dashboard-client/dist/assets/{CacheTab-CPvso4-0.js → CacheTab--epzrQHD.js} +2 -2
  45. package/extensions/dashboard-client/dist/assets/{CacheTab-CPvso4-0.js.map → CacheTab--epzrQHD.js.map} +1 -1
  46. package/extensions/dashboard-client/dist/assets/{ConfigTab-DXD7zbqy.js → ConfigTab-D3ralwSj.js} +2 -2
  47. package/extensions/dashboard-client/dist/assets/ConfigTab-D3ralwSj.js.map +1 -0
  48. package/extensions/dashboard-client/dist/assets/EventsTab-BKkuVQsM.js +2 -0
  49. package/extensions/dashboard-client/dist/assets/EventsTab-BKkuVQsM.js.map +1 -0
  50. package/extensions/dashboard-client/dist/assets/{GameTab-Qj19W1Y-.js → GameTab-CgUvITYy.js} +2 -2
  51. package/extensions/dashboard-client/dist/assets/{GameTab-Qj19W1Y-.js.map → GameTab-CgUvITYy.js.map} +1 -1
  52. package/extensions/dashboard-client/dist/assets/{MetricsTab-DA27vA95.js → MetricsTab-DrlJqXGL.js} +2 -2
  53. package/extensions/dashboard-client/dist/assets/{MetricsTab-DA27vA95.js.map → MetricsTab-DrlJqXGL.js.map} +1 -1
  54. package/extensions/dashboard-client/dist/assets/{OverviewTab-lVGR1Jl_.js → OverviewTab-_gD756wJ.js} +2 -2
  55. package/extensions/dashboard-client/dist/assets/{OverviewTab-lVGR1Jl_.js.map → OverviewTab-_gD756wJ.js.map} +1 -1
  56. package/extensions/dashboard-client/dist/assets/{ReposTab-_HNR3qYj.js → ReposTab-DjEZmqUb.js} +2 -2
  57. package/extensions/dashboard-client/dist/assets/{ReposTab-_HNR3qYj.js.map → ReposTab-DjEZmqUb.js.map} +1 -1
  58. package/extensions/dashboard-client/dist/assets/SessionsTab-DsYMUzoh.js +70 -0
  59. package/extensions/dashboard-client/dist/assets/SessionsTab-DsYMUzoh.js.map +1 -0
  60. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-B7aPcBMs.js → TimeSavedCard-BbAXT1wO.js} +2 -2
  61. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-B7aPcBMs.js.map → TimeSavedCard-BbAXT1wO.js.map} +1 -1
  62. package/extensions/dashboard-client/dist/assets/{index-Cfxniu2R.css → index-BTft6DM_.css} +1 -1
  63. package/extensions/dashboard-client/dist/assets/{index-B2JJExE9.js → index-CnRzusfi.js} +10 -10
  64. package/extensions/dashboard-client/dist/assets/{index-B2JJExE9.js.map → index-CnRzusfi.js.map} +1 -1
  65. package/extensions/dashboard-client/dist/assets/useSSE-Bf-1rZ2N.js +2 -0
  66. package/extensions/dashboard-client/dist/assets/useSSE-Bf-1rZ2N.js.map +1 -0
  67. package/extensions/dashboard-client/dist/index.html +2 -2
  68. package/extensions/dashboard-client/package-lock.json +374 -2
  69. package/extensions/dashboard-client/package.json +2 -1
  70. package/extensions/dashboard-client/src/App.tsx +5 -1
  71. package/extensions/dashboard-client/src/api/client.ts +12 -0
  72. package/extensions/dashboard-client/src/components/ActiveSessionsTable.tsx +185 -0
  73. package/extensions/dashboard-client/src/components/EventStream.tsx +3 -0
  74. package/extensions/dashboard-client/src/components/SessionsMemoryChart.tsx +199 -0
  75. package/extensions/dashboard-client/src/main.tsx +1 -0
  76. package/extensions/dashboard-client/src/styles/sessions.css +193 -0
  77. package/extensions/dashboard-client/src/tabs/ConfigTab.tsx +6 -3
  78. package/extensions/dashboard-client/src/tabs/SessionsTab.tsx +193 -0
  79. package/extensions/dashboard-server/api-contracts/core.ts +25 -0
  80. package/extensions/dashboard-server/api-contracts/endpoints.ts +102 -0
  81. package/extensions/dashboard-server/api-contracts/index.ts +10 -1
  82. package/extensions/dashboard-server/api-contracts.test.ts +20 -7
  83. package/extensions/dashboard-server/server.ts +100 -0
  84. package/extensions/mega-compact-s38.test.ts +63 -0
  85. package/extensions/mega-events/agent-handlers.ts +47 -3
  86. package/extensions/mega-events/compact-handlers.ts +11 -4
  87. package/extensions/mega-events/error-classifier.ts +25 -1
  88. package/extensions/mega-events/send-safe.ts +37 -0
  89. package/extensions/mega-events.ts +1 -0
  90. package/extensions/mega-runtime/state.ts +31 -0
  91. package/package.json +1 -1
  92. package/src/store/sqlite/global-index.test.ts +222 -0
  93. package/src/store/sqlite/global-index.ts +316 -1
  94. package/dist/extensions/dashboard-client/src/hooks/useApi.js +0 -51
  95. package/dist/extensions/dashboard-client/src/hooks/useSSE.js +0 -63
  96. package/extensions/dashboard-client/dist/assets/ConfigTab-DXD7zbqy.js.map +0 -1
  97. package/extensions/dashboard-client/dist/assets/EventsTab-BGeuYvM3.js +0 -2
  98. package/extensions/dashboard-client/dist/assets/EventsTab-BGeuYvM3.js.map +0 -1
@@ -0,0 +1,2 @@
1
+ import{r as e}from"./index-CnRzusfi.js";function R(i={}){const{maxEvents:c=500,maxReconnects:l=5}=i,[f,v]=e.useState([]),[E,n]=e.useState("connecting"),[S,p]=e.useState(0),[m,d]=e.useState(null),o=e.useRef(0),s=e.useRef(null),r=e.useCallback(()=>{s.current&&s.current.close(),n("connecting");const t=new EventSource("/api/events");s.current=t,t.onopen=()=>{n("connected"),o.current=0},t.onmessage=g=>{try{const x=JSON.parse(g.data);v(u=>{const a=[...u,x];return a.length>c?a.slice(-c):a}),p(u=>u+1),d(Date.now())}catch{}},t.onerror=()=>{t.close(),o.current<l?(o.current+=1,n("connecting"),setTimeout(r,1e3)):n("disconnected")}},[c,l]);return e.useEffect(()=>(r(),()=>{s.current?.close()}),[r]),{events:f,status:E,eventCount:S,lastEventAt:m}}export{R as u};
2
+ //# sourceMappingURL=useSSE-Bf-1rZ2N.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSSE-Bf-1rZ2N.js","sources":["../../src/hooks/useSSE.ts"],"sourcesContent":["/**\n * dashboard-client/src/hooks/useSSE.ts — Server-Sent Events hook.\n *\n * Connects to /api/events, provides real-time event stream.\n * SPRINT-B1: basic connection. SPRINT-D1: reconnect with backoff.\n */\n\nimport { useState, useEffect, useRef, useCallback } from 'react';\nimport type { SseEvent } from '../../../dashboard-server/api-contracts';\n\nexport type SseStatus = 'connecting' | 'connected' | 'disconnected' | 'error';\n\nexport interface UseSSEResult {\n events: SseEvent[];\n status: SseStatus;\n eventCount: number;\n lastEventAt: number | null;\n}\n\nexport interface UseSSEOptions {\n /** Max events to keep in memory (ring buffer). */\n maxEvents?: number;\n /** Max reconnect attempts before giving up. */\n maxReconnects?: number;\n}\n\n// SPRINT-D1-REMAINING: exponential backoff (1s → 2s → 4s → max 30s).\n// SPRINT-D1-REMAINING: after 5 failures, set status='disconnected'.\n\nexport function useSSE(options: UseSSEOptions = {}): UseSSEResult {\n const { maxEvents = 500, maxReconnects = 5 } = options;\n const [events, setEvents] = useState<SseEvent[]>([]);\n const [status, setStatus] = useState<SseStatus>('connecting');\n const [eventCount, setEventCount] = useState(0);\n const [lastEventAt, setLastEventAt] = useState<number | null>(null);\n const reconnectCount = useRef(0);\n const sourceRef = useRef<EventSource | null>(null);\n\n const connect = useCallback(() => {\n if (sourceRef.current) {\n sourceRef.current.close();\n }\n setStatus('connecting');\n const source = new EventSource('/api/events');\n sourceRef.current = source;\n\n source.onopen = () => {\n setStatus('connected');\n reconnectCount.current = 0;\n };\n\n source.onmessage = (event) => {\n try {\n const parsed = JSON.parse(event.data) as SseEvent;\n setEvents(prev => {\n const next = [...prev, parsed];\n return next.length > maxEvents ? next.slice(-maxEvents) : next;\n });\n setEventCount(c => c + 1);\n setLastEventAt(Date.now());\n } catch {\n // non-fatal: skip malformed events\n }\n };\n\n source.onerror = () => {\n source.close();\n if (reconnectCount.current < maxReconnects) {\n reconnectCount.current += 1;\n setStatus('connecting');\n // SPRINT-D1: use exponential backoff instead of fixed delay\n setTimeout(connect, 1000);\n } else {\n setStatus('disconnected');\n }\n };\n }, [maxEvents, maxReconnects]);\n\n useEffect(() => {\n connect();\n return () => {\n sourceRef.current?.close();\n };\n }, [connect]);\n\n return { events, status, eventCount, lastEventAt };\n}\n"],"names":["useSSE","options","maxEvents","maxReconnects","events","setEvents","useState","status","setStatus","eventCount","setEventCount","lastEventAt","setLastEventAt","reconnectCount","useRef","sourceRef","connect","useCallback","source","event","parsed","prev","next","c","useEffect"],"mappings":"wCA6BO,SAASA,EAAOC,EAAyB,GAAkB,CAChE,KAAM,CAAE,UAAAC,EAAY,IAAK,cAAAC,EAAgB,GAAMF,EACzC,CAACG,EAAQC,CAAS,EAAIC,EAAAA,SAAqB,CAAA,CAAE,EAC7C,CAACC,EAAQC,CAAS,EAAIF,EAAAA,SAAoB,YAAY,EACtD,CAACG,EAAYC,CAAa,EAAIJ,EAAAA,SAAS,CAAC,EACxC,CAACK,EAAaC,CAAc,EAAIN,EAAAA,SAAwB,IAAI,EAC5DO,EAAiBC,EAAAA,OAAO,CAAC,EACzBC,EAAYD,EAAAA,OAA2B,IAAI,EAE3CE,EAAUC,EAAAA,YAAY,IAAM,CAC5BF,EAAU,SACZA,EAAU,QAAQ,MAAA,EAEpBP,EAAU,YAAY,EACtB,MAAMU,EAAS,IAAI,YAAY,aAAa,EAC5CH,EAAU,QAAUG,EAEpBA,EAAO,OAAS,IAAM,CACpBV,EAAU,WAAW,EACrBK,EAAe,QAAU,CAC3B,EAEAK,EAAO,UAAaC,GAAU,CAC5B,GAAI,CACF,MAAMC,EAAS,KAAK,MAAMD,EAAM,IAAI,EACpCd,EAAUgB,GAAQ,CAChB,MAAMC,EAAO,CAAC,GAAGD,EAAMD,CAAM,EAC7B,OAAOE,EAAK,OAASpB,EAAYoB,EAAK,MAAM,CAACpB,CAAS,EAAIoB,CAC5D,CAAC,EACDZ,EAAca,GAAKA,EAAI,CAAC,EACxBX,EAAe,KAAK,KAAK,CAC3B,MAAQ,CAER,CACF,EAEAM,EAAO,QAAU,IAAM,CACrBA,EAAO,MAAA,EACHL,EAAe,QAAUV,GAC3BU,EAAe,SAAW,EAC1BL,EAAU,YAAY,EAEtB,WAAWQ,EAAS,GAAI,GAExBR,EAAU,cAAc,CAE5B,CACF,EAAG,CAACN,EAAWC,CAAa,CAAC,EAE7BqB,OAAAA,EAAAA,UAAU,KACRR,EAAA,EACO,IAAM,CACXD,EAAU,SAAS,MAAA,CACrB,GACC,CAACC,CAAO,CAAC,EAEL,CAAE,OAAAZ,EAAQ,OAAAG,EAAQ,WAAAE,EAAY,YAAAE,CAAA,CACvC"}
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>mega-compact dashboard</title>
7
- <script type="module" crossorigin src="./assets/index-B2JJExE9.js"></script>
8
- <link rel="stylesheet" crossorigin href="./assets/index-Cfxniu2R.css">
7
+ <script type="module" crossorigin src="./assets/index-CnRzusfi.js"></script>
8
+ <link rel="stylesheet" crossorigin href="./assets/index-BTft6DM_.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
@@ -9,7 +9,8 @@
9
9
  "version": "0.8.9",
10
10
  "dependencies": {
11
11
  "react": "^18.3.1",
12
- "react-dom": "^18.3.1"
12
+ "react-dom": "^18.3.1",
13
+ "recharts": "^2.13.0"
13
14
  },
14
15
  "devDependencies": {
15
16
  "@types/react": "^18.3.12",
@@ -253,6 +254,15 @@
253
254
  "@babel/core": "^7.0.0-0"
254
255
  }
255
256
  },
257
+ "node_modules/@babel/runtime": {
258
+ "version": "7.29.7",
259
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz",
260
+ "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
261
+ "license": "MIT",
262
+ "engines": {
263
+ "node": ">=6.9.0"
264
+ }
265
+ },
256
266
  "node_modules/@babel/template": {
257
267
  "version": "7.29.7",
258
268
  "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
@@ -1183,6 +1193,69 @@
1183
1193
  "@babel/types": "^7.28.2"
1184
1194
  }
1185
1195
  },
1196
+ "node_modules/@types/d3-array": {
1197
+ "version": "3.2.2",
1198
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",
1199
+ "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==",
1200
+ "license": "MIT"
1201
+ },
1202
+ "node_modules/@types/d3-color": {
1203
+ "version": "3.1.3",
1204
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
1205
+ "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
1206
+ "license": "MIT"
1207
+ },
1208
+ "node_modules/@types/d3-ease": {
1209
+ "version": "3.0.2",
1210
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
1211
+ "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
1212
+ "license": "MIT"
1213
+ },
1214
+ "node_modules/@types/d3-interpolate": {
1215
+ "version": "3.0.4",
1216
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
1217
+ "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
1218
+ "license": "MIT",
1219
+ "dependencies": {
1220
+ "@types/d3-color": "*"
1221
+ }
1222
+ },
1223
+ "node_modules/@types/d3-path": {
1224
+ "version": "3.1.1",
1225
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
1226
+ "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
1227
+ "license": "MIT"
1228
+ },
1229
+ "node_modules/@types/d3-scale": {
1230
+ "version": "4.0.9",
1231
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
1232
+ "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
1233
+ "license": "MIT",
1234
+ "dependencies": {
1235
+ "@types/d3-time": "*"
1236
+ }
1237
+ },
1238
+ "node_modules/@types/d3-shape": {
1239
+ "version": "3.1.8",
1240
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz",
1241
+ "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==",
1242
+ "license": "MIT",
1243
+ "dependencies": {
1244
+ "@types/d3-path": "*"
1245
+ }
1246
+ },
1247
+ "node_modules/@types/d3-time": {
1248
+ "version": "3.0.4",
1249
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
1250
+ "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
1251
+ "license": "MIT"
1252
+ },
1253
+ "node_modules/@types/d3-timer": {
1254
+ "version": "3.0.2",
1255
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
1256
+ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
1257
+ "license": "MIT"
1258
+ },
1186
1259
  "node_modules/@types/estree": {
1187
1260
  "version": "1.0.9",
1188
1261
  "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
@@ -1307,6 +1380,15 @@
1307
1380
  ],
1308
1381
  "license": "CC-BY-4.0"
1309
1382
  },
1383
+ "node_modules/clsx": {
1384
+ "version": "2.1.1",
1385
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
1386
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
1387
+ "license": "MIT",
1388
+ "engines": {
1389
+ "node": ">=6"
1390
+ }
1391
+ },
1310
1392
  "node_modules/convert-source-map": {
1311
1393
  "version": "2.0.0",
1312
1394
  "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
@@ -1318,9 +1400,129 @@
1318
1400
  "version": "3.2.3",
1319
1401
  "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
1320
1402
  "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
1321
- "dev": true,
1322
1403
  "license": "MIT"
1323
1404
  },
1405
+ "node_modules/d3-array": {
1406
+ "version": "3.2.4",
1407
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
1408
+ "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
1409
+ "license": "ISC",
1410
+ "dependencies": {
1411
+ "internmap": "1 - 2"
1412
+ },
1413
+ "engines": {
1414
+ "node": ">=12"
1415
+ }
1416
+ },
1417
+ "node_modules/d3-color": {
1418
+ "version": "3.1.0",
1419
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
1420
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
1421
+ "license": "ISC",
1422
+ "engines": {
1423
+ "node": ">=12"
1424
+ }
1425
+ },
1426
+ "node_modules/d3-ease": {
1427
+ "version": "3.0.1",
1428
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
1429
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
1430
+ "license": "BSD-3-Clause",
1431
+ "engines": {
1432
+ "node": ">=12"
1433
+ }
1434
+ },
1435
+ "node_modules/d3-format": {
1436
+ "version": "3.1.2",
1437
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz",
1438
+ "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==",
1439
+ "license": "ISC",
1440
+ "engines": {
1441
+ "node": ">=12"
1442
+ }
1443
+ },
1444
+ "node_modules/d3-interpolate": {
1445
+ "version": "3.0.1",
1446
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
1447
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
1448
+ "license": "ISC",
1449
+ "dependencies": {
1450
+ "d3-color": "1 - 3"
1451
+ },
1452
+ "engines": {
1453
+ "node": ">=12"
1454
+ }
1455
+ },
1456
+ "node_modules/d3-path": {
1457
+ "version": "3.1.0",
1458
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
1459
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
1460
+ "license": "ISC",
1461
+ "engines": {
1462
+ "node": ">=12"
1463
+ }
1464
+ },
1465
+ "node_modules/d3-scale": {
1466
+ "version": "4.0.2",
1467
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
1468
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
1469
+ "license": "ISC",
1470
+ "dependencies": {
1471
+ "d3-array": "2.10.0 - 3",
1472
+ "d3-format": "1 - 3",
1473
+ "d3-interpolate": "1.2.0 - 3",
1474
+ "d3-time": "2.1.1 - 3",
1475
+ "d3-time-format": "2 - 4"
1476
+ },
1477
+ "engines": {
1478
+ "node": ">=12"
1479
+ }
1480
+ },
1481
+ "node_modules/d3-shape": {
1482
+ "version": "3.2.0",
1483
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
1484
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
1485
+ "license": "ISC",
1486
+ "dependencies": {
1487
+ "d3-path": "^3.1.0"
1488
+ },
1489
+ "engines": {
1490
+ "node": ">=12"
1491
+ }
1492
+ },
1493
+ "node_modules/d3-time": {
1494
+ "version": "3.1.0",
1495
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
1496
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
1497
+ "license": "ISC",
1498
+ "dependencies": {
1499
+ "d3-array": "2 - 3"
1500
+ },
1501
+ "engines": {
1502
+ "node": ">=12"
1503
+ }
1504
+ },
1505
+ "node_modules/d3-time-format": {
1506
+ "version": "4.1.0",
1507
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
1508
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
1509
+ "license": "ISC",
1510
+ "dependencies": {
1511
+ "d3-time": "1 - 3"
1512
+ },
1513
+ "engines": {
1514
+ "node": ">=12"
1515
+ }
1516
+ },
1517
+ "node_modules/d3-timer": {
1518
+ "version": "3.0.1",
1519
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
1520
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
1521
+ "license": "ISC",
1522
+ "engines": {
1523
+ "node": ">=12"
1524
+ }
1525
+ },
1324
1526
  "node_modules/debug": {
1325
1527
  "version": "4.4.3",
1326
1528
  "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -1339,6 +1541,22 @@
1339
1541
  }
1340
1542
  }
1341
1543
  },
1544
+ "node_modules/decimal.js-light": {
1545
+ "version": "2.5.1",
1546
+ "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
1547
+ "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
1548
+ "license": "MIT"
1549
+ },
1550
+ "node_modules/dom-helpers": {
1551
+ "version": "5.2.1",
1552
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
1553
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
1554
+ "license": "MIT",
1555
+ "dependencies": {
1556
+ "@babel/runtime": "^7.8.7",
1557
+ "csstype": "^3.0.2"
1558
+ }
1559
+ },
1342
1560
  "node_modules/electron-to-chromium": {
1343
1561
  "version": "1.5.393",
1344
1562
  "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.393.tgz",
@@ -1395,6 +1613,21 @@
1395
1613
  "node": ">=6"
1396
1614
  }
1397
1615
  },
1616
+ "node_modules/eventemitter3": {
1617
+ "version": "4.0.7",
1618
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
1619
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
1620
+ "license": "MIT"
1621
+ },
1622
+ "node_modules/fast-equals": {
1623
+ "version": "5.4.1",
1624
+ "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.1.tgz",
1625
+ "integrity": "sha512-DjlFSM5Pk9cGcL0q5QXl66eGzx0N6szNgaswwc5ZphlBohjTVJSnGgI+rJVOgOi65qUoQnDZN4nDqi33udtydQ==",
1626
+ "license": "MIT",
1627
+ "engines": {
1628
+ "node": ">=6.0.0"
1629
+ }
1630
+ },
1398
1631
  "node_modules/fsevents": {
1399
1632
  "version": "2.3.3",
1400
1633
  "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -1420,6 +1653,15 @@
1420
1653
  "node": ">=6.9.0"
1421
1654
  }
1422
1655
  },
1656
+ "node_modules/internmap": {
1657
+ "version": "2.0.3",
1658
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
1659
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
1660
+ "license": "ISC",
1661
+ "engines": {
1662
+ "node": ">=12"
1663
+ }
1664
+ },
1423
1665
  "node_modules/js-tokens": {
1424
1666
  "version": "4.0.0",
1425
1667
  "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -1452,6 +1694,12 @@
1452
1694
  "node": ">=6"
1453
1695
  }
1454
1696
  },
1697
+ "node_modules/lodash": {
1698
+ "version": "4.18.1",
1699
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
1700
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
1701
+ "license": "MIT"
1702
+ },
1455
1703
  "node_modules/loose-envify": {
1456
1704
  "version": "1.4.0",
1457
1705
  "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -1510,6 +1758,15 @@
1510
1758
  "node": ">=18"
1511
1759
  }
1512
1760
  },
1761
+ "node_modules/object-assign": {
1762
+ "version": "4.1.1",
1763
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1764
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
1765
+ "license": "MIT",
1766
+ "engines": {
1767
+ "node": ">=0.10.0"
1768
+ }
1769
+ },
1513
1770
  "node_modules/picocolors": {
1514
1771
  "version": "1.1.1",
1515
1772
  "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -1546,6 +1803,23 @@
1546
1803
  "node": "^10 || ^12 || >=14"
1547
1804
  }
1548
1805
  },
1806
+ "node_modules/prop-types": {
1807
+ "version": "15.8.1",
1808
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
1809
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
1810
+ "license": "MIT",
1811
+ "dependencies": {
1812
+ "loose-envify": "^1.4.0",
1813
+ "object-assign": "^4.1.1",
1814
+ "react-is": "^16.13.1"
1815
+ }
1816
+ },
1817
+ "node_modules/prop-types/node_modules/react-is": {
1818
+ "version": "16.13.1",
1819
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
1820
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
1821
+ "license": "MIT"
1822
+ },
1549
1823
  "node_modules/react": {
1550
1824
  "version": "18.3.1",
1551
1825
  "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
@@ -1571,6 +1845,12 @@
1571
1845
  "react": "^18.3.1"
1572
1846
  }
1573
1847
  },
1848
+ "node_modules/react-is": {
1849
+ "version": "18.3.1",
1850
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
1851
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
1852
+ "license": "MIT"
1853
+ },
1574
1854
  "node_modules/react-refresh": {
1575
1855
  "version": "0.17.0",
1576
1856
  "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
@@ -1581,6 +1861,70 @@
1581
1861
  "node": ">=0.10.0"
1582
1862
  }
1583
1863
  },
1864
+ "node_modules/react-smooth": {
1865
+ "version": "4.0.4",
1866
+ "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
1867
+ "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==",
1868
+ "license": "MIT",
1869
+ "dependencies": {
1870
+ "fast-equals": "^5.0.1",
1871
+ "prop-types": "^15.8.1",
1872
+ "react-transition-group": "^4.4.5"
1873
+ },
1874
+ "peerDependencies": {
1875
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
1876
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
1877
+ }
1878
+ },
1879
+ "node_modules/react-transition-group": {
1880
+ "version": "4.4.5",
1881
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
1882
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
1883
+ "license": "BSD-3-Clause",
1884
+ "dependencies": {
1885
+ "@babel/runtime": "^7.5.5",
1886
+ "dom-helpers": "^5.0.1",
1887
+ "loose-envify": "^1.4.0",
1888
+ "prop-types": "^15.6.2"
1889
+ },
1890
+ "peerDependencies": {
1891
+ "react": ">=16.6.0",
1892
+ "react-dom": ">=16.6.0"
1893
+ }
1894
+ },
1895
+ "node_modules/recharts": {
1896
+ "version": "2.15.4",
1897
+ "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz",
1898
+ "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==",
1899
+ "deprecated": "1.x and 2.x branches are no longer active. Bump to Recharts v3 to receive latest features and bugfixes. See https://github.com/recharts/recharts/wiki/3.0-migration-guide",
1900
+ "license": "MIT",
1901
+ "dependencies": {
1902
+ "clsx": "^2.0.0",
1903
+ "eventemitter3": "^4.0.1",
1904
+ "lodash": "^4.17.21",
1905
+ "react-is": "^18.3.1",
1906
+ "react-smooth": "^4.0.4",
1907
+ "recharts-scale": "^0.4.4",
1908
+ "tiny-invariant": "^1.3.1",
1909
+ "victory-vendor": "^36.6.8"
1910
+ },
1911
+ "engines": {
1912
+ "node": ">=14"
1913
+ },
1914
+ "peerDependencies": {
1915
+ "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
1916
+ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
1917
+ }
1918
+ },
1919
+ "node_modules/recharts-scale": {
1920
+ "version": "0.4.5",
1921
+ "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
1922
+ "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
1923
+ "license": "MIT",
1924
+ "dependencies": {
1925
+ "decimal.js-light": "^2.4.1"
1926
+ }
1927
+ },
1584
1928
  "node_modules/rollup": {
1585
1929
  "version": "4.62.2",
1586
1930
  "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz",
@@ -1655,6 +1999,12 @@
1655
1999
  "node": ">=0.10.0"
1656
2000
  }
1657
2001
  },
2002
+ "node_modules/tiny-invariant": {
2003
+ "version": "1.3.3",
2004
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
2005
+ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
2006
+ "license": "MIT"
2007
+ },
1658
2008
  "node_modules/typescript": {
1659
2009
  "version": "5.9.3",
1660
2010
  "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
@@ -1700,6 +2050,28 @@
1700
2050
  "browserslist": ">= 4.21.0"
1701
2051
  }
1702
2052
  },
2053
+ "node_modules/victory-vendor": {
2054
+ "version": "36.9.2",
2055
+ "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
2056
+ "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
2057
+ "license": "MIT AND ISC",
2058
+ "dependencies": {
2059
+ "@types/d3-array": "^3.0.3",
2060
+ "@types/d3-ease": "^3.0.0",
2061
+ "@types/d3-interpolate": "^3.0.1",
2062
+ "@types/d3-scale": "^4.0.2",
2063
+ "@types/d3-shape": "^3.1.0",
2064
+ "@types/d3-time": "^3.0.0",
2065
+ "@types/d3-timer": "^3.0.0",
2066
+ "d3-array": "^3.1.6",
2067
+ "d3-ease": "^3.0.1",
2068
+ "d3-interpolate": "^3.0.1",
2069
+ "d3-scale": "^4.0.2",
2070
+ "d3-shape": "^3.1.0",
2071
+ "d3-time": "^3.0.0",
2072
+ "d3-timer": "^3.0.1"
2073
+ }
2074
+ },
1703
2075
  "node_modules/vite": {
1704
2076
  "version": "5.4.21",
1705
2077
  "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
@@ -12,7 +12,8 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "react": "^18.3.1",
15
- "react-dom": "^18.3.1"
15
+ "react-dom": "^18.3.1",
16
+ "recharts": "^2.13.0"
16
17
  },
17
18
  "devDependencies": {
18
19
  "@types/react": "^18.3.12",
@@ -22,6 +22,7 @@ const MetricsTab = React.lazy(() => import("./tabs/MetricsTab"));
22
22
  const CacheTab = React.lazy(() => import("./tabs/CacheTab"));
23
23
  const GameTab = React.lazy(() => import("./tabs/GameTab"));
24
24
  const AchievementsTab = React.lazy(() => import("./tabs/AchievementsTab"));
25
+ const SessionsTab = React.lazy(() => import("./tabs/SessionsTab"));
25
26
 
26
27
  export type TabId =
27
28
  | "overview"
@@ -31,7 +32,8 @@ export type TabId =
31
32
  | "metrics"
32
33
  | "cache"
33
34
  | "game"
34
- | "achievements";
35
+ | "achievements"
36
+ | "sessions";
35
37
 
36
38
  const TABS: Array<{ id: TabId; label: string }> = [
37
39
  { id: "overview", label: "Overview" },
@@ -42,6 +44,7 @@ const TABS: Array<{ id: TabId; label: string }> = [
42
44
  { id: "cache", label: "Cache" },
43
45
  { id: "game", label: "Game" },
44
46
  { id: "achievements", label: "Achievements" },
47
+ { id: "sessions", label: "Sessions" },
45
48
  ];
46
49
 
47
50
  export default function App(): React.ReactElement {
@@ -88,6 +91,7 @@ export default function App(): React.ReactElement {
88
91
  {activeTab === "cache" && <CacheTab />}
89
92
  {activeTab === "game" && <GameTab />}
90
93
  {activeTab === "achievements" && <AchievementsTab />}
94
+ {activeTab === "sessions" && <SessionsTab />}
91
95
  </React.Suspense>
92
96
  </main>
93
97
  </div>
@@ -26,6 +26,8 @@ import type {
26
26
  PerfResponse,
27
27
  PerfQuery,
28
28
  AchievementRow,
29
+ SessionsResponse,
30
+ SessionTimeseriesResponse,
29
31
  } from "@contracts";
30
32
 
31
33
  /** Error thrown when a dashboard API response is not 2xx. */
@@ -143,3 +145,13 @@ export function fetchPerf(params: PerfQuery = {}): Promise<PerfResponse> {
143
145
  export function fetchAchievements(): Promise<AchievementRow[]> {
144
146
  return getJson<AchievementRow[]>(ENDPOINTS.achievements.path);
145
147
  }
148
+
149
+ export function fetchSessions(): Promise<SessionsResponse> {
150
+ return getJson<SessionsResponse>(ENDPOINTS.sessions.path);
151
+ }
152
+
153
+ export function fetchSessionTimeseries(minutes: number): Promise<SessionTimeseriesResponse> {
154
+ return getJson<SessionTimeseriesResponse>(
155
+ `${ENDPOINTS.sessionTimeseries.path}${query({ minutes })}`,
156
+ );
157
+ }