@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.
- package/README.md +33 -10
- package/dashboard/dist/assets/index-C0wZYq1m.js +53 -0
- package/dashboard/dist/assets/index-DGNrK5qb.css +1 -0
- package/dashboard/dist/index.html +14 -0
- package/dist/cli/index.js +8511 -177
- package/dist/count-bmj4r2zb.js +10 -0
- package/dist/{diagnose-e0w5rwbc.js → diagnose-3q5cy9ra.js} +2 -2
- package/dist/{export-c3eqjste.js → export-cngdb9fh.js} +1 -1
- package/dist/{http-zm3ph78w.js → http-r0xc3d2s.js} +79 -8
- package/dist/index-931pbyn5.js +141 -0
- package/dist/index-b5c72f1p.js +7 -0
- package/dist/{index-p1vgwwsz.js → index-bnr19y0h.js} +596 -37
- package/dist/{index-7w7v7hnr.js → index-by1pdzbr.js} +14 -5
- package/dist/{index-3dr7d80h.js → index-e1930v9b.js} +12 -8
- package/dist/{index-eh9bkbpa.js → index-e72k53yq.js} +10 -2
- package/dist/{index-edn08m6f.js → index-gcd14q2f.js} +9 -6
- package/dist/index-hq6kzaah.js +26 -0
- package/dist/index-j34f36wy.js +5672 -0
- package/dist/index-p4dbdzx4.js +1849 -0
- package/dist/{index-5qznfyah.js → index-q27bgpr1.js} +1086 -1646
- package/dist/index-t3x838zw.js +2583 -0
- package/dist/{index-ww5ggfv3.js → index-zkb3z95a.js} +12 -9
- package/dist/index.js +2982 -22
- package/dist/{jobs-ypmmc2ma.js → jobs-hsgyhfvm.js} +2 -1
- package/dist/mcp/index.js +1473 -4286
- package/dist/{query-7jwj05er.js → query-c5a43zx3.js} +3 -2
- package/dist/server/index.js +2944 -417
- package/dist/storage.js +50 -0
- package/package.json +27 -8
- package/biome.json +0 -13
- package/bun.lock +0 -376
- package/dashboard/README.md +0 -73
- package/dashboard/bun.lock +0 -526
- package/dashboard/eslint.config.js +0 -23
- package/dashboard/index.html +0 -13
- package/dashboard/package.json +0 -32
- package/dashboard/src/App.css +0 -184
- package/dashboard/src/App.tsx +0 -49
- package/dashboard/src/api.ts +0 -33
- package/dashboard/src/assets/hero.png +0 -0
- package/dashboard/src/assets/react.svg +0 -1
- package/dashboard/src/assets/vite.svg +0 -1
- package/dashboard/src/index.css +0 -111
- package/dashboard/src/main.tsx +0 -10
- package/dashboard/src/pages/Alerts.tsx +0 -69
- package/dashboard/src/pages/Issues.tsx +0 -50
- package/dashboard/src/pages/Perf.tsx +0 -75
- package/dashboard/src/pages/Projects.tsx +0 -67
- package/dashboard/src/pages/Summary.tsx +0 -67
- package/dashboard/src/pages/Tail.tsx +0 -65
- package/dashboard/tsconfig.app.json +0 -28
- package/dashboard/tsconfig.json +0 -7
- package/dashboard/tsconfig.node.json +0 -26
- package/dashboard/vite.config.ts +0 -14
- package/dist/count-x3n7qg3c.js +0 -9
- package/dist/index-5cj74qka.js +0 -10803
- package/dist/index-997bkzr2.js +0 -15
- package/dist/index-kezb178p.js +0 -1241
- package/dist/index-pen6t0yc.js +0 -10794
- package/sdk/package.json +0 -27
- package/sdk/src/index.ts +0 -143
- package/sdk/src/types.ts +0 -56
- package/src/cli/entrypoints.test.ts +0 -63
- package/src/cli/index.ts +0 -471
- package/src/db/index.test.ts +0 -33
- package/src/db/index.ts +0 -189
- package/src/db/migrations/001_alert_rules.ts +0 -21
- package/src/db/migrations/002_issues.ts +0 -21
- package/src/db/migrations/003_retention.ts +0 -15
- package/src/db/migrations/004_page_auth.ts +0 -13
- package/src/db/pg-migrations.ts +0 -167
- package/src/index.ts +0 -1
- package/src/lib/alerts.test.ts +0 -67
- package/src/lib/alerts.ts +0 -117
- package/src/lib/browser-script.test.ts +0 -35
- package/src/lib/browser-script.ts +0 -31
- package/src/lib/compare.test.ts +0 -52
- package/src/lib/compare.ts +0 -85
- package/src/lib/count.test.ts +0 -44
- package/src/lib/count.ts +0 -55
- package/src/lib/diagnose.test.ts +0 -55
- package/src/lib/diagnose.ts +0 -91
- package/src/lib/export.test.ts +0 -66
- package/src/lib/export.ts +0 -65
- package/src/lib/github.ts +0 -38
- package/src/lib/health.test.ts +0 -48
- package/src/lib/health.ts +0 -51
- package/src/lib/ingest.test.ts +0 -57
- package/src/lib/ingest.ts +0 -78
- package/src/lib/issues.test.ts +0 -79
- package/src/lib/issues.ts +0 -70
- package/src/lib/jobs.test.ts +0 -69
- package/src/lib/jobs.ts +0 -63
- package/src/lib/lighthouse.ts +0 -65
- package/src/lib/package-meta.test.ts +0 -43
- package/src/lib/package-meta.ts +0 -80
- package/src/lib/page-auth.test.ts +0 -54
- package/src/lib/page-auth.ts +0 -48
- package/src/lib/parse-time.test.ts +0 -37
- package/src/lib/parse-time.ts +0 -14
- package/src/lib/perf.test.ts +0 -45
- package/src/lib/perf.ts +0 -46
- package/src/lib/projects.test.ts +0 -73
- package/src/lib/projects.ts +0 -69
- package/src/lib/query.test.ts +0 -104
- package/src/lib/query.ts +0 -84
- package/src/lib/retention.test.ts +0 -42
- package/src/lib/retention.ts +0 -62
- package/src/lib/rotate.test.ts +0 -37
- package/src/lib/rotate.ts +0 -27
- package/src/lib/scanner.ts +0 -131
- package/src/lib/scheduler.ts +0 -63
- package/src/lib/session-context.ts +0 -28
- package/src/lib/summarize.test.ts +0 -38
- package/src/lib/summarize.ts +0 -23
- package/src/mcp/http.test.ts +0 -92
- package/src/mcp/http.ts +0 -135
- package/src/mcp/index.test.ts +0 -27
- package/src/mcp/index.ts +0 -444
- package/src/server/index.ts +0 -61
- package/src/server/routes/alerts.ts +0 -32
- package/src/server/routes/issues.ts +0 -43
- package/src/server/routes/jobs.ts +0 -32
- package/src/server/routes/logs.ts +0 -113
- package/src/server/routes/perf.ts +0 -23
- package/src/server/routes/projects.ts +0 -67
- package/src/server/routes/stream.ts +0 -43
- package/src/server/server.test.ts +0 -194
- package/src/types/index.ts +0 -119
- package/tsconfig.json +0 -22
- /package/dashboard/{public → dist}/favicon.svg +0 -0
- /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
|
-
})
|