MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add deduplication by hash/hitcount

+329 -42
+1
docs/reports/migrations/0001_fluffy_lockheed.sql
··· 1 + CREATE UNIQUE INDEX `reports_trace_unique` ON `reports` (`trace`);
+245
docs/reports/migrations/meta/0001_snapshot.json
··· 1 + { 2 + "version": "6", 3 + "dialect": "sqlite", 4 + "id": "bbaeff3b-328a-4c14-a851-5a252a334c26", 5 + "prevId": "f888823f-8746-428a-894d-7a6e8578f8d2", 6 + "tables": { 7 + "frames": { 8 + "name": "frames", 9 + "columns": { 10 + "hash": { 11 + "name": "hash", 12 + "type": "text", 13 + "primaryKey": true, 14 + "notNull": true, 15 + "autoincrement": false 16 + }, 17 + "frame": { 18 + "name": "frame", 19 + "type": "text", 20 + "primaryKey": false, 21 + "notNull": true, 22 + "autoincrement": false 23 + } 24 + }, 25 + "indexes": {}, 26 + "foreignKeys": {}, 27 + "compositePrimaryKeys": {}, 28 + "uniqueConstraints": {}, 29 + "checkConstraints": {} 30 + }, 31 + "report_frames": { 32 + "name": "report_frames", 33 + "columns": { 34 + "report_id": { 35 + "name": "report_id", 36 + "type": "text", 37 + "primaryKey": false, 38 + "notNull": true, 39 + "autoincrement": false 40 + }, 41 + "frame_index": { 42 + "name": "frame_index", 43 + "type": "integer", 44 + "primaryKey": false, 45 + "notNull": true, 46 + "autoincrement": false 47 + }, 48 + "frame_hash": { 49 + "name": "frame_hash", 50 + "type": "text", 51 + "primaryKey": false, 52 + "notNull": true, 53 + "autoincrement": false 54 + } 55 + }, 56 + "indexes": {}, 57 + "foreignKeys": { 58 + "report_frames_report_id_reports_id_fk": { 59 + "name": "report_frames_report_id_reports_id_fk", 60 + "tableFrom": "report_frames", 61 + "tableTo": "reports", 62 + "columnsFrom": [ 63 + "report_id" 64 + ], 65 + "columnsTo": [ 66 + "id" 67 + ], 68 + "onDelete": "cascade", 69 + "onUpdate": "no action" 70 + }, 71 + "report_frames_frame_hash_frames_hash_fk": { 72 + "name": "report_frames_frame_hash_frames_hash_fk", 73 + "tableFrom": "report_frames", 74 + "tableTo": "frames", 75 + "columnsFrom": [ 76 + "frame_hash" 77 + ], 78 + "columnsTo": [ 79 + "hash" 80 + ], 81 + "onDelete": "restrict", 82 + "onUpdate": "no action" 83 + } 84 + }, 85 + "compositePrimaryKeys": { 86 + "report_frames_report_id_frame_index_pk": { 87 + "columns": [ 88 + "report_id", 89 + "frame_index" 90 + ], 91 + "name": "report_frames_report_id_frame_index_pk" 92 + } 93 + }, 94 + "uniqueConstraints": {}, 95 + "checkConstraints": {} 96 + }, 97 + "reports": { 98 + "name": "reports", 99 + "columns": { 100 + "id": { 101 + "name": "id", 102 + "type": "text", 103 + "primaryKey": true, 104 + "notNull": true, 105 + "autoincrement": false 106 + }, 107 + "runtime": { 108 + "name": "runtime", 109 + "type": "text", 110 + "primaryKey": false, 111 + "notNull": true, 112 + "autoincrement": false 113 + }, 114 + "version": { 115 + "name": "version", 116 + "type": "text", 117 + "primaryKey": false, 118 + "notNull": true, 119 + "autoincrement": false 120 + }, 121 + "trace": { 122 + "name": "trace", 123 + "type": "text", 124 + "primaryKey": false, 125 + "notNull": true, 126 + "autoincrement": false 127 + }, 128 + "kind": { 129 + "name": "kind", 130 + "type": "text", 131 + "primaryKey": false, 132 + "notNull": true, 133 + "autoincrement": false 134 + }, 135 + "reason": { 136 + "name": "reason", 137 + "type": "text", 138 + "primaryKey": false, 139 + "notNull": true, 140 + "autoincrement": false 141 + }, 142 + "code": { 143 + "name": "code", 144 + "type": "text", 145 + "primaryKey": false, 146 + "notNull": true, 147 + "autoincrement": false 148 + }, 149 + "target": { 150 + "name": "target", 151 + "type": "text", 152 + "primaryKey": false, 153 + "notNull": true, 154 + "autoincrement": false 155 + }, 156 + "os": { 157 + "name": "os", 158 + "type": "text", 159 + "primaryKey": false, 160 + "notNull": true, 161 + "autoincrement": false 162 + }, 163 + "arch": { 164 + "name": "arch", 165 + "type": "text", 166 + "primaryKey": false, 167 + "notNull": true, 168 + "autoincrement": false 169 + }, 170 + "fault_address": { 171 + "name": "fault_address", 172 + "type": "text", 173 + "primaryKey": false, 174 + "notNull": true, 175 + "autoincrement": false 176 + }, 177 + "elapsed_ms": { 178 + "name": "elapsed_ms", 179 + "type": "integer", 180 + "primaryKey": false, 181 + "notNull": false, 182 + "autoincrement": false 183 + }, 184 + "peak_rss": { 185 + "name": "peak_rss", 186 + "type": "integer", 187 + "primaryKey": false, 188 + "notNull": false, 189 + "autoincrement": false 190 + }, 191 + "first_seen_at": { 192 + "name": "first_seen_at", 193 + "type": "text", 194 + "primaryKey": false, 195 + "notNull": true, 196 + "autoincrement": false 197 + }, 198 + "last_seen_at": { 199 + "name": "last_seen_at", 200 + "type": "text", 201 + "primaryKey": false, 202 + "notNull": true, 203 + "autoincrement": false 204 + }, 205 + "hit_count": { 206 + "name": "hit_count", 207 + "type": "integer", 208 + "primaryKey": false, 209 + "notNull": true, 210 + "autoincrement": false 211 + }, 212 + "expires_at": { 213 + "name": "expires_at", 214 + "type": "text", 215 + "primaryKey": false, 216 + "notNull": true, 217 + "autoincrement": false 218 + } 219 + }, 220 + "indexes": { 221 + "reports_trace_unique": { 222 + "name": "reports_trace_unique", 223 + "columns": [ 224 + "trace" 225 + ], 226 + "isUnique": true 227 + } 228 + }, 229 + "foreignKeys": {}, 230 + "compositePrimaryKeys": {}, 231 + "uniqueConstraints": {}, 232 + "checkConstraints": {} 233 + } 234 + }, 235 + "views": {}, 236 + "enums": {}, 237 + "_meta": { 238 + "schemas": {}, 239 + "tables": {}, 240 + "columns": {} 241 + }, 242 + "internal": { 243 + "indexes": {} 244 + } 245 + }
+7
docs/reports/migrations/meta/_journal.json
··· 8 8 "when": 1777160084343, 9 9 "tag": "0000_melodic_maginty", 10 10 "breakpoints": true 11 + }, 12 + { 13 + "idx": 1, 14 + "version": "6", 15 + "when": 1777165898584, 16 + "tag": "0001_fluffy_lockheed", 17 + "breakpoints": true 11 18 } 12 19 ] 13 20 }
+52 -22
docs/reports/src/reports.ts
··· 1 1 import { sha256 } from './helpers'; 2 - import { asc, eq } from 'drizzle-orm'; 3 2 import { drizzle } from 'drizzle-orm/d1'; 3 + import { asc, eq, sql } from 'drizzle-orm'; 4 4 import { generate as generateShortUuid } from 'short-uuid'; 5 5 import { frames as frameTable, reportFrames, reports, type CrashReport } from './schema'; 6 6 ··· 127 127 export async function createReport(db: ReportDb, report: CrashReport): Promise<string> { 128 128 const id = generateShortUuid(); 129 129 const trace = await reportTrace(report); 130 + 130 131 const now = new Date(); 131 132 const expires = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000); 132 133 133 - await db.insert(reports).values({ 134 - id, 135 - trace, 136 - hitCount: 1, 137 - runtime: report.runtime, 138 - version: report.version, 139 - kind: report.kind, 140 - reason: report.reason, 141 - code: report.code, 142 - target: report.target, 143 - os: report.os, 144 - arch: report.arch, 145 - faultAddress: report.addr, 146 - elapsedMs: report.elapsedMs, 147 - peakRss: report.peakRss, 148 - firstSeenAt: now.toISOString(), 149 - lastSeenAt: now.toISOString(), 150 - expiresAt: expires.toISOString(), 151 - }); 134 + const nowIso = now.toISOString(); 135 + const expiresIso = expires.toISOString(); 152 136 153 - await insertReportFrames(db, id, report.frames); 154 - return id; 137 + await db 138 + .insert(reports) 139 + .values({ 140 + id, 141 + trace, 142 + hitCount: 1, 143 + runtime: report.runtime, 144 + version: report.version, 145 + kind: report.kind, 146 + reason: report.reason, 147 + code: report.code, 148 + target: report.target, 149 + os: report.os, 150 + arch: report.arch, 151 + faultAddress: report.addr, 152 + elapsedMs: report.elapsedMs, 153 + peakRss: report.peakRss, 154 + firstSeenAt: nowIso, 155 + lastSeenAt: nowIso, 156 + expiresAt: expiresIso, 157 + }) 158 + .onConflictDoNothing({ target: reports.trace }); 159 + 160 + const [row] = await db 161 + .select({ 162 + id: reports.id, 163 + hitCount: reports.hitCount, 164 + }) 165 + .from(reports) 166 + .where(eq(reports.trace, trace)) 167 + .limit(1); 168 + 169 + if (!row) return id; 170 + if (row.id === id) { 171 + await insertReportFrames(db, id, report.frames); 172 + return id; 173 + } 174 + 175 + await db 176 + .update(reports) 177 + .set({ 178 + hitCount: sql`${reports.hitCount} + 1`, 179 + lastSeenAt: nowIso, 180 + expiresAt: expiresIso, 181 + }) 182 + .where(eq(reports.id, row.id)); 183 + 184 + return row.id; 155 185 }
+24 -20
docs/reports/src/schema.ts
··· 1 1 import { z } from 'zod'; 2 - import { integer, primaryKey, sqliteTable, text } from 'drizzle-orm/sqlite-core'; 2 + import { integer, primaryKey, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core'; 3 3 4 4 const boundedString = z 5 5 .string() ··· 27 27 }) 28 28 .strict(); 29 29 30 - export const reports = sqliteTable('reports', { 31 - id: text('id').primaryKey(), 32 - runtime: text('runtime').notNull(), 33 - version: text('version').notNull(), 34 - trace: text('trace').notNull(), 35 - kind: text('kind').notNull(), 36 - reason: text('reason').notNull(), 37 - code: text('code').notNull(), 38 - target: text('target').notNull(), 39 - os: text('os').notNull(), 40 - arch: text('arch').notNull(), 41 - faultAddress: text('fault_address').notNull(), 42 - elapsedMs: integer('elapsed_ms'), 43 - peakRss: integer('peak_rss'), 44 - firstSeenAt: text('first_seen_at').notNull(), 45 - lastSeenAt: text('last_seen_at').notNull(), 46 - hitCount: integer('hit_count').notNull(), 47 - expiresAt: text('expires_at').notNull(), 48 - }); 30 + export const reports = sqliteTable( 31 + 'reports', 32 + { 33 + id: text('id').primaryKey(), 34 + runtime: text('runtime').notNull(), 35 + version: text('version').notNull(), 36 + trace: text('trace').notNull(), 37 + kind: text('kind').notNull(), 38 + reason: text('reason').notNull(), 39 + code: text('code').notNull(), 40 + target: text('target').notNull(), 41 + os: text('os').notNull(), 42 + arch: text('arch').notNull(), 43 + faultAddress: text('fault_address').notNull(), 44 + elapsedMs: integer('elapsed_ms'), 45 + peakRss: integer('peak_rss'), 46 + firstSeenAt: text('first_seen_at').notNull(), 47 + lastSeenAt: text('last_seen_at').notNull(), 48 + hitCount: integer('hit_count').notNull(), 49 + expiresAt: text('expires_at').notNull(), 50 + }, 51 + table => [uniqueIndex('reports_trace_unique').on(table.trace)], 52 + ); 49 53 50 54 export const frames = sqliteTable('frames', { 51 55 hash: text('hash').primaryKey(),