effect-start 0.30.1 → 0.32.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.
- package/dist/Fetch.d.ts +1 -1
- package/dist/Fetch.d.ts.map +1 -1
- package/dist/Fetch.js +1 -1
- package/dist/Fetch.js.map +1 -1
- package/dist/GlobalLayer.d.ts.map +1 -1
- package/dist/GlobalLayer.js.map +1 -1
- package/dist/Html.js +3 -3
- package/dist/Html.js.map +1 -1
- package/dist/Password.d.ts +26 -0
- package/dist/Password.d.ts.map +1 -0
- package/dist/Password.js +83 -0
- package/dist/Password.js.map +1 -0
- package/dist/Route.d.ts.map +1 -1
- package/dist/Route.js +1 -1
- package/dist/Route.js.map +1 -1
- package/dist/RouteBody.d.ts +10 -5
- package/dist/RouteBody.d.ts.map +1 -1
- package/dist/RouteBody.js +6 -2
- package/dist/RouteBody.js.map +1 -1
- package/dist/RouteHttp.d.ts.map +1 -1
- package/dist/RouteHttp.js +10 -3
- package/dist/RouteHttp.js.map +1 -1
- package/dist/RouteSse.d.ts +3 -4
- package/dist/RouteSse.d.ts.map +1 -1
- package/dist/RouteSse.js +3 -3
- package/dist/RouteSse.js.map +1 -1
- package/dist/RouteTree.d.ts +20 -5
- package/dist/RouteTree.d.ts.map +1 -1
- package/dist/RouteTree.js +172 -40
- package/dist/RouteTree.js.map +1 -1
- package/dist/StaticFiles.d.ts +23 -0
- package/dist/StaticFiles.d.ts.map +1 -0
- package/dist/StaticFiles.js +74 -0
- package/dist/StaticFiles.js.map +1 -0
- package/dist/_Mime.d.ts +2 -0
- package/dist/_Mime.d.ts.map +1 -0
- package/dist/_Mime.js +33 -0
- package/dist/_Mime.js.map +1 -0
- package/dist/_PathPattern.js +4 -4
- package/dist/_PathPattern.js.map +1 -1
- package/dist/_StreamExtra.d.ts.map +1 -1
- package/dist/_StreamExtra.js +0 -1
- package/dist/_StreamExtra.js.map +1 -1
- package/dist/bun/BunRoute.d.ts.map +1 -1
- package/dist/bun/BunRoute.js +6 -0
- package/dist/bun/BunRoute.js.map +1 -1
- package/dist/bun/BunServer.d.ts +2 -0
- package/dist/bun/BunServer.d.ts.map +1 -1
- package/dist/bun/BunServer.js +18 -7
- package/dist/bun/BunServer.js.map +1 -1
- package/dist/studio/Studio.d.ts +1 -1
- package/dist/studio/Studio.d.ts.map +1 -1
- package/dist/studio/Studio.js +5 -4
- package/dist/studio/Studio.js.map +1 -1
- package/dist/studio/StudioErrors.d.ts.map +1 -1
- package/dist/studio/StudioErrors.js +2 -2
- package/dist/studio/StudioErrors.js.map +1 -1
- package/dist/studio/StudioLogger.d.ts.map +1 -1
- package/dist/studio/StudioLogger.js +1 -3
- package/dist/studio/StudioLogger.js.map +1 -1
- package/dist/studio/StudioStore.d.ts +23 -17
- package/dist/studio/StudioStore.d.ts.map +1 -1
- package/dist/studio/StudioStore.js +48 -44
- package/dist/studio/StudioStore.js.map +1 -1
- package/dist/studio/StudioTracer.d.ts.map +1 -1
- package/dist/studio/StudioTracer.js +10 -4
- package/dist/studio/StudioTracer.js.map +1 -1
- package/dist/studio/routes/errors/route.d.ts +2 -2
- package/dist/studio/routes/errors/route.d.ts.map +1 -1
- package/dist/studio/routes/errors/route.js +2 -2
- package/dist/studio/routes/errors/route.js.map +1 -1
- package/dist/studio/routes/fiberDetail.d.ts +1 -1
- package/dist/studio/routes/fiberDetail.js +4 -4
- package/dist/studio/routes/fiberDetail.js.map +1 -1
- package/dist/studio/routes/fibers/route.d.ts +3 -3
- package/dist/studio/routes/fibers/route.js +4 -4
- package/dist/studio/routes/fibers/route.js.map +1 -1
- package/dist/studio/routes/logs/route.d.ts +2 -2
- package/dist/studio/routes/logs/route.d.ts.map +1 -1
- package/dist/studio/routes/logs/route.js +2 -2
- package/dist/studio/routes/logs/route.js.map +1 -1
- package/dist/studio/routes/metrics/route.d.ts +1 -1
- package/dist/studio/routes/system/route.d.ts +1 -1
- package/dist/studio/routes/traceDetail.d.ts +1 -1
- package/dist/studio/routes/traceDetail.js +1 -1
- package/dist/studio/routes/traceDetail.js.map +1 -1
- package/dist/studio/routes/traces/route.d.ts +3 -3
- package/dist/studio/routes/traces/route.js +5 -5
- package/dist/studio/routes/traces/route.js.map +1 -1
- package/dist/studio/routes/tree.d.ts +14 -14
- package/dist/studio/ui/Traces.d.ts +1 -0
- package/dist/studio/ui/Traces.d.ts.map +1 -1
- package/dist/studio/ui/Traces.js +32 -11
- package/dist/studio/ui/Traces.js.map +1 -1
- package/package.json +2 -2
- package/src/Fetch.ts +2 -2
- package/src/GlobalLayer.ts +1 -3
- package/src/Html.ts +3 -3
- package/src/Password.ts +130 -0
- package/src/Route.ts +10 -12
- package/src/RouteBody.ts +36 -14
- package/src/RouteHttp.ts +14 -3
- package/src/RouteSse.ts +10 -10
- package/src/RouteTree.ts +252 -61
- package/src/StaticFiles.ts +112 -0
- package/src/_Mime.ts +33 -0
- package/src/_PathPattern.ts +4 -4
- package/src/_StreamExtra.ts +0 -1
- package/src/bun/BunRoute.ts +10 -0
- package/src/bun/BunServer.ts +33 -7
- package/src/studio/Studio.ts +6 -5
- package/src/studio/StudioErrors.ts +2 -3
- package/src/studio/StudioLogger.ts +2 -3
- package/src/studio/StudioStore.ts +159 -115
- package/src/studio/StudioTracer.ts +11 -5
- package/src/studio/routes/errors/route.tsx +4 -5
- package/src/studio/routes/fiberDetail.tsx +4 -4
- package/src/studio/routes/fibers/route.tsx +4 -4
- package/src/studio/routes/logs/route.tsx +3 -1
- package/src/studio/routes/traceDetail.tsx +1 -1
- package/src/studio/routes/traces/route.tsx +8 -8
- package/src/studio/ui/Traces.tsx +41 -13
package/src/bun/BunServer.ts
CHANGED
|
@@ -50,6 +50,7 @@ export type BunServer = {
|
|
|
50
50
|
readonly server: Bun.Server<WebSocketContext>
|
|
51
51
|
readonly pushHandler: (fetch: FetchHandler) => void
|
|
52
52
|
readonly popHandler: () => void
|
|
53
|
+
readonly setRoutes: (tree: RouteTree.RouteTree) => Effect.Effect<void>
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
export const BunServer = Context.GenericTag<BunServer>("effect-start/BunServer")
|
|
@@ -77,6 +78,9 @@ export const make = (
|
|
|
77
78
|
},
|
|
78
79
|
]
|
|
79
80
|
|
|
81
|
+
const setRoutesDeferred =
|
|
82
|
+
yield* Deferred.make<(tree: RouteTree.RouteTree) => Effect.Effect<void>>()
|
|
83
|
+
|
|
80
84
|
const service = BunServer.of({
|
|
81
85
|
// During the construction we need to create a service imlpementation
|
|
82
86
|
// first so we can provide it in the runtime that will be used in web
|
|
@@ -93,6 +97,11 @@ export const make = (
|
|
|
93
97
|
handlerStack.pop()
|
|
94
98
|
reload()
|
|
95
99
|
},
|
|
100
|
+
setRoutes(tree) {
|
|
101
|
+
return Deferred.await(setRoutesDeferred).pipe(
|
|
102
|
+
Effect.flatMap((applyRoutes) => applyRoutes(tree)),
|
|
103
|
+
)
|
|
104
|
+
},
|
|
96
105
|
})
|
|
97
106
|
|
|
98
107
|
const runtime = yield* Effect.runtime().pipe(
|
|
@@ -150,6 +159,18 @@ export const make = (
|
|
|
150
159
|
})
|
|
151
160
|
}
|
|
152
161
|
|
|
162
|
+
yield* Deferred.succeed(setRoutesDeferred, (tree) =>
|
|
163
|
+
walkBunRoutes(runtime, tree).pipe(
|
|
164
|
+
Effect.tap((bunRoutes) =>
|
|
165
|
+
Effect.sync(() => {
|
|
166
|
+
currentRoutes = bunRoutes
|
|
167
|
+
reload()
|
|
168
|
+
}),
|
|
169
|
+
),
|
|
170
|
+
Effect.asVoid,
|
|
171
|
+
),
|
|
172
|
+
)
|
|
173
|
+
|
|
153
174
|
const bunServer = BunServer.of({
|
|
154
175
|
server,
|
|
155
176
|
pushHandler(fetch) {
|
|
@@ -160,6 +181,11 @@ export const make = (
|
|
|
160
181
|
handlerStack.pop()
|
|
161
182
|
reload()
|
|
162
183
|
},
|
|
184
|
+
setRoutes(tree) {
|
|
185
|
+
return Deferred.await(setRoutesDeferred).pipe(
|
|
186
|
+
Effect.flatMap((applyRoutes) => applyRoutes(tree)),
|
|
187
|
+
)
|
|
188
|
+
},
|
|
163
189
|
})
|
|
164
190
|
|
|
165
191
|
return bunServer
|
|
@@ -185,6 +211,7 @@ export const layerRoutes = (
|
|
|
185
211
|
/**
|
|
186
212
|
* Resolves the Bun server in one place for Start.serve so routes are available:
|
|
187
213
|
* 1) Reuse a user-provided BunServer when one already exists in context.
|
|
214
|
+
* If Route.Routes are available, upgrade the existing server with them.
|
|
188
215
|
* 2) Otherwise create the server from Route.Routes when routes are available.
|
|
189
216
|
* 3) Otherwise create a fallback server with the default 404 handler.
|
|
190
217
|
*/
|
|
@@ -195,18 +222,17 @@ export const layerStart = (
|
|
|
195
222
|
BunServer,
|
|
196
223
|
Effect.gen(function* () {
|
|
197
224
|
const app = yield* StartApp.StartApp
|
|
225
|
+
const routes = yield* Effect.serviceOption(Route.Routes)
|
|
226
|
+
const routeTree = Option.getOrNull(routes)
|
|
198
227
|
const existing = yield* Effect.serviceOption(BunServer)
|
|
199
228
|
if (Option.isSome(existing)) {
|
|
229
|
+
if (routeTree !== null) {
|
|
230
|
+
yield* existing.value.setRoutes(routeTree)
|
|
231
|
+
}
|
|
200
232
|
yield* Deferred.succeed(app.server, existing.value)
|
|
201
233
|
return existing.value
|
|
202
234
|
}
|
|
203
|
-
const
|
|
204
|
-
if (Option.isSome(routes)) {
|
|
205
|
-
const server = yield* make(options ?? {}, routes.value)
|
|
206
|
-
yield* Deferred.succeed(app.server, server)
|
|
207
|
-
return server
|
|
208
|
-
}
|
|
209
|
-
const server = yield* make(options ?? {})
|
|
235
|
+
const server = yield* make(options ?? {}, routeTree ?? undefined)
|
|
210
236
|
yield* Deferred.succeed(app.server, server)
|
|
211
237
|
return server
|
|
212
238
|
}),
|
package/src/studio/Studio.ts
CHANGED
|
@@ -10,16 +10,17 @@ import * as StudioMetrics from "./StudioMetrics.ts"
|
|
|
10
10
|
import * as StudioProcess from "./StudioProcess.ts"
|
|
11
11
|
import * as StudioStore from "./StudioStore.ts"
|
|
12
12
|
import * as StudioTracer from "./StudioTracer.ts"
|
|
13
|
-
import
|
|
13
|
+
import routes from "./routes/tree.ts"
|
|
14
14
|
|
|
15
15
|
export function layer(
|
|
16
16
|
options?: StudioStore.StudioStoreOptions & {
|
|
17
17
|
readonly sqlLayer?: Layer.Layer<SqlClient.SqlClient, SqlClient.SqlError>
|
|
18
18
|
},
|
|
19
|
-
): Layer.Layer<StudioStore.StudioStore> {
|
|
19
|
+
): Layer.Layer<StudioStore.StudioStore | SqlClient.SqlClient> {
|
|
20
20
|
const sqlLayer =
|
|
21
21
|
options?.sqlLayer ?? sqlBun.layer({ adapter: "sqlite" as const, filename: ":memory:" })
|
|
22
|
-
const
|
|
22
|
+
const providedSqlLayer = sqlLayer.pipe(Layer.orDie)
|
|
23
|
+
const store = StudioStore.layer(options).pipe(Layer.provide(providedSqlLayer), Layer.orDie)
|
|
23
24
|
const features = Layer.mergeAll(
|
|
24
25
|
StudioTracer.layer,
|
|
25
26
|
StudioLogger.layer,
|
|
@@ -27,7 +28,7 @@ export function layer(
|
|
|
27
28
|
StudioErrors.layer,
|
|
28
29
|
StudioProcess.layer,
|
|
29
30
|
).pipe(Layer.provide(store))
|
|
30
|
-
return Layer.
|
|
31
|
+
return Layer.mergeAll(providedSqlLayer, store, features)
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
export function layerRoutes(options?: { prefix?: string }) {
|
|
@@ -39,7 +40,7 @@ export function layerRoutes(options?: { prefix?: string }) {
|
|
|
39
40
|
const existing = yield* Route.Routes
|
|
40
41
|
StudioStore.store.prefix = prefix
|
|
41
42
|
const tree = Route.tree({
|
|
42
|
-
[prefix as "/"]:
|
|
43
|
+
[prefix as "/"]: routes,
|
|
43
44
|
})
|
|
44
45
|
return RouteTree.merge(existing, tree)
|
|
45
46
|
}),
|
|
@@ -185,7 +185,6 @@ function make(store: StudioStore.StudioStoreShape): Supervisor.Supervisor<void>
|
|
|
185
185
|
|
|
186
186
|
StudioStore.runWrite(
|
|
187
187
|
StudioStore.upsertFiber(
|
|
188
|
-
store.sql,
|
|
189
188
|
childId,
|
|
190
189
|
parentId !== childId ? parentId : undefined,
|
|
191
190
|
span?._tag === "Span" ? span.name : undefined,
|
|
@@ -206,8 +205,8 @@ function make(store: StudioStore.StudioStoreShape): Supervisor.Supervisor<void>
|
|
|
206
205
|
}
|
|
207
206
|
StudioStore.runWrite(
|
|
208
207
|
Effect.zipRight(
|
|
209
|
-
StudioStore.insertError(
|
|
210
|
-
StudioStore.evict(
|
|
208
|
+
StudioStore.insertError(error),
|
|
209
|
+
StudioStore.evict("Error", store.errorCapacity),
|
|
211
210
|
),
|
|
212
211
|
)
|
|
213
212
|
Effect.runSync(PubSub.publish(store.events, { _tag: "Error", error }))
|
|
@@ -9,7 +9,6 @@ import * as StudioStore from "./StudioStore.ts"
|
|
|
9
9
|
|
|
10
10
|
const studioLogger = Logger.make((options) => {
|
|
11
11
|
const store = StudioStore.store
|
|
12
|
-
if (!store.sql) return
|
|
13
12
|
|
|
14
13
|
try {
|
|
15
14
|
const levelMap: Record<string, StudioStore.StudioLog["level"]> = {
|
|
@@ -41,8 +40,8 @@ const studioLogger = Logger.make((options) => {
|
|
|
41
40
|
}
|
|
42
41
|
StudioStore.runWrite(
|
|
43
42
|
Effect.zipRight(
|
|
44
|
-
StudioStore.insertLog(
|
|
45
|
-
StudioStore.evict(
|
|
43
|
+
StudioStore.insertLog(log),
|
|
44
|
+
StudioStore.evict("Log", store.logCapacity),
|
|
46
45
|
),
|
|
47
46
|
)
|
|
48
47
|
Effect.runSync(PubSub.publish(store.events, { _tag: "Log", log }))
|
|
@@ -114,6 +114,8 @@ export interface StudioMetricSnapshot {
|
|
|
114
114
|
export type StudioEvent =
|
|
115
115
|
| { readonly _tag: "SpanStart"; readonly span: StudioSpan }
|
|
116
116
|
| { readonly _tag: "SpanEnd"; readonly span: StudioSpan }
|
|
117
|
+
| { readonly _tag: "TraceStart"; readonly traceId: bigint }
|
|
118
|
+
| { readonly _tag: "TraceEnd"; readonly traceId: bigint }
|
|
117
119
|
| { readonly _tag: "Log"; readonly log: StudioLog }
|
|
118
120
|
| { readonly _tag: "Error"; readonly error: StudioError }
|
|
119
121
|
| { readonly _tag: "MetricsSnapshot"; readonly metrics: Array<StudioMetricSnapshot> }
|
|
@@ -327,179 +329,221 @@ function deserializeError(row: ErrorRow): StudioError {
|
|
|
327
329
|
}
|
|
328
330
|
}
|
|
329
331
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
332
|
+
const withSql = <A, E>(
|
|
333
|
+
f: (sql: SqlClient.SqlClient) => Effect.Effect<A, E>,
|
|
334
|
+
): Effect.Effect<A, E, SqlClient.SqlClient> => Effect.flatMap(SqlClient.SqlClient, f)
|
|
335
|
+
|
|
336
|
+
export function insertSpan(span: StudioSpan) {
|
|
337
|
+
return withSql((sql) =>
|
|
338
|
+
sql`INSERT INTO Span ${sql({
|
|
339
|
+
spanId: span.spanId,
|
|
340
|
+
traceId: span.traceId,
|
|
341
|
+
fiberId: span.fiberId ?? null,
|
|
342
|
+
name: span.name,
|
|
343
|
+
kind: span.kind,
|
|
344
|
+
parentSpanId: span.parentSpanId ?? null,
|
|
345
|
+
startTime: span.startTime.toString(),
|
|
346
|
+
endTime: span.endTime?.toString() ?? null,
|
|
347
|
+
durationMs: span.durationMs ?? null,
|
|
348
|
+
status: span.status,
|
|
349
|
+
attributes: JSON.stringify(span.attributes),
|
|
350
|
+
events: JSON.stringify(serializeBigint(span.events)),
|
|
351
|
+
})}`,
|
|
352
|
+
)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export function updateSpan(span: StudioSpan) {
|
|
356
|
+
return withSql(
|
|
357
|
+
(sql) => sql`UPDATE Span SET
|
|
358
|
+
endTime = ${span.endTime?.toString() ?? null},
|
|
359
|
+
durationMs = ${span.durationMs ?? null},
|
|
360
|
+
status = ${span.status},
|
|
361
|
+
attributes = ${JSON.stringify(span.attributes)},
|
|
362
|
+
events = ${JSON.stringify(serializeBigint(span.events))}
|
|
363
|
+
WHERE spanId = ${span.spanId}`,
|
|
364
|
+
)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export function insertLog(log: StudioLog) {
|
|
368
|
+
return withSql((sql) =>
|
|
369
|
+
sql`INSERT INTO Log ${sql({
|
|
370
|
+
id: log.id,
|
|
371
|
+
level: log.level,
|
|
372
|
+
message: log.message,
|
|
373
|
+
fiberId: log.fiberId,
|
|
374
|
+
cause: log.cause ?? null,
|
|
375
|
+
spans: JSON.stringify(log.spans),
|
|
376
|
+
annotations: JSON.stringify(log.annotations),
|
|
377
|
+
})}`,
|
|
378
|
+
)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export function insertError(error: StudioError) {
|
|
382
|
+
return withSql((sql) =>
|
|
383
|
+
sql`INSERT INTO Error ${sql({
|
|
384
|
+
id: error.id,
|
|
385
|
+
fiberId: error.fiberId,
|
|
386
|
+
interrupted: error.interrupted ? 1 : 0,
|
|
387
|
+
prettyPrint: error.prettyPrint,
|
|
388
|
+
details: JSON.stringify(error.details),
|
|
389
|
+
})}`,
|
|
390
|
+
)
|
|
377
391
|
}
|
|
378
392
|
|
|
379
393
|
export function upsertFiber(
|
|
380
|
-
sql: SqlClient.SqlClient,
|
|
381
394
|
id: string,
|
|
382
395
|
parentId: string | undefined,
|
|
383
396
|
spanName: string | undefined,
|
|
384
397
|
traceId: bigint | undefined,
|
|
385
398
|
annotations: Record<string, unknown>,
|
|
386
399
|
) {
|
|
387
|
-
return sql
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
export function evict(sql: SqlClient.SqlClient, table: string, capacity: number) {
|
|
397
|
-
return Effect.gen(function* () {
|
|
398
|
-
const [{ cnt }] = yield* sql<{ cnt: number }>`SELECT count(*) as cnt FROM ${sql(table)}`
|
|
399
|
-
if (cnt > capacity) {
|
|
400
|
-
const excess = cnt - capacity
|
|
401
|
-
yield* sql`DELETE FROM ${sql(table)} WHERE rowid IN (SELECT rowid FROM ${sql(table)} ORDER BY rowid LIMIT ${excess})`
|
|
402
|
-
}
|
|
403
|
-
})
|
|
400
|
+
return withSql((sql) =>
|
|
401
|
+
sql`INSERT OR REPLACE INTO Fiber ${sql({
|
|
402
|
+
id,
|
|
403
|
+
parentId: parentId ?? null,
|
|
404
|
+
spanName: spanName ?? null,
|
|
405
|
+
traceId: traceId ?? null,
|
|
406
|
+
annotations: JSON.stringify(annotations),
|
|
407
|
+
})}`,
|
|
408
|
+
)
|
|
404
409
|
}
|
|
405
410
|
|
|
406
|
-
export function
|
|
411
|
+
export function evict(table: string, capacity: number) {
|
|
412
|
+
return withSql((sql) =>
|
|
413
|
+
Effect.gen(function* () {
|
|
414
|
+
const [{ cnt }] = yield* sql<{ cnt: number }>`SELECT count(*) as cnt FROM ${sql(table)}`
|
|
415
|
+
if (cnt > capacity) {
|
|
416
|
+
const excess = cnt - capacity
|
|
417
|
+
yield* sql`DELETE FROM ${sql(table)} WHERE rowid IN (SELECT rowid FROM ${sql(table)} ORDER BY rowid LIMIT ${excess})`
|
|
418
|
+
}
|
|
419
|
+
}),
|
|
420
|
+
)
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export function runWrite(effect: Effect.Effect<unknown, SqlClient.SqlError, SqlClient.SqlClient>) {
|
|
424
|
+
const sql = store.sql
|
|
425
|
+
if (!sql) return
|
|
407
426
|
writeQueue = writeQueue
|
|
408
|
-
.then(() =>
|
|
427
|
+
.then(() =>
|
|
428
|
+
Effect.runPromise(
|
|
429
|
+
Effect.withTracerEnabled(
|
|
430
|
+
Effect.provideService(effect, SqlClient.SqlClient, sql),
|
|
431
|
+
false,
|
|
432
|
+
),
|
|
433
|
+
).then(() => undefined),
|
|
434
|
+
)
|
|
409
435
|
.catch(() => undefined)
|
|
410
436
|
}
|
|
411
437
|
|
|
412
438
|
let writeQueue = Promise.resolve<void>(undefined)
|
|
413
439
|
|
|
414
|
-
const noTrace = <A, E>(effect: Effect.Effect<A, E>): Effect.Effect<A, E> =>
|
|
440
|
+
const noTrace = <A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
|
|
415
441
|
Effect.withTracerEnabled(effect, false)
|
|
416
442
|
|
|
417
|
-
export function allSpans(
|
|
443
|
+
export function allSpans() {
|
|
418
444
|
return noTrace(
|
|
419
|
-
|
|
420
|
-
|
|
445
|
+
withSql((sql) =>
|
|
446
|
+
Effect.map(sql<SpanRow>`SELECT * FROM Span ORDER BY rowid`, (rows) =>
|
|
447
|
+
rows.map(deserializeSpan),
|
|
448
|
+
),
|
|
421
449
|
),
|
|
422
450
|
)
|
|
423
451
|
}
|
|
424
452
|
|
|
425
|
-
export function allLogs(
|
|
453
|
+
export function allLogs() {
|
|
426
454
|
return noTrace(
|
|
427
|
-
|
|
455
|
+
withSql((sql) =>
|
|
456
|
+
Effect.map(sql<LogRow>`SELECT * FROM Log ORDER BY rowid`, (rows) => rows.map(deserializeLog)),
|
|
457
|
+
),
|
|
428
458
|
)
|
|
429
459
|
}
|
|
430
460
|
|
|
431
|
-
export function allErrors(
|
|
461
|
+
export function allErrors() {
|
|
432
462
|
return noTrace(
|
|
433
|
-
|
|
434
|
-
|
|
463
|
+
withSql((sql) =>
|
|
464
|
+
Effect.map(sql<ErrorRow>`SELECT * FROM Error ORDER BY rowid`, (rows) =>
|
|
465
|
+
rows.map(deserializeError),
|
|
466
|
+
),
|
|
435
467
|
),
|
|
436
468
|
)
|
|
437
469
|
}
|
|
438
470
|
|
|
439
|
-
export function spansByTraceId(
|
|
471
|
+
export function spansByTraceId(traceId: bigint) {
|
|
440
472
|
return noTrace(
|
|
441
|
-
|
|
442
|
-
|
|
473
|
+
withSql((sql) =>
|
|
474
|
+
Effect.map(sql<SpanRow>`SELECT * FROM Span WHERE traceId = ${traceId} ORDER BY rowid`, (rows) =>
|
|
475
|
+
rows.map(deserializeSpan),
|
|
476
|
+
),
|
|
443
477
|
),
|
|
444
478
|
)
|
|
445
479
|
}
|
|
446
480
|
|
|
447
|
-
export function spansByFiberId(
|
|
481
|
+
export function spansByFiberId(fiberId: string) {
|
|
448
482
|
return noTrace(
|
|
449
|
-
|
|
450
|
-
|
|
483
|
+
withSql((sql) =>
|
|
484
|
+
Effect.map(sql<SpanRow>`SELECT * FROM Span WHERE fiberId = ${fiberId} ORDER BY rowid`, (rows) =>
|
|
485
|
+
rows.map(deserializeSpan),
|
|
486
|
+
),
|
|
451
487
|
),
|
|
452
488
|
)
|
|
453
489
|
}
|
|
454
490
|
|
|
455
|
-
export function logsByFiberId(
|
|
491
|
+
export function logsByFiberId(fiberId: string) {
|
|
456
492
|
return noTrace(
|
|
457
|
-
|
|
458
|
-
|
|
493
|
+
withSql((sql) =>
|
|
494
|
+
Effect.map(sql<LogRow>`SELECT * FROM Log WHERE fiberId = ${fiberId} ORDER BY rowid`, (rows) =>
|
|
495
|
+
rows.map(deserializeLog),
|
|
496
|
+
),
|
|
459
497
|
),
|
|
460
498
|
)
|
|
461
499
|
}
|
|
462
500
|
|
|
463
|
-
export function getFiber(
|
|
501
|
+
export function getFiber(fiberId: string) {
|
|
464
502
|
return noTrace(
|
|
465
|
-
|
|
466
|
-
|
|
503
|
+
withSql((sql) =>
|
|
504
|
+
Effect.map(sql<FiberRow>`SELECT * FROM Fiber WHERE id = ${fiberId}`, (rows) =>
|
|
505
|
+
rows.length > 0 ? rows[0] : undefined,
|
|
506
|
+
),
|
|
467
507
|
),
|
|
468
508
|
)
|
|
469
509
|
}
|
|
470
510
|
|
|
471
|
-
export function getParentChain(
|
|
511
|
+
export function getParentChain(fiberId: string) {
|
|
472
512
|
return noTrace(
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
513
|
+
withSql((sql) =>
|
|
514
|
+
Effect.gen(function* () {
|
|
515
|
+
const chain: Array<string> = []
|
|
516
|
+
const visited = new Set<string>()
|
|
517
|
+
let current = fiberId
|
|
518
|
+
while (true) {
|
|
519
|
+
const rows = yield* sql<FiberRow>`SELECT * FROM Fiber WHERE id = ${current}`
|
|
520
|
+
if (rows.length === 0 || !rows[0].parentId) break
|
|
521
|
+
const parentId = rows[0].parentId
|
|
522
|
+
if (visited.has(parentId)) break
|
|
523
|
+
chain.push(parentId)
|
|
524
|
+
visited.add(parentId)
|
|
525
|
+
current = parentId
|
|
526
|
+
}
|
|
527
|
+
return chain.reverse()
|
|
528
|
+
}),
|
|
529
|
+
),
|
|
488
530
|
)
|
|
489
531
|
}
|
|
490
532
|
|
|
491
|
-
export function getFiberContext(
|
|
533
|
+
export function getFiberContext(fiberId: string) {
|
|
492
534
|
return noTrace(
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
rows
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
535
|
+
withSql((sql) =>
|
|
536
|
+
Effect.map(
|
|
537
|
+
sql<FiberRow>`SELECT * FROM Fiber WHERE id = ${fiberId}`,
|
|
538
|
+
(rows): FiberContext | undefined =>
|
|
539
|
+
rows.length > 0
|
|
540
|
+
? {
|
|
541
|
+
spanName: rows[0].spanName ?? undefined,
|
|
542
|
+
traceId: rows[0].traceId != null ? BigInt(rows[0].traceId) : undefined,
|
|
543
|
+
annotations: JSON.parse(rows[0].annotations),
|
|
544
|
+
}
|
|
545
|
+
: undefined,
|
|
546
|
+
),
|
|
503
547
|
),
|
|
504
548
|
)
|
|
505
549
|
}
|
|
@@ -62,11 +62,14 @@ const make = (store: StudioStore.StudioStoreShape): Tracer.Tracer =>
|
|
|
62
62
|
|
|
63
63
|
StudioStore.runWrite(
|
|
64
64
|
Effect.zipRight(
|
|
65
|
-
StudioStore.insertSpan(
|
|
66
|
-
StudioStore.evict(
|
|
65
|
+
StudioStore.insertSpan(studioSpan),
|
|
66
|
+
StudioStore.evict("Span", store.spanCapacity),
|
|
67
67
|
),
|
|
68
68
|
)
|
|
69
69
|
publish(store, { _tag: "SpanStart", span: studioSpan })
|
|
70
|
+
if (parentSpanId === undefined) {
|
|
71
|
+
publish(store, { _tag: "TraceStart", traceId })
|
|
72
|
+
}
|
|
70
73
|
|
|
71
74
|
const attrs = new Map<string, unknown>(Object.entries(attributes))
|
|
72
75
|
const spanLinks = [...links]
|
|
@@ -97,17 +100,20 @@ const make = (store: StudioStore.StudioStoreShape): Tracer.Tracer =>
|
|
|
97
100
|
studioSpan.endTime = endTime
|
|
98
101
|
studioSpan.durationMs = Number(endTime - studioSpan.startTime) / 1_000_000
|
|
99
102
|
studioSpan.status = Exit.isSuccess(exit) ? "ok" : "error"
|
|
100
|
-
StudioStore.runWrite(StudioStore.updateSpan(
|
|
103
|
+
StudioStore.runWrite(StudioStore.updateSpan(studioSpan))
|
|
101
104
|
publish(store, { _tag: "SpanEnd", span: studioSpan })
|
|
105
|
+
if (parentSpanId === undefined) {
|
|
106
|
+
publish(store, { _tag: "TraceEnd", traceId })
|
|
107
|
+
}
|
|
102
108
|
},
|
|
103
109
|
attribute(key, value) {
|
|
104
110
|
attrs.set(key, value)
|
|
105
111
|
;(studioSpan.attributes as Record<string, unknown>)[key] = value
|
|
106
|
-
StudioStore.runWrite(StudioStore.updateSpan(
|
|
112
|
+
StudioStore.runWrite(StudioStore.updateSpan(studioSpan))
|
|
107
113
|
},
|
|
108
114
|
event(name, startTime, attributes) {
|
|
109
115
|
studioSpan.events.push({ name, startTime, attributes })
|
|
110
|
-
StudioStore.runWrite(StudioStore.updateSpan(
|
|
116
|
+
StudioStore.runWrite(StudioStore.updateSpan(studioSpan))
|
|
111
117
|
},
|
|
112
118
|
addLinks(newLinks) {
|
|
113
119
|
spanLinks.push(...newLinks)
|
|
@@ -12,7 +12,7 @@ export default Route.get(
|
|
|
12
12
|
const url = new URL(ctx.request.url)
|
|
13
13
|
const search = url.searchParams.get("errorSearch") || ""
|
|
14
14
|
const tag = url.searchParams.get("errorTag") || ""
|
|
15
|
-
const allErrors = yield* StudioStore.allErrors(
|
|
15
|
+
const allErrors = yield* StudioStore.allErrors()
|
|
16
16
|
const tagSet = new Set<string>()
|
|
17
17
|
for (const error of allErrors) {
|
|
18
18
|
for (const d of error.details) {
|
|
@@ -67,6 +67,8 @@ export default Route.get(
|
|
|
67
67
|
<Errors.ErrorLine error={e} />
|
|
68
68
|
))}
|
|
69
69
|
</div>
|
|
70
|
+
|
|
71
|
+
<div data-init={`@get('${prefix}/errors')`} />
|
|
70
72
|
</form>
|
|
71
73
|
</Shell.Shell>
|
|
72
74
|
)
|
|
@@ -75,10 +77,7 @@ export default Route.get(
|
|
|
75
77
|
Stream.fromPubSub(StudioStore.store.events).pipe(
|
|
76
78
|
Stream.filter((e) => e._tag === "Error"),
|
|
77
79
|
Stream.map((e) => {
|
|
78
|
-
const html = Html.renderToString(<Errors.ErrorLine error={e.error} />).replace(
|
|
79
|
-
/\n/g,
|
|
80
|
-
"",
|
|
81
|
-
)
|
|
80
|
+
const html = Html.renderToString(<Errors.ErrorLine error={e.error} />).replace(/\n/g, "")
|
|
82
81
|
return {
|
|
83
82
|
event: "datastar-patch-elements",
|
|
84
83
|
data: `selector #errors-list\nmode prepend\nelements ${html}`,
|
|
@@ -12,8 +12,8 @@ export default Route.get(
|
|
|
12
12
|
const fiberId = ctx.pathParams.id
|
|
13
13
|
const fiberName = fiberId.startsWith("#") ? fiberId : `#${fiberId}`
|
|
14
14
|
|
|
15
|
-
const fiberLogs = yield* StudioStore.logsByFiberId(
|
|
16
|
-
const fiberSpans = yield* StudioStore.spansByFiberId(
|
|
15
|
+
const fiberLogs = yield* StudioStore.logsByFiberId(fiberName)
|
|
16
|
+
const fiberSpans = yield* StudioStore.spansByFiberId(fiberName)
|
|
17
17
|
|
|
18
18
|
const fiberNum = parseInt(fiberName.slice(1), 10)
|
|
19
19
|
const counter = StudioStore.fiberIdCounter()
|
|
@@ -30,8 +30,8 @@ export default Route.get(
|
|
|
30
30
|
else if (fiberNum < counter) alive = "dead"
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const parents = yield* StudioStore.getParentChain(
|
|
34
|
-
const fiberContext = yield* StudioStore.getFiberContext(
|
|
33
|
+
const parents = yield* StudioStore.getParentChain(fiberName)
|
|
34
|
+
const fiberContext = yield* StudioStore.getFiberContext(fiberName)
|
|
35
35
|
|
|
36
36
|
return (
|
|
37
37
|
<Shell.Shell prefix={StudioStore.store.prefix} active="fibers">
|
|
@@ -8,8 +8,8 @@ import * as Shell from "../../ui/Shell.tsx"
|
|
|
8
8
|
|
|
9
9
|
export default Route.get(
|
|
10
10
|
Route.html(function* () {
|
|
11
|
-
const logs = yield* StudioStore.allLogs(
|
|
12
|
-
const spans = yield* StudioStore.allSpans(
|
|
11
|
+
const logs = yield* StudioStore.allLogs()
|
|
12
|
+
const spans = yield* StudioStore.allSpans()
|
|
13
13
|
const fibers = Fibers.collectFibers(logs, spans)
|
|
14
14
|
return (
|
|
15
15
|
<Shell.Shell prefix={StudioStore.store.prefix} active="fibers">
|
|
@@ -28,8 +28,8 @@ export default Route.get(
|
|
|
28
28
|
Stream.filter((e) => e._tag === "SpanStart" || e._tag === "SpanEnd" || e._tag === "Log"),
|
|
29
29
|
Stream.mapEffect(() =>
|
|
30
30
|
Effect.gen(function* () {
|
|
31
|
-
const logs = yield* StudioStore.allLogs(
|
|
32
|
-
const spans = yield* StudioStore.allSpans(
|
|
31
|
+
const logs = yield* StudioStore.allLogs()
|
|
32
|
+
const spans = yield* StudioStore.allSpans()
|
|
33
33
|
const fibers = Fibers.collectFibers(logs, spans)
|
|
34
34
|
const html = Html.renderToString(
|
|
35
35
|
<Fibers.FiberList fibers={fibers} prefix={StudioStore.store.prefix} />,
|
|
@@ -12,7 +12,7 @@ export default Route.get(
|
|
|
12
12
|
const url = new URL(ctx.request.url)
|
|
13
13
|
const level = url.searchParams.get("logLevel") || ""
|
|
14
14
|
const search = url.searchParams.get("logSearch") || ""
|
|
15
|
-
let logs = yield* StudioStore.allLogs(
|
|
15
|
+
let logs = yield* StudioStore.allLogs()
|
|
16
16
|
if (level) logs = logs.filter((l) => l.level === level)
|
|
17
17
|
if (search) {
|
|
18
18
|
const lower = search.toLowerCase()
|
|
@@ -53,6 +53,8 @@ export default Route.get(
|
|
|
53
53
|
<Logs.LogLine log={l} />
|
|
54
54
|
))}
|
|
55
55
|
</div>
|
|
56
|
+
|
|
57
|
+
<div data-init={`@get('${prefix}/logs')`} />
|
|
56
58
|
</form>
|
|
57
59
|
</Shell.Shell>
|
|
58
60
|
)
|
|
@@ -18,7 +18,7 @@ export default Route.get(
|
|
|
18
18
|
</Shell.Shell>
|
|
19
19
|
)
|
|
20
20
|
}
|
|
21
|
-
const spans = yield* StudioStore.spansByTraceId(
|
|
21
|
+
const spans = yield* StudioStore.spansByTraceId(traceId)
|
|
22
22
|
|
|
23
23
|
return (
|
|
24
24
|
<Shell.Shell prefix={StudioStore.store.prefix} active="traces">
|