Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
0b42cf4
Add endpoint
dmashuda Aug 4, 2025
edb122e
Update logging
dmashuda Aug 4, 2025
3807d19
Add run configs
dmashuda Aug 4, 2025
3604ac0
Fix printly
dmashuda Aug 4, 2025
5db6c4d
Fix precommit
dmashuda Aug 4, 2025
5c75a46
fix typo in function name
moribellamy Aug 4, 2025
026d81a
SSE plumbing
moribellamy Aug 4, 2025
dbe2abc
observer pattern
moribellamy Aug 4, 2025
fd32694
rm nil check
moribellamy Aug 4, 2025
4a251b4
Add toggle for new "events" section on SPA.
moribellamy Aug 5, 2025
0774bfc
modes for SPA
moribellamy Aug 5, 2025
2c82092
Revert "modes for SPA"
moribellamy Aug 5, 2025
4086268
Revert "Add toggle for new "events" section on SPA."
moribellamy Aug 5, 2025
6a4c791
update git ignore
moribellamy Aug 5, 2025
9731ced
rename app to flagpage
moribellamy Aug 5, 2025
62b25bf
finish multi-page refactor
moribellamy Aug 5, 2025
2ae6b6a
Revert "finish multi-page refactor"
moribellamy Aug 5, 2025
f5b5fe7
Revert "rename app to flagpage"
moribellamy Aug 5, 2025
5f614b8
Merge remote-tracking branch 'origin/main' into moonshot-xxii-debugger
moribellamy Aug 5, 2025
0974eb5
add buttons to swap
moribellamy Aug 5, 2025
f3b19db
Add event splitting
dmashuda Aug 5, 2025
a4fde70
Switch to raw message
dmashuda Aug 5, 2025
770455a
initial stream page
moribellamy Aug 5, 2025
d9fdfb6
Merge branch 'moonshot-xxii-debugger' of github.com:launchdarkly/ldcl…
moribellamy Aug 5, 2025
8228fa8
Add kind selection
dmashuda Aug 5, 2025
f124472
take event rendering to be its own component
moribellamy Aug 5, 2025
3936b51
Merge branch 'moonshot-xxii-debugger' of github.com:launchdarkly/ldcl…
moribellamy Aug 5, 2025
ecce163
list
moribellamy Aug 5, 2025
c3fd209
add basic tabularization of summary events
moribellamy Aug 5, 2025
9ca3e16
more context types
moribellamy Aug 6, 2025
6d824b6
add react router
tvarney13 Aug 6, 2025
724f7c0
clean up buttons to be dropdown
tvarney13 Aug 6, 2025
d0b3f44
add feature events to list
tvarney13 Aug 6, 2025
2a868e2
fix refresh without dev server
moribellamy Aug 6, 2025
356143c
Add basic sql for event writting
dmashuda Aug 6, 2025
8ad426f
Add query
dmashuda Aug 6, 2025
cab542e
Add debug session
dmashuda Aug 6, 2025
5e02e7b
Add event store
dmashuda Aug 6, 2025
104772e
Add query
dmashuda Aug 6, 2025
1f6b3fc
remove random text
tvarney13 Aug 6, 2025
1aa5080
enable feature events to be visible based on if a flag is overridden …
tvarney13 Aug 6, 2025
432c411
Add sqllite writter
dmashuda Aug 6, 2025
688d8fb
ui improvements
moribellamy Aug 6, 2025
969c26d
Merge branch 'moonshot-xxii-debugger' of github.com:launchdarkly/ldcl…
moribellamy Aug 6, 2025
037ebda
make feature events more visible
tvarney13 Aug 6, 2025
1d6550d
clean up link column
tvarney13 Aug 6, 2025
6034185
add pagination for debug events
dmashuda Aug 6, 2025
2260ca4
Add enpoint for debug sessions
dmashuda Aug 6, 2025
5caa3b5
dark mode css
moribellamy Aug 6, 2025
cb0b2dd
Merge branch 'moonshot-xxii-debugger' of github.com:launchdarkly/ldcl…
moribellamy Aug 6, 2025
c58d6f2
Fix data parsing
dmashuda Aug 6, 2025
2fa6bc0
streaming css
moribellamy Aug 6, 2025
a03deee
Merge branch 'moonshot-xxii-debugger' of github.com:launchdarkly/ldcl…
moribellamy Aug 6, 2025
987639b
Fix linting
dmashuda Aug 6, 2025
53a37af
add streaming button
moribellamy Aug 6, 2025
fb26adf
Merge branch 'moonshot-xxii-debugger' of github.com:launchdarkly/ldcl…
moribellamy Aug 6, 2025
c5fa466
timestamp fallback
moribellamy Aug 6, 2025
6f3c80f
rename key to target
moribellamy Aug 6, 2025
e35caeb
refactor events table
moribellamy Aug 6, 2025
2e7de06
hideButtonSometimes
moribellamy Aug 6, 2025
6fc04dd
Up the limit
dmashuda Aug 6, 2025
81c8c08
remove limit
moribellamy Aug 6, 2025
7dcd345
Merge branch 'moonshot-xxii-debugger' of github.com:launchdarkly/ldcl…
moribellamy Aug 6, 2025
a8de148
Vibes
dmashuda Aug 6, 2025
49113c4
Vibes: add debug sessions page
dmashuda Aug 6, 2025
e505269
remove per event filtering
tvarney13 Aug 6, 2025
fcf8d12
more parsing of context kinds
moribellamy Aug 6, 2025
1105174
Merge branch 'moonshot-xxii-debugger' of github.com:launchdarkly/ldcl…
moribellamy Aug 6, 2025
7e2bfd9
remove back link for debug session
moribellamy Aug 6, 2025
ae1a73f
css
moribellamy Aug 6, 2025
04506c9
bigger boxes
moribellamy Aug 6, 2025
e1bd882
handle empty list
moribellamy Aug 6, 2025
63501c0
Add debug session deletion
dmashuda Aug 6, 2025
6871f12
Add delete endpoint
dmashuda Aug 6, 2025
0a1fa97
Handle debug sessions
dmashuda Aug 6, 2025
01369f5
copy button improvements
moribellamy Aug 6, 2025
d7f8dcc
Clean up empty debug session on startup
dmashuda Aug 6, 2025
2d1668c
add filter for debug session page
moribellamy Aug 6, 2025
ab84df3
Merge branch 'moonshot-xxii-debugger' of github.com:launchdarkly/ldcl…
moribellamy Aug 6, 2025
5e0a2be
Don't log json raw message
dmashuda Aug 6, 2025
4de2d85
new search
moribellamy Aug 6, 2025
e7f7f9a
Merge branch 'moonshot-xxii-debugger' of github.com:launchdarkly/ldcl…
moribellamy Aug 6, 2025
a5b9ab4
remove explicit any
moribellamy Aug 13, 2025
6d33b2b
more lint
moribellamy Aug 13, 2025
2954da2
better types
moribellamy Aug 14, 2025
d184d3b
lint
moribellamy Aug 14, 2025
3ce62c8
lint
moribellamy Aug 14, 2025
88ff6cc
more test
moribellamy Aug 14, 2025
bce9ea2
TS
moribellamy Aug 14, 2025
5844d4d
dist
moribellamy Aug 14, 2025
4fb3eda
prettier
moribellamy Aug 14, 2025
9f908b2
Fix sql semantics
dmashuda Aug 19, 2025
0584dce
Add overrides
dmashuda Aug 19, 2025
47ef182
remove run configs
dmashuda Aug 19, 2025
36a8028
Merge branch 'main' into moonshot-xxii-debugger
dmashuda Aug 19, 2025
c0fb94b
fix clipboard link
moribellamy Aug 19, 2025
e4bae99
dist
moribellamy Aug 19, 2025
6d0153a
Docs
dmashuda Aug 20, 2025
338e2ab
Move event base
dmashuda Aug 22, 2025
f00ec6a
Move events api to its own package because its a cross-cutting concern
dmashuda Aug 22, 2025
8bd0edc
Use launchdpad components for everything
brianvans Sep 18, 2025
eb221a6
Fix debug sessions page zero state crash
brianvans Sep 19, 2025
252a933
Use event timestamps over now() where possible
brianvans Sep 19, 2025
7056473
Heading > h3
brianvans Sep 19, 2025
dbfa198
Merge remote-tracking branch 'origin/main' into moonshot-xxii-debugger
brianvans Sep 23, 2025
d77388b
Fix issue where some pages dont re-render
brianvans Sep 25, 2025
38e6c8b
Linter
brianvans Sep 26, 2025
2722b1d
Revive the favicon by skipping SPA for /ui/*.svg
brianvans Sep 26, 2025
efcb03c
Remove log spam
dmashuda Sep 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 169 additions & 0 deletions internal/dev_server/api/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,92 @@ paths:
$ref: "#/components/responses/ErrorResponse"
400:
$ref: "#/components/responses/ErrorResponse"
/debug-sessions:
get:
operationId: getDebugSessions
summary: list all debug sessions with event counts
parameters:
- name: limit
in: query
description: limit the number of debug sessions returned
required: false
schema:
type: integer
default: 50
- name: offset
in: query
description: offset for pagination
required: false
schema:
type: integer
default: 0
responses:
200:
description: OK. List of debug sessions
content:
application/json:
schema:
$ref: "#/components/schemas/DebugSessionsPage"
400:
$ref: "#/components/responses/ErrorResponse"
/debug-sessions/{debugSessionKey}:
delete:
operationId: deleteDebugSession
summary: delete a specific debug session and all its events
parameters:
- name: debugSessionKey
in: path
required: true
schema:
type: string
description: unique identifier for the debug session
responses:
204:
description: OK. Debug session and all associated events were deleted
404:
$ref: "#/components/responses/ErrorResponse"
/debug-sessions/{debugSessionKey}/events:
get:
operationId: getDebugSessionEvents
summary: get events for a specific debug session
parameters:
- name: debugSessionKey
in: path
required: true
schema:
type: string
description: unique identifier for the debug session
- name: kind
in: query
description: filter events by kind (e.g., summary, diagnostic, feature)
required: false
schema:
type: string
- name: limit
in: query
description: limit the number of events returned
required: false
schema:
type: integer
default: 50
- name: offset
in: query
description: offset for pagination
required: false
schema:
type: integer
default: 0
responses:
200:
description: OK. List of events for the debug session
content:
application/json:
schema:
$ref: "#/components/schemas/EventsPage"
404:
$ref: "#/components/responses/ErrorResponse"
400:
$ref: "#/components/responses/ErrorResponse"
components:
parameters:
flagKey:
Expand Down Expand Up @@ -294,6 +380,89 @@ components:
type: string
name:
type: string
DebugSession:
description: Debug session with event count
type: object
required:
- key
- written_at
- event_count
properties:
key:
type: string
description: unique identifier for the debug session
written_at:
type: string
format: date-time
description: timestamp when the debug session was created
event_count:
type: integer
format: int64
description: number of events associated with this debug session
DebugSessionsPage:
description: Paginated response of debug sessions
type: object
required:
- sessions
- total_count
- has_more
properties:
sessions:
type: array
items:
$ref: "#/components/schemas/DebugSession"
description: list of debug sessions
total_count:
type: integer
format: int64
description: total number of debug sessions available
has_more:
type: boolean
description: whether there are more results available
Event:
description: A stored event with metadata
type: object
required:
- id
- written_at
- kind
- data
properties:
id:
type: integer
format: int64
description: unique identifier for the event
written_at:
type: string
format: date-time
description: timestamp when the event was written
kind:
type: string
description: type of event (e.g., summary, diagnostic, feature)
data:
type: object
description: raw event data as JSON
x-go-type: json.RawMessage
EventsPage:
description: Paginated response of events
type: object
required:
- events
- total_count
- has_more
properties:
events:
type: array
items:
$ref: "#/components/schemas/Event"
description: list of events
total_count:
type: integer
format: int64
description: total number of events available
has_more:
type: boolean
description: whether there are more results available
responses:
FlagOverride:
description: Flag override
Expand Down
19 changes: 19 additions & 0 deletions internal/dev_server/api/delete_debug_session.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package api

import (
"context"

"github.com/launchdarkly/ldcli/internal/dev_server/model"
)

func (s server) DeleteDebugSession(ctx context.Context, request DeleteDebugSessionRequestObject) (DeleteDebugSessionResponseObject, error) {
eventStore := model.EventStoreFromContext(ctx)

// Delete the debug session
err := eventStore.DeleteDebugSession(ctx, request.DebugSessionKey)
if err != nil {
return nil, err
}

return DeleteDebugSession204Response{}, nil
}
82 changes: 82 additions & 0 deletions internal/dev_server/api/events/events_stream.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package events

import (
"context"
"encoding/json"
"log"
"net/http"

"github.com/google/uuid"
"github.com/launchdarkly/ldcli/internal/dev_server/model"
"github.com/pkg/errors"

"github.com/launchdarkly/ldcli/internal/dev_server/sdk"
)

type sdkEventObserver struct {
ctx context.Context
debugSessionKey string
updateChan chan<- sdk.Message
}

func newSdkEventObserver(updateChan chan<- sdk.Message, ctx context.Context) sdkEventObserver {
debugSessionKey := uuid.New().String()
db := model.EventStoreFromContext(ctx)
err := db.CreateDebugSession(ctx, debugSessionKey)
if err != nil {
log.Printf("sdkEventObserver: error writting debug session: %v", err)
}
return sdkEventObserver{
debugSessionKey: debugSessionKey,
ctx: ctx,
updateChan: updateChan,
}
}

func (o sdkEventObserver) Handle(message interface{}) {
str, ok := message.(json.RawMessage)
if !ok {
return
}

event := sdk.SDKEventBase{}
err := json.Unmarshal(str, &event)
if err != nil {
log.Printf("sdkEventObserver: error unmarshaling event: %v", err)
return
}

db := model.EventStoreFromContext(o.ctx)

err = db.WriteEvent(o.ctx, o.debugSessionKey, event.Kind, str)
if err != nil {
log.Printf("sdkEventObserver: error writting event: %v", err)
return
}

o.updateChan <- sdk.Message{Event: sdk.TYPE_PUT, Data: str}
}

func SdkEventsTeeHandler(writer http.ResponseWriter, request *http.Request) {
updateChan, errChan := sdk.OpenStream(
writer,
request.Context().Done(),
sdk.Message{Event: sdk.TYPE_PUT, Data: []byte{}},
)
defer close(updateChan)
observers := model.GetObserversFromContext(request.Context())

observerId := observers.RegisterObserver(newSdkEventObserver(updateChan, request.Context()))
defer func() {
ok := observers.DeregisterObserver(observerId)
if !ok {
log.Printf("unable to remove observer")
}
}()

err := <-errChan
if err != nil {
sdk.WriteError(request.Context(), writer, errors.Wrap(err, "stream failure"))
return
}
}
7 changes: 7 additions & 0 deletions internal/dev_server/api/events/routes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package events

import "github.com/gorilla/mux"

func BindRoutes(router *mux.Router) {
router.HandleFunc("/events/tee", SdkEventsTeeHandler)
}
61 changes: 61 additions & 0 deletions internal/dev_server/api/get_debug_session_events.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package api

import (
"context"
"github.com/launchdarkly/ldcli/internal/dev_server/model"
)

func (s server) GetDebugSessionEvents(ctx context.Context, request GetDebugSessionEventsRequestObject) (GetDebugSessionEventsResponseObject, error) {
eventStore := model.EventStoreFromContext(ctx)

// Set default values for pagination
limit := 50
offset := 0

if request.Params.Limit != nil {
limit = *request.Params.Limit
}
if request.Params.Offset != nil {
offset = *request.Params.Offset
}

// Validate parameters
if limit < 1 || limit > 10000 {
return GetDebugSessionEvents400JSONResponse{ErrorResponseJSONResponse{
Code: "invalid_parameter",
Message: "limit must be between 1 and 10000",
}}, nil
}

if offset < 0 {
return GetDebugSessionEvents400JSONResponse{ErrorResponseJSONResponse{
Code: "invalid_parameter",
Message: "offset must be non-negative",
}}, nil
}

// Query events from the event store
page, err := eventStore.QueryEvents(ctx, request.DebugSessionKey, request.Params.Kind, limit, offset)
if err != nil {
return nil, err
}

// Convert model.Event to API Event
var apiEvents []Event
for _, event := range page.Events {
apiEvents = append(apiEvents, Event{
Id: event.ID,
WrittenAt: event.WrittenAt,
Kind: event.Kind,
Data: event.Data,
})
}

response := EventsPage{
Events: apiEvents,
TotalCount: page.TotalCount,
HasMore: page.HasMore,
}

return GetDebugSessionEvents200JSONResponse(response), nil
}
Loading
Loading