effect-start 0.26.0 → 0.27.0

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 (58) hide show
  1. package/package.json +4 -2
  2. package/src/Entity.ts +6 -6
  3. package/src/FileRouterCodegen.ts +4 -4
  4. package/src/FileSystem.ts +4 -8
  5. package/src/RouteHook.ts +1 -1
  6. package/src/RouteSse.ts +3 -3
  7. package/src/SqlIntrospect.ts +2 -2
  8. package/src/Start.ts +102 -2
  9. package/src/Values.ts +11 -0
  10. package/src/bun/BunRoute.ts +1 -1
  11. package/src/bun/BunRuntime.ts +5 -5
  12. package/src/hyper/HyperHtml.ts +11 -7
  13. package/src/hyper/jsx.d.ts +1 -1
  14. package/src/lint/plugin.js +174 -4
  15. package/src/sql/SqlClient.ts +355 -0
  16. package/src/sql/bun/index.ts +117 -50
  17. package/src/sql/index.ts +1 -1
  18. package/src/sql/libsql/index.ts +91 -77
  19. package/src/sql/libsql/libsql.d.ts +4 -1
  20. package/src/sql/mssql/index.ts +141 -108
  21. package/src/sql/mssql/mssql.d.ts +1 -0
  22. package/src/testing/TestLogger.ts +4 -4
  23. package/src/x/tailwind/compile.ts +6 -14
  24. package/src/console/Console.ts +0 -42
  25. package/src/console/ConsoleErrors.ts +0 -213
  26. package/src/console/ConsoleLogger.ts +0 -56
  27. package/src/console/ConsoleMetrics.ts +0 -72
  28. package/src/console/ConsoleProcess.ts +0 -59
  29. package/src/console/ConsoleStore.ts +0 -187
  30. package/src/console/ConsoleTracer.ts +0 -107
  31. package/src/console/Simulation.ts +0 -814
  32. package/src/console/console.html +0 -340
  33. package/src/console/index.ts +0 -3
  34. package/src/console/routes/errors/route.tsx +0 -97
  35. package/src/console/routes/fiberDetail.tsx +0 -54
  36. package/src/console/routes/fibers/route.tsx +0 -45
  37. package/src/console/routes/git/route.tsx +0 -64
  38. package/src/console/routes/layout.tsx +0 -4
  39. package/src/console/routes/logs/route.tsx +0 -77
  40. package/src/console/routes/metrics/route.tsx +0 -36
  41. package/src/console/routes/route.tsx +0 -8
  42. package/src/console/routes/routes/route.tsx +0 -30
  43. package/src/console/routes/services/route.tsx +0 -21
  44. package/src/console/routes/system/route.tsx +0 -43
  45. package/src/console/routes/traceDetail.tsx +0 -22
  46. package/src/console/routes/traces/route.tsx +0 -81
  47. package/src/console/routes/tree.ts +0 -30
  48. package/src/console/ui/Errors.tsx +0 -76
  49. package/src/console/ui/Fibers.tsx +0 -321
  50. package/src/console/ui/Git.tsx +0 -182
  51. package/src/console/ui/Logs.tsx +0 -46
  52. package/src/console/ui/Metrics.tsx +0 -78
  53. package/src/console/ui/Routes.tsx +0 -125
  54. package/src/console/ui/Services.tsx +0 -273
  55. package/src/console/ui/Shell.tsx +0 -62
  56. package/src/console/ui/System.tsx +0 -131
  57. package/src/console/ui/Traces.tsx +0 -426
  58. package/src/sql/Sql.ts +0 -51
