@hasna/logs 0.3.25 → 0.3.27

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 (132) hide show
  1. package/README.md +33 -10
  2. package/dashboard/dist/assets/index-C0wZYq1m.js +53 -0
  3. package/dashboard/dist/assets/index-DGNrK5qb.css +1 -0
  4. package/dashboard/dist/index.html +14 -0
  5. package/dist/cli/index.js +8511 -177
  6. package/dist/count-bmj4r2zb.js +10 -0
  7. package/dist/{diagnose-e0w5rwbc.js → diagnose-3q5cy9ra.js} +2 -2
  8. package/dist/{export-c3eqjste.js → export-cngdb9fh.js} +1 -1
  9. package/dist/{http-zm3ph78w.js → http-r0xc3d2s.js} +79 -8
  10. package/dist/index-931pbyn5.js +141 -0
  11. package/dist/index-b5c72f1p.js +7 -0
  12. package/dist/{index-p1vgwwsz.js → index-bnr19y0h.js} +596 -37
  13. package/dist/{index-7w7v7hnr.js → index-by1pdzbr.js} +14 -5
  14. package/dist/{index-3dr7d80h.js → index-e1930v9b.js} +12 -8
  15. package/dist/{index-eh9bkbpa.js → index-e72k53yq.js} +10 -2
  16. package/dist/{index-edn08m6f.js → index-gcd14q2f.js} +9 -6
  17. package/dist/index-hq6kzaah.js +26 -0
  18. package/dist/index-j34f36wy.js +5672 -0
  19. package/dist/index-p4dbdzx4.js +1849 -0
  20. package/dist/{index-5qznfyah.js → index-q27bgpr1.js} +1086 -1646
  21. package/dist/index-t3x838zw.js +2583 -0
  22. package/dist/{index-ww5ggfv3.js → index-zkb3z95a.js} +12 -9
  23. package/dist/index.js +2982 -22
  24. package/dist/{jobs-ypmmc2ma.js → jobs-hsgyhfvm.js} +2 -1
  25. package/dist/mcp/index.js +1473 -4286
  26. package/dist/{query-7jwj05er.js → query-c5a43zx3.js} +3 -2
  27. package/dist/server/index.js +2944 -417
  28. package/dist/storage.js +50 -0
  29. package/package.json +27 -8
  30. package/biome.json +0 -13
  31. package/bun.lock +0 -376
  32. package/dashboard/README.md +0 -73
  33. package/dashboard/bun.lock +0 -526
  34. package/dashboard/eslint.config.js +0 -23
  35. package/dashboard/index.html +0 -13
  36. package/dashboard/package.json +0 -32
  37. package/dashboard/src/App.css +0 -184
  38. package/dashboard/src/App.tsx +0 -49
  39. package/dashboard/src/api.ts +0 -33
  40. package/dashboard/src/assets/hero.png +0 -0
  41. package/dashboard/src/assets/react.svg +0 -1
  42. package/dashboard/src/assets/vite.svg +0 -1
  43. package/dashboard/src/index.css +0 -111
  44. package/dashboard/src/main.tsx +0 -10
  45. package/dashboard/src/pages/Alerts.tsx +0 -69
  46. package/dashboard/src/pages/Issues.tsx +0 -50
  47. package/dashboard/src/pages/Perf.tsx +0 -75
  48. package/dashboard/src/pages/Projects.tsx +0 -67
  49. package/dashboard/src/pages/Summary.tsx +0 -67
  50. package/dashboard/src/pages/Tail.tsx +0 -65
  51. package/dashboard/tsconfig.app.json +0 -28
  52. package/dashboard/tsconfig.json +0 -7
  53. package/dashboard/tsconfig.node.json +0 -26
  54. package/dashboard/vite.config.ts +0 -14
  55. package/dist/count-x3n7qg3c.js +0 -9
  56. package/dist/index-5cj74qka.js +0 -10803
  57. package/dist/index-997bkzr2.js +0 -15
  58. package/dist/index-kezb178p.js +0 -1241
  59. package/dist/index-pen6t0yc.js +0 -10794
  60. package/sdk/package.json +0 -27
  61. package/sdk/src/index.ts +0 -143
  62. package/sdk/src/types.ts +0 -56
  63. package/src/cli/entrypoints.test.ts +0 -63
  64. package/src/cli/index.ts +0 -471
  65. package/src/db/index.test.ts +0 -33
  66. package/src/db/index.ts +0 -189
  67. package/src/db/migrations/001_alert_rules.ts +0 -21
  68. package/src/db/migrations/002_issues.ts +0 -21
  69. package/src/db/migrations/003_retention.ts +0 -15
  70. package/src/db/migrations/004_page_auth.ts +0 -13
  71. package/src/db/pg-migrations.ts +0 -167
  72. package/src/index.ts +0 -1
  73. package/src/lib/alerts.test.ts +0 -67
  74. package/src/lib/alerts.ts +0 -117
  75. package/src/lib/browser-script.test.ts +0 -35
  76. package/src/lib/browser-script.ts +0 -31
  77. package/src/lib/compare.test.ts +0 -52
  78. package/src/lib/compare.ts +0 -85
  79. package/src/lib/count.test.ts +0 -44
  80. package/src/lib/count.ts +0 -55
  81. package/src/lib/diagnose.test.ts +0 -55
  82. package/src/lib/diagnose.ts +0 -91
  83. package/src/lib/export.test.ts +0 -66
  84. package/src/lib/export.ts +0 -65
  85. package/src/lib/github.ts +0 -38
  86. package/src/lib/health.test.ts +0 -48
  87. package/src/lib/health.ts +0 -51
  88. package/src/lib/ingest.test.ts +0 -57
  89. package/src/lib/ingest.ts +0 -78
  90. package/src/lib/issues.test.ts +0 -79
  91. package/src/lib/issues.ts +0 -70
  92. package/src/lib/jobs.test.ts +0 -69
  93. package/src/lib/jobs.ts +0 -63
  94. package/src/lib/lighthouse.ts +0 -65
  95. package/src/lib/package-meta.test.ts +0 -43
  96. package/src/lib/package-meta.ts +0 -80
  97. package/src/lib/page-auth.test.ts +0 -54
  98. package/src/lib/page-auth.ts +0 -48
  99. package/src/lib/parse-time.test.ts +0 -37
  100. package/src/lib/parse-time.ts +0 -14
  101. package/src/lib/perf.test.ts +0 -45
  102. package/src/lib/perf.ts +0 -46
  103. package/src/lib/projects.test.ts +0 -73
  104. package/src/lib/projects.ts +0 -69
  105. package/src/lib/query.test.ts +0 -104
  106. package/src/lib/query.ts +0 -84
  107. package/src/lib/retention.test.ts +0 -42
  108. package/src/lib/retention.ts +0 -62
  109. package/src/lib/rotate.test.ts +0 -37
  110. package/src/lib/rotate.ts +0 -27
  111. package/src/lib/scanner.ts +0 -131
  112. package/src/lib/scheduler.ts +0 -63
  113. package/src/lib/session-context.ts +0 -28
  114. package/src/lib/summarize.test.ts +0 -38
  115. package/src/lib/summarize.ts +0 -23
  116. package/src/mcp/http.test.ts +0 -92
  117. package/src/mcp/http.ts +0 -135
  118. package/src/mcp/index.test.ts +0 -27
  119. package/src/mcp/index.ts +0 -444
  120. package/src/server/index.ts +0 -61
  121. package/src/server/routes/alerts.ts +0 -32
  122. package/src/server/routes/issues.ts +0 -43
  123. package/src/server/routes/jobs.ts +0 -32
  124. package/src/server/routes/logs.ts +0 -113
  125. package/src/server/routes/perf.ts +0 -23
  126. package/src/server/routes/projects.ts +0 -67
  127. package/src/server/routes/stream.ts +0 -43
  128. package/src/server/server.test.ts +0 -194
  129. package/src/types/index.ts +0 -119
  130. package/tsconfig.json +0 -22
  131. /package/dashboard/{public → dist}/favicon.svg +0 -0
  132. /package/dashboard/{public → dist}/icons.svg +0 -0
