@hasna/logs 0.3.26 → 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 (130) 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-gc0zvs88.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-997bkzr2.js +0 -15
  57. package/dist/index-pen6t0yc.js +0 -10794
  58. package/sdk/package.json +0 -27
  59. package/sdk/src/index.ts +0 -143
  60. package/sdk/src/types.ts +0 -56
  61. package/src/cli/entrypoints.test.ts +0 -63
  62. package/src/cli/index.ts +0 -471
  63. package/src/db/index.test.ts +0 -33
  64. package/src/db/index.ts +0 -189
  65. package/src/db/migrations/001_alert_rules.ts +0 -21
  66. package/src/db/migrations/002_issues.ts +0 -21
  67. package/src/db/migrations/003_retention.ts +0 -15
  68. package/src/db/migrations/004_page_auth.ts +0 -13
  69. package/src/db/pg-migrations.ts +0 -167
  70. package/src/index.ts +0 -1
  71. package/src/lib/alerts.test.ts +0 -67
  72. package/src/lib/alerts.ts +0 -117
  73. package/src/lib/browser-script.test.ts +0 -35
  74. package/src/lib/browser-script.ts +0 -31
  75. package/src/lib/compare.test.ts +0 -52
  76. package/src/lib/compare.ts +0 -85
  77. package/src/lib/count.test.ts +0 -44
  78. package/src/lib/count.ts +0 -55
  79. package/src/lib/diagnose.test.ts +0 -55
  80. package/src/lib/diagnose.ts +0 -91
  81. package/src/lib/export.test.ts +0 -66
  82. package/src/lib/export.ts +0 -65
  83. package/src/lib/github.ts +0 -38
  84. package/src/lib/health.test.ts +0 -48
  85. package/src/lib/health.ts +0 -51
  86. package/src/lib/ingest.test.ts +0 -57
  87. package/src/lib/ingest.ts +0 -78
  88. package/src/lib/issues.test.ts +0 -79
  89. package/src/lib/issues.ts +0 -70
  90. package/src/lib/jobs.test.ts +0 -69
  91. package/src/lib/jobs.ts +0 -63
  92. package/src/lib/lighthouse.ts +0 -65
  93. package/src/lib/package-meta.test.ts +0 -43
  94. package/src/lib/package-meta.ts +0 -80
  95. package/src/lib/page-auth.test.ts +0 -54
  96. package/src/lib/page-auth.ts +0 -48
  97. package/src/lib/parse-time.test.ts +0 -37
  98. package/src/lib/parse-time.ts +0 -14
  99. package/src/lib/perf.test.ts +0 -45
  100. package/src/lib/perf.ts +0 -46
  101. package/src/lib/projects.test.ts +0 -73
  102. package/src/lib/projects.ts +0 -69
  103. package/src/lib/query.test.ts +0 -104
  104. package/src/lib/query.ts +0 -84
  105. package/src/lib/retention.test.ts +0 -42
  106. package/src/lib/retention.ts +0 -62
  107. package/src/lib/rotate.test.ts +0 -37
  108. package/src/lib/rotate.ts +0 -27
  109. package/src/lib/scanner.ts +0 -131
  110. package/src/lib/scheduler.ts +0 -63
  111. package/src/lib/session-context.ts +0 -28
  112. package/src/lib/summarize.test.ts +0 -38
  113. package/src/lib/summarize.ts +0 -23
  114. package/src/mcp/http.test.ts +0 -92
  115. package/src/mcp/http.ts +0 -135
  116. package/src/mcp/index.test.ts +0 -27
  117. package/src/mcp/index.ts +0 -444
  118. package/src/server/index.ts +0 -61
  119. package/src/server/routes/alerts.ts +0 -32
  120. package/src/server/routes/issues.ts +0 -43
  121. package/src/server/routes/jobs.ts +0 -32
  122. package/src/server/routes/logs.ts +0 -113
  123. package/src/server/routes/perf.ts +0 -23
  124. package/src/server/routes/projects.ts +0 -67
  125. package/src/server/routes/stream.ts +0 -43
  126. package/src/server/server.test.ts +0 -194
  127. package/src/types/index.ts +0 -119
  128. package/tsconfig.json +0 -22
  129. /package/dashboard/{public → dist}/favicon.svg +0 -0
  130. /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
- })