@@ -1,273 +0,0 @@
1
- import * as Option from "effect/Option"
2
-
3
- const EffectTypeIds: Record<symbol, string> = {
4
- [Symbol.for("effect/Ref")]: "Ref",
5
- [Symbol.for("effect/SynchronizedRef")]: "SynchronizedRef",
6
- [Symbol.for("effect/QueueDequeue")]: "Dequeue",
7
- [Symbol.for("effect/QueueEnqueue")]: "Enqueue",
8
- [Symbol.for("effect/Pool")]: "Pool",
9
- [Symbol.for("effect/Deferred")]: "Deferred",
10
- [Symbol.for("effect/FiberRef")]: "FiberRef",
11
- [Symbol.for("effect/Scope")]: "Scope",
12
- [Symbol.for("effect/Tracer")]: "Tracer",
13
- [Symbol.for("effect/Request/Cache")]: "RequestCache",
14
- [Symbol.for("effect/Logger")]: "Logger",
15
- [Symbol.for("effect/Supervisor")]: "Supervisor",
16
- [Symbol.for("effect/Clock")]: "Clock",
17
- [Symbol.for("effect/Random")]: "Random",
18
- [Symbol.for("effect/KeyValueStore")]: "KeyValueStore",
19
- [Symbol.for("effect/RateLimiter")]: "RateLimiter",
20
- }
21
-
22
- function detectEffectType(value: unknown): string | undefined {
23
- if (value === null || value === undefined || typeof value !== "object") return undefined
24
- if ("publish" in value && "subscribe" in value && "offer" in value) return "PubSub"
25
- for (const sym of Object.getOwnPropertySymbols(value)) {
26
- const name = EffectTypeIds[sym]
27
- if (name) return name
28
- }
29
- return undefined
30
- }
31
-
32
- function inspectEffectValue(type: string, value: any): Record<string, unknown> {
33
- const info: Record<string, unknown> = { _type: type }
34
- try {
35
- switch (type) {
36
- case "PubSub": {
37
- if (typeof value.capacity === "function") info.capacity = value.capacity()
38
- if (typeof value.isActive === "function") info.active = value.isActive()
39
- if (typeof value.unsafeSize === "function") {
40
- const size = value.unsafeSize()
41
- info.size = Option.isSome(size) ? size.value : "shutdown"
42
- }
43
- if (value.pubsub && typeof value.pubsub.subscriberCount === "number") {
44
- info.subscribers = value.pubsub.subscriberCount
45
- }
46
- break
47
- }
48
- case "Enqueue":
49
- case "Dequeue": {
50
- if (typeof value.capacity === "function") info.capacity = value.capacity()
51
- if (typeof value.isActive === "function") info.active = value.isActive()
52
- if (typeof value.unsafeSize === "function") {
53
- const size = value.unsafeSize()
54
- info.size = Option.isSome(size) ? size.value : "shutdown"
55
- }
56
- break
57
- }
58
- case "Ref":
59
- case "SynchronizedRef": {
60
- if (value.ref && "current" in value.ref) {
61
- const current = value.ref.current
62
- info.value = safeSerialize(current)
63
- }
64
- break
65
- }
66
- case "Pool": {
67
- if (typeof value.minSize === "number") info.minSize = value.minSize
68
- if (typeof value.maxSize === "number") info.maxSize = value.maxSize
69
- if (typeof value.concurrency === "number") info.concurrency = value.concurrency
70
- if (value.items instanceof Set) info.items = value.items.size
71
- if (value.available instanceof Set) info.available = value.available.size
72
- if (value.invalidated instanceof Set) info.invalidated = value.invalidated.size
73
- if (typeof value.waiters === "number") info.waiters = value.waiters
74
- break
75
- }
76
- case "FiberRef": {
77
- if ("initial" in value) info.initial = safeSerialize(value.initial)
78
- break
79
- }
80
- case "Deferred": {
81
- if ("state" in value && value.state) {
82
- const state = value.state
83
- if (typeof state === "object" && "_tag" in state) {
84
- info.status = state._tag
85
- }
86
- }
87
- break
88
- }
89
- }
90
- } catch {
91
- // ignore introspection errors
92
- }
93
- return info
94
- }
95
-
96
- function safeSerialize(value: unknown): unknown {
97
- if (value === null || value === undefined) return value
98
- if (typeof value === "bigint") return `${value}n`
99
- if (typeof value === "function") return "<function>"
100
- if (typeof value === "symbol") return value.toString()
101
- if (typeof value !== "object") return value
102
- if (detectEffectType(value)) return `<${detectEffectType(value)}>`
103
- if (Array.isArray(value)) return value.map(safeSerialize)
104
- const proto = Object.getPrototypeOf(value)
105
- if (proto !== null && proto !== Object.prototype)
106
- return `<${proto.constructor?.name ?? "object"}>`
107
- const out: Record<string, unknown> = {}
108
- for (const [k, v] of Object.entries(value)) {
109
- if (typeof v === "function") continue
110
- out[k] = safeSerialize(v)
111
- }
112
- return out
113
- }
114
-
115
- export interface ServiceEntry {
116
- readonly key: string
117
- readonly kind: string
118
- readonly display: string
119
- readonly type: "config" | "value" | "effect"
120
- }
121
-
122
- function isJsonPrimitive(value: unknown): boolean {
123
- if (value === null) return true
124
- const t = typeof value
125
- return t === "string" || t === "number" || t === "boolean" || t === "bigint"
126
- }
127
-
128
- function isPlainJson(value: unknown): boolean {
129
- if (isJsonPrimitive(value)) return true
130
- if (Array.isArray(value)) return value.every(isPlainJson)
131
- if (typeof value === "object" && value !== null) {
132
- const proto = Object.getPrototypeOf(value)
133
- if (proto !== null && proto !== Object.prototype) return false
134
- return Object.values(value).every(isPlainJson)
135
- }
136
- return false
137
- }
138
-
139
- function jsonReplacer(_key: string, v: unknown): unknown {
140
- if (typeof v === "bigint") return `${v}n`
141
- return v
142
- }
143
-
144
- function collectDisplayValues(obj: unknown, prefix: string, out: Record<string, unknown>): void {
145
- if (typeof obj === "function") return
146
- if (isJsonPrimitive(obj) || Array.isArray(obj)) {
147
- out[prefix] = safeSerialize(obj)
148
- return
149
- }
150
- if (typeof obj === "object" && obj !== null) {
151
- const et = detectEffectType(obj)
152
- if (et) {
153
- out[prefix || et] = inspectEffectValue(et, obj)
154
- return
155
- }
156
- for (const [k, v] of Object.entries(obj)) {
157
- if (typeof v === "function") continue
158
- const path = prefix ? `${prefix}.${k}` : k
159
- if (isPlainJson(v)) {
160
- out[path] = safeSerialize(v)
161
- } else if (typeof v === "object" && v !== null) {
162
- collectDisplayValues(v, path, out)
163
- }
164
- }
165
- }
166
- }
167
-
168
- function kindColor(kind: string): { bg: string; fg: string } {
169
- if (kind === "config") return { bg: "#2d1f0e", fg: "#fbbf24" }
170
- if (kind === "effect") return { bg: "#2d1a3e", fg: "#c084fc" }
171
- if (kind === "empty") return { bg: "#1f2937", fg: "#64748b" }
172
- if (kind === "function") return { bg: "#1a2e1a", fg: "#4ade80" }
173
- return { bg: "#1e3a5f", fg: "#60a5fa" }
174
- }
175
-
176
- function ServiceRow({ entry }: { entry: ServiceEntry }) {
177
- const colors = kindColor(entry.type)
178
- return (
179
- <details class="tl-row">
180
- <summary class="tl-summary tl-cols">
181
- <span class="tl-cell tl-cell-status">
182
- <span
183
- style={`width:8px;height:8px;border-radius:50%;background:${colors.fg};display:block`}
184
- />
185
- </span>
186
- <span class="tl-cell tl-cell-name">{entry.key}</span>
187
- <span class="tl-cell tl-cell-dur">
188
- <span
189
- style={`font-size:10px;padding:1px 6px;border-radius:4px;background:${colors.bg};color:${colors.fg}`}
190
- >
191
- {entry.kind}
192
- </span>
193
- </span>
194
- </summary>
195
- <div class="tl-body">
196
- {entry.display ? (
197
- <pre style="color:#e2e8f0;font-family:monospace;font-size:12px;margin:0;padding:8px;white-space:pre-wrap;word-break:break-all">
198
- {entry.display}
199
- </pre>
200
- ) : (
201
- <div style="padding:4px 8px;color:#64748b;font-size:12px">No inspectable values</div>
202
- )}
203
- </div>
204
- </details>
205
- )
206
- }
207
-
208
- export function ServiceList({ services }: { services: Array<ServiceEntry> }) {
209
- if (services.length === 0) {
210
- return <div class="empty">No services registered</div>
211
- }
212
- return (
213
- <div class="tl-grid">
214
- <div class="tl-header tl-cols">
215
- <span class="tl-cell tl-cell-status" />
216
- <span class="tl-cell tl-cell-name">Service</span>
217
- <span class="tl-cell tl-cell-dur">Kind</span>
218
- </div>
219
- {services.map((s) => (
220
- <ServiceRow entry={s} />
221
- ))}
222
- </div>
223
- )
224
- }
225
-
226
- export function collectServices(unsafeMap: Map<string, any>): Array<ServiceEntry> {
227
- const entries: Array<ServiceEntry> = []
228
- for (const [key, value] of unsafeMap) {
229
- const isConfig =
230
- key.toLowerCase().includes("config") || key.toLowerCase().includes("configuration")
231
-
232
- const effectType =
233
- typeof value === "object" && value !== null ? detectEffectType(value) : undefined
234
- if (effectType) {
235
- const info = inspectEffectValue(effectType, value)
236
- entries.push({
237
- key,
238
- kind: effectType,
239
- display: JSON.stringify(info, jsonReplacer, 2),
240
- type: "effect",
241
- })
242
- continue
243
- }
244
-
245
- if (typeof value === "function") {
246
- entries.push({ key, kind: "function", display: "", type: "value" })
247
- continue
248
- }
249
-
250
- if (value === null || value === undefined) {
251
- entries.push({ key, kind: "empty", display: "", type: "value" })
252
- continue
253
- }
254
-
255
- const type = isConfig ? ("config" as const) : ("value" as const)
256
- const plain: Record<string, unknown> = {}
257
- collectDisplayValues(value, "", plain)
258
- const display = Object.keys(plain).length > 0 ? JSON.stringify(plain, jsonReplacer, 2) : ""
259
-
260
- let kind = "object"
261
- if (typeof value !== "object") {
262
- kind = typeof value
263
- } else {
264
- const proto = Object.getPrototypeOf(value)
265
- if (proto && proto.constructor && proto.constructor.name !== "Object") {
266
- kind = proto.constructor.name
267
- }
268
- }
269
-
270
- entries.push({ key, kind: isConfig ? "config" : kind, display, type })
271
- }
272
- return entries.sort((a, b) => a.key.localeCompare(b.key))
273
- }
@@ -1,62 +0,0 @@
1
- export type NavTab =
2
- | "traces"
3
- | "metrics"
4
- | "logs"
5
- | "errors"
6
- | "fibers"
7
- | "routes"
8
- | "system"
9
- | "services"
10
- | "git"
11
-
12
- export function Sidebar({ prefix, active }: { prefix: string; active: NavTab }) {
13
- return (
14
- <div class="sidebar">
15
- <div class="sidebar-title">Effect Console</div>
16
- <a href={`${prefix}/traces`} class={active === "traces" ? "nav-link active" : "nav-link"}>
17
- Traces
18
- </a>
19
- <a href={`${prefix}/metrics`} class={active === "metrics" ? "nav-link active" : "nav-link"}>
20
- Metrics
21
- </a>
22
- <a href={`${prefix}/logs`} class={active === "logs" ? "nav-link active" : "nav-link"}>
23
- Logs
24
- </a>
25
- <a href={`${prefix}/errors`} class={active === "errors" ? "nav-link active" : "nav-link"}>
26
- Errors
27
- </a>
28
- <a href={`${prefix}/fibers`} class={active === "fibers" ? "nav-link active" : "nav-link"}>
29
- Fibers
30
- </a>
31
- <a href={`${prefix}/routes`} class={active === "routes" ? "nav-link active" : "nav-link"}>
32
- Routes
33
- </a>
34
- <a href={`${prefix}/system`} class={active === "system" ? "nav-link active" : "nav-link"}>
35
- System
36
- </a>
37
- <a href={`${prefix}/services`} class={active === "services" ? "nav-link active" : "nav-link"}>
38
- Services
39
- </a>
40
- <a href={`${prefix}/git`} class={active === "git" ? "nav-link active" : "nav-link"}>
41
- Git
42
- </a>
43
- </div>
44
- )
45
- }
46
-
47
- export function Shell({
48
- prefix,
49
- active,
50
- children,
51
- }: {
52
- prefix: string
53
- active: NavTab
54
- children: any
55
- }) {
56
- return (
57
- <div class="shell">
58
- <Sidebar prefix={prefix} active={active} />
59
- <div class="content">{children}</div>
60
- </div>
61
- )
62
- }
@@ -1,131 +0,0 @@
1
- import type * as ConsoleStore from "../ConsoleStore.ts"
2
-
3
- function formatBytes(bytes: number): string {
4
- if (bytes < 1024) return `${bytes}B`
5
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`
6
- if (bytes < 1024 * 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)}MB`
7
- return `${(bytes / 1024 / 1024 / 1024).toFixed(2)}GB`
8
- }
9
-
10
- function formatUptime(seconds: number): string {
11
- const d = Math.floor(seconds / 86400)
12
- const h = Math.floor((seconds % 86400) / 3600)
13
- const m = Math.floor((seconds % 3600) / 60)
14
- const s = Math.floor(seconds % 60)
15
- if (d > 0) return `${d}d ${h}h ${m}m`
16
- if (h > 0) return `${h}h ${m}m ${s}s`
17
- if (m > 0) return `${m}m ${s}s`
18
- return `${s}s`
19
- }
20
-
21
- function StatCard({ label, value, sub }: { label: string; value: string; sub?: string }) {
22
- return (
23
- <div style="background:#111827;border:1px solid #374151;border-radius:6px;padding:12px;min-width:180px">
24
- <div style="color:#9ca3af;font-size:11px;margin-bottom:4px">{label}</div>
25
- <div style="color:#f3f4f6;font-size:22px;font-weight:700;font-family:monospace">{value}</div>
26
- {sub && <div style="color:#6b7280;font-size:10px;margin-top:2px">{sub}</div>}
27
- </div>
28
- )
29
- }
30
-
31
- function BarMeter({ label, used, total }: { label: string; used: number; total: number }) {
32
- const pct = total > 0 ? (used / total) * 100 : 0
33
- const color = pct > 90 ? "#ef4444" : pct > 70 ? "#f59e0b" : "#22c55e"
34
- return (
35
- <div style="background:#111827;border:1px solid #374151;border-radius:6px;padding:12px">
36
- <div style="display:flex;justify-content:space-between;margin-bottom:6px">
37
- <span style="color:#9ca3af;font-size:11px">{label}</span>
38
- <span style="color:#e5e7eb;font-size:11px;font-family:monospace">
39
- {formatBytes(used)} / {formatBytes(total)}
40
- </span>
41
- </div>
42
- <div style="height:8px;background:#1f2937;border-radius:4px;overflow:hidden">
43
- <div
44
- style={`width:${pct.toFixed(1)}%;height:100%;background:${color};border-radius:4px;transition:width .3s`}
45
- />
46
- </div>
47
- <div style="color:#6b7280;font-size:10px;margin-top:2px;text-align:right">
48
- {pct.toFixed(1)}%
49
- </div>
50
- </div>
51
- )
52
- }
53
-
54
- export function SystemStatsView({ stats }: { stats: ConsoleStore.ProcessStats }) {
55
- const cpuTotal = stats.cpu.user + stats.cpu.system
56
- return (
57
- <>
58
- <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;padding:12px">
59
- <StatCard
60
- label="PID"
61
- value={String(stats.pid)}
62
- sub={`${stats.system.platform} ${stats.system.arch}`}
63
- />
64
- <StatCard label="Uptime" value={formatUptime(stats.uptime)} />
65
- <StatCard
66
- label="CPU Time"
67
- value={`${(cpuTotal / 1_000_000).toFixed(2)}s`}
68
- sub={`user ${(stats.cpu.user / 1_000_000).toFixed(2)}s / sys ${(stats.cpu.system / 1_000_000).toFixed(2)}s`}
69
- />
70
- <StatCard
71
- label="Load Average"
72
- value={stats.system.loadavg[0].toFixed(2)}
73
- sub={`${stats.system.loadavg[0].toFixed(2)} / ${stats.system.loadavg[1].toFixed(2)} / ${stats.system.loadavg[2].toFixed(2)} (${stats.system.cpuCount} cores)`}
74
- />
75
- </div>
76
- <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:12px;padding:0 12px 12px">
77
- <BarMeter label="Heap Memory" used={stats.memory.heapUsed} total={stats.memory.heapTotal} />
78
- <BarMeter
79
- label="System Memory"
80
- used={stats.system.totalmem - stats.system.freemem}
81
- total={stats.system.totalmem}
82
- />
83
- </div>
84
- <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;padding:0 12px 12px">
85
- <StatCard label="RSS" value={formatBytes(stats.memory.rss)} />
86
- <StatCard label="Peak RSS" value={formatBytes(stats.resourceUsage.maxRSS)} />
87
- <StatCard label="External" value={formatBytes(stats.memory.external)} />
88
- <StatCard label="Array Buffers" value={formatBytes(stats.memory.arrayBuffers)} />
89
- </div>
90
- <div style="padding:0 12px 12px">
91
- <div style="background:#111827;border:1px solid #374151;border-radius:6px;padding:12px">
92
- <div style="color:#9ca3af;font-size:11px;margin-bottom:8px">Resource Usage</div>
93
- <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:4px">
94
- <div style="display:flex;justify-content:space-between;font-size:12px">
95
- <span style="color:#6b7280">Page Faults (minor)</span>
96
- <span style="color:#e5e7eb;font-family:monospace">
97
- {stats.resourceUsage.minorPageFault}
98
- </span>
99
- </div>
100
- <div style="display:flex;justify-content:space-between;font-size:12px">
101
- <span style="color:#6b7280">Page Faults (major)</span>
102
- <span style="color:#e5e7eb;font-family:monospace">
103
- {stats.resourceUsage.majorPageFault}
104
- </span>
105
- </div>
106
- <div style="display:flex;justify-content:space-between;font-size:12px">
107
- <span style="color:#6b7280">FS Reads</span>
108
- <span style="color:#e5e7eb;font-family:monospace">{stats.resourceUsage.fsRead}</span>
109
- </div>
110
- <div style="display:flex;justify-content:space-between;font-size:12px">
111
- <span style="color:#6b7280">FS Writes</span>
112
- <span style="color:#e5e7eb;font-family:monospace">{stats.resourceUsage.fsWrite}</span>
113
- </div>
114
- <div style="display:flex;justify-content:space-between;font-size:12px">
115
- <span style="color:#6b7280">Context Switches (vol)</span>
116
- <span style="color:#e5e7eb;font-family:monospace">
117
- {stats.resourceUsage.voluntaryContextSwitches}
118
- </span>
119
- </div>
120
- <div style="display:flex;justify-content:space-between;font-size:12px">
121
- <span style="color:#6b7280">Context Switches (invol)</span>
122
- <span style="color:#e5e7eb;font-family:monospace">
123
- {stats.resourceUsage.involuntaryContextSwitches}
124
- </span>
125
- </div>
126
- </div>
127
- </div>
128
- </div>
129
- </>
130
- )
131
- }