package/sdk/package.json DELETED
@@ -1,27 +0,0 @@
1
- {
2
- "name": "@hasna/logs-sdk",
3
- "version": "0.1.0",
4
- "description": "Zero-dependency fetch client for @hasna/logs \u2014 push logs, query, browse issues, perf snapshots",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
8
- "exports": {
9
- ".": "./dist/index.js",
10
- "./browser": "./dist/index.js"
11
- },
12
- "publishConfig": {
13
- "access": "public",
14
- "registry": "https://registry.npmjs.org/"
15
- },
16
- "scripts": {
17
- "build": "bun build src/index.ts --outdir dist --target browser"
18
- },
19
- "keywords": [
20
- "logs",
21
- "monitoring",
22
- "sdk",
23
- "ai-agents"
24
- ],
25
- "author": "Andrei Hasna <andrei@hasna.com>",
26
- "license": "Apache-2.0"
27
- }
package/sdk/src/index.ts DELETED
@@ -1,143 +0,0 @@
1
- import type { LogEntry, LogLevel, LogQuery, LogRow, LogSummary, Page, PerformanceSnapshot, Project, ScanJob } from "./types.ts"
2
-
3
- export type { LogEntry, LogLevel, LogQuery, LogRow, LogSummary, Page, PerformanceSnapshot, Project, ScanJob }
4
-
5
- export interface LogsClientOptions {
6
- url?: string
7
- projectId?: string
8
- apiKey?: string
9
- }
10
-
11
- const DEFAULT_URL = "http://localhost:3460"
12
-
13
- export class LogsClient {
14
- private url: string
15
- private projectId?: string
16
- private headers: Record<string, string>
17
-
18
- constructor(opts: LogsClientOptions = {}) {
19
- this.url = (opts.url ?? DEFAULT_URL).replace(/\/$/, "")
20
- this.projectId = opts.projectId
21
- this.headers = { "Content-Type": "application/json" }
22
- if (opts.apiKey) this.headers["Authorization"] = `Bearer ${opts.apiKey}`
23
- }
24
-
25
- async push(entry: LogEntry): Promise<LogRow> {
26
- const res = await fetch(`${this.url}/api/logs`, {
27
- method: "POST",
28
- headers: this.headers,
29
- body: JSON.stringify({ project_id: this.projectId, ...entry }),
30
- })
31
- return res.json() as Promise<LogRow>
32
- }
33
-
34
- async pushBatch(entries: LogEntry[]): Promise<{ inserted: number }> {
35
- const res = await fetch(`${this.url}/api/logs`, {
36
- method: "POST",
37
- headers: this.headers,
38
- body: JSON.stringify(entries.map(e => ({ project_id: this.projectId, ...e }))),
39
- })
40
- return res.json() as Promise<{ inserted: number }>
41
- }
42
-
43
- async search(query: LogQuery = {}): Promise<LogRow[]> {
44
- const params = new URLSearchParams()
45
- if (query.project_id ?? this.projectId) params.set("project_id", query.project_id ?? this.projectId!)
46
- if (query.page_id) params.set("page_id", query.page_id)
47
- if (query.level) params.set("level", Array.isArray(query.level) ? query.level.join(",") : query.level)
48
- if (query.service) params.set("service", query.service)
49
- if (query.since) params.set("since", query.since)
50
- if (query.until) params.set("until", query.until)
51
- if (query.text) params.set("text", query.text)
52
- if (query.limit) params.set("limit", String(query.limit))
53
- if (query.offset) params.set("offset", String(query.offset))
54
- if (query.fields) params.set("fields", query.fields.join(","))
55
- const res = await fetch(`${this.url}/api/logs?${params}`, { headers: this.headers })
56
- return res.json() as Promise<LogRow[]>
57
- }
58
-
59
- async tail(projectId?: string, n = 50): Promise<LogRow[]> {
60
- const params = new URLSearchParams({ n: String(n) })
61
- const pid = projectId ?? this.projectId
62
- if (pid) params.set("project_id", pid)
63
- const res = await fetch(`${this.url}/api/logs/tail?${params}`, { headers: this.headers })
64
- return res.json() as Promise<LogRow[]>
65
- }
66
-
67
- async summary(projectId?: string, since?: string): Promise<LogSummary[]> {
68
- const params = new URLSearchParams()
69
- const pid = projectId ?? this.projectId
70
- if (pid) params.set("project_id", pid)
71
- if (since) params.set("since", since)
72
- const res = await fetch(`${this.url}/api/logs/summary?${params}`, { headers: this.headers })
73
- return res.json() as Promise<LogSummary[]>
74
- }
75
-
76
- async context(traceId: string): Promise<LogRow[]> {
77
- const res = await fetch(`${this.url}/api/logs/${traceId}/context`, { headers: this.headers })
78
- return res.json() as Promise<LogRow[]>
79
- }
80
-
81
- async registerProject(name: string, githubRepo?: string, baseUrl?: string): Promise<Project> {
82
- const res = await fetch(`${this.url}/api/projects`, {
83
- method: "POST",
84
- headers: this.headers,
85
- body: JSON.stringify({ name, github_repo: githubRepo, base_url: baseUrl }),
86
- })
87
- return res.json() as Promise<Project>
88
- }
89
-
90
- async registerPage(projectId: string, url: string, path?: string, name?: string): Promise<Page> {
91
- const res = await fetch(`${this.url}/api/projects/${projectId}/pages`, {
92
- method: "POST",
93
- headers: this.headers,
94
- body: JSON.stringify({ url, path, name }),
95
- })
96
- return res.json() as Promise<Page>
97
- }
98
-
99
- async createScanJob(projectId: string, schedule: string, pageId?: string): Promise<ScanJob> {
100
- const res = await fetch(`${this.url}/api/jobs`, {
101
- method: "POST",
102
- headers: this.headers,
103
- body: JSON.stringify({ project_id: projectId, schedule, page_id: pageId }),
104
- })
105
- return res.json() as Promise<ScanJob>
106
- }
107
-
108
- async perfSnapshot(projectId: string, pageId?: string): Promise<PerformanceSnapshot | null> {
109
- const params = new URLSearchParams({ project_id: projectId })
110
- if (pageId) params.set("page_id", pageId)
111
- const res = await fetch(`${this.url}/api/perf?${params}`, { headers: this.headers })
112
- return res.json() as Promise<PerformanceSnapshot | null>
113
- }
114
-
115
- async perfTrend(projectId: string, pageId?: string, since?: string, limit?: number): Promise<PerformanceSnapshot[]> {
116
- const params = new URLSearchParams({ project_id: projectId })
117
- if (pageId) params.set("page_id", pageId)
118
- if (since) params.set("since", since)
119
- if (limit) params.set("limit", String(limit))
120
- const res = await fetch(`${this.url}/api/perf/trend?${params}`, { headers: this.headers })
121
- return res.json() as Promise<PerformanceSnapshot[]>
122
- }
123
- }
124
-
125
- // Browser auto-capture init
126
- export function initLogs(opts: { projectId: string; url?: string }): void {
127
- if (typeof window === "undefined") return
128
- const serverUrl = (opts.url ?? DEFAULT_URL).replace(/\/$/, "")
129
- const client = new LogsClient({ url: serverUrl, projectId: opts.projectId })
130
- const q: LogEntry[] = []
131
- const flush = () => { if (q.length) client.pushBatch(q.splice(0)).catch(() => {}) }
132
- setInterval(flush, 2000)
133
-
134
- const _ce = console.error.bind(console)
135
- console.error = (...args: unknown[]) => { _ce(...args); q.push({ level: "error", message: args.map(String).join(" "), source: "script", url: location.href }) }
136
-
137
- const _cw = console.warn.bind(console)
138
- console.warn = (...args: unknown[]) => { _cw(...args); q.push({ level: "warn", message: args.map(String).join(" "), source: "script", url: location.href }) }
139
-
140
- window.addEventListener("error", (e) => { q.push({ level: "error", message: e.message, stack_trace: e.error?.stack, source: "script", url: location.href }) })
141
- window.addEventListener("unhandledrejection", (e) => { q.push({ level: "error", message: `Unhandled: ${e.reason?.message ?? e.reason}`, stack_trace: e.reason?.stack, source: "script", url: location.href }) })
142
- window.addEventListener("beforeunload", flush)
143
- }
package/sdk/src/types.ts DELETED
@@ -1,56 +0,0 @@
1
- export type LogLevel = "debug" | "info" | "warn" | "error" | "fatal"
2
- export type LogSource = "sdk" | "script" | "scanner"
3
-
4
- export interface LogEntry {
5
- level: LogLevel
6
- message: string
7
- project_id?: string
8
- page_id?: string
9
- source?: LogSource
10
- service?: string
11
- trace_id?: string
12
- session_id?: string
13
- agent?: string
14
- url?: string
15
- stack_trace?: string
16
- metadata?: Record<string, unknown>
17
- }
18
-
19
- export interface LogRow {
20
- id: string; timestamp: string; project_id: string | null; page_id: string | null
21
- level: LogLevel; source: LogSource; service: string | null; message: string
22
- trace_id: string | null; session_id: string | null; agent: string | null
23
- url: string | null; stack_trace: string | null; metadata: string | null
24
- }
25
-
26
- export interface Project {
27
- id: string; name: string; github_repo: string | null; base_url: string | null
28
- description: string | null; created_at: string
29
- }
30
-
31
- export interface Page {
32
- id: string; project_id: string; url: string; path: string
33
- name: string | null; last_scanned_at: string | null; created_at: string
34
- }
35
-
36
- export interface ScanJob {
37
- id: string; project_id: string; page_id: string | null
38
- schedule: string; enabled: number; last_run_at: string | null; created_at: string
39
- }
40
-
41
- export interface PerformanceSnapshot {
42
- id: string; timestamp: string; project_id: string; page_id: string | null
43
- url: string; lcp: number | null; fcp: number | null; cls: number | null
44
- tti: number | null; ttfb: number | null; score: number | null; raw_audit: string | null
45
- }
46
-
47
- export interface LogQuery {
48
- project_id?: string; page_id?: string; level?: LogLevel | LogLevel[]
49
- service?: string; since?: string; until?: string; text?: string
50
- trace_id?: string; limit?: number; offset?: number; fields?: string[]
51
- }
52
-
53
- export interface LogSummary {
54
- project_id: string | null; service: string | null; page_id: string | null
55
- level: LogLevel; count: number; latest: string
56
- }
@@ -1,63 +0,0 @@
1
- import { expect, test } from "bun:test"
2
- import { readFileSync } from "node:fs"
3
- import { fileURLToPath } from "node:url"
4
-
5
- const packageJson = JSON.parse(
6
- readFileSync(new URL("../../package.json", import.meta.url), "utf8"),
7
- ) as { version: string }
8
-
9
- async function runEntrypoint(entryRelativePath: string, args: string[]) {
10
- const entry = fileURLToPath(new URL(entryRelativePath, import.meta.url))
11
- const proc = Bun.spawn(["bun", entry, ...args], {
12
- env: {
13
- ...process.env,
14
- LOGS_PORT: "0",
15
- },
16
- stdout: "pipe",
17
- stderr: "pipe",
18
- })
19
-
20
- const timeout = new Promise<never>((_, reject) => {
21
- const timer = setTimeout(() => {
22
- proc.kill()
23
- reject(new Error(`Timed out running ${entryRelativePath} ${args.join(" ")}`.trim()))
24
- }, 2000)
25
-
26
- proc.exited.finally(() => clearTimeout(timer))
27
- })
28
-
29
- const exitCode = await Promise.race([proc.exited, timeout])
30
- const stdout = await new Response(proc.stdout).text()
31
- const stderr = await new Response(proc.stderr).text()
32
-
33
- return { exitCode, stdout, stderr }
34
- }
35
-
36
- test("logs --version matches package.json", async () => {
37
- const result = await runEntrypoint("./index.ts", ["--version"])
38
-
39
- expect(result.exitCode).toBe(0)
40
- expect(result.stdout.trim()).toBe(packageJson.version)
41
- expect(result.stderr.trim()).toBe("")
42
- })
43
-
44
- test("logs-mcp --help prints usage and exits without starting stdio transport", async () => {
45
- const result = await runEntrypoint("../mcp/index.ts", ["--help"])
46
-
47
- expect(result.exitCode).toBe(0)
48
- expect(result.stdout).toContain("Usage: logs-mcp [options]")
49
- expect(result.stdout).toContain("Start the @hasna/logs MCP server (stdio by default).")
50
- expect(result.stdout).not.toContain("Listening")
51
- expect(result.stderr.trim()).toBe("")
52
- })
53
-
54
- test("logs-serve --help prints usage and exits without starting the server", async () => {
55
- const result = await runEntrypoint("../server/index.ts", ["--help"])
56
-
57
- expect(result.exitCode).toBe(0)
58
- expect(result.stdout).toContain("Usage: logs-serve [options]")
59
- expect(result.stdout).toContain("Start the @hasna/logs REST API server.")
60
- expect(result.stdout).not.toContain("server running")
61
- expect(result.stdout).not.toContain("Scheduler started")
62
- expect(result.stderr.trim()).toBe("")
63
- })