v0.32.0 0617278

browser_transport.go RawHistoryFolder

1package systemviewsbridge
2
3import (
4	"bytes"
5	"encoding/json"
6	"fmt"
7	"strings"
8)
9
10type browserRequest struct {
11	messageType string
12	requestID   string
13	body        []byte
14	directAPI   bool
15}
16
17func decodeBrowserRequest(body []byte) (browserRequest, error) {
18	var envelope struct {
19		Type    string          `json:"type"`
20		Payload json.RawMessage `json:"payload"`
21	}
22	if err := json.Unmarshal(body, &envelope); err != nil {
23		return browserRequest{}, fmt.Errorf("unmarshal browser request: %w", err)
24	}
25	if envelope.Type == "" {
26		return browserRequest{}, fmt.Errorf("browser request missing type")
27	}
28
29	var payload struct {
30		RequestID string `json:"requestId"`
31	}
32	if len(envelope.Payload) != 0 {
33		if err := json.Unmarshal(envelope.Payload, &payload); err != nil {
34			return browserRequest{}, fmt.Errorf("unmarshal request payload: %w", err)
35		}
36	}
37
38	return browserRequest{
39		messageType: envelope.Type,
40		requestID:   payload.RequestID,
41		body:        body,
42		directAPI:   strings.HasPrefix(envelope.Type, "/api/"),
43	}, nil
44}
45
46func wrapDirectAPIResponse(requestID string, payload []byte) ([]byte, error) {
47	return json.Marshal(bridgeResponse{
48		RequestID: requestID,
49		Success:   true,
50		Data:      json.RawMessage(payload),
51	})
52}
53
54// normalizeBridgeShimResponse accepts the bridge shapes emitted by native and
55// fallback shims: a direct bridgeResponse envelope, a wrapper with data/error,
56// or a wrapper whose data field itself contains a bridgeResponse envelope.
57func normalizeBridgeShimResponse(requestID string, body []byte) ([]byte, error) {
58	trimmed := bytes.TrimSpace(body)
59	if len(trimmed) == 0 {
60		return nil, fmt.Errorf("empty bridge shim response")
61	}
62
63	var topLevel map[string]json.RawMessage
64	if err := json.Unmarshal(trimmed, &topLevel); err != nil {
65		return nil, fmt.Errorf("unmarshal top-level bridge response: %w", err)
66	}
67
68	_, hasSuccess := topLevel["success"]
69	_, hasRequestID := topLevel["requestId"]
70	_, hasCallback := topLevel["_callback"]
71	_, hasData := topLevel["data"]
72
73	// Native callbacks can already return the final bridgeResponse shape.
74	if (hasSuccess || hasRequestID) && !hasCallback {
75		var resp bridgeResponse
76		if err := json.Unmarshal(trimmed, &resp); err != nil {
77			return nil, fmt.Errorf("unmarshal direct bridge response: %w", err)
78		}
79		if resp.RequestID == "" {
80			// Some direct shims omit requestId; preserve correlation for callers.
81			resp.RequestID = requestID
82		}
83		return json.Marshal(resp)
84	}
85
86	var wrapped struct {
87		Data  json.RawMessage `json:"data,omitempty"`
88		Error string          `json:"error,omitempty"`
89	}
90	if err := json.Unmarshal(trimmed, &wrapped); err != nil {
91		return nil, fmt.Errorf("unmarshal wrapped bridge response: %w", err)
92	}
93	if wrapped.Error != "" {
94		// Fetch-style wrappers report transport errors outside the data payload.
95		return json.Marshal(bridgeResponse{RequestID: requestID, Error: wrapped.Error})
96	}
97	if !hasData {
98		// Wrapped shim responses must carry either data or a top-level error.
99		return nil, fmt.Errorf("wrapped bridge response missing data")
100	}
101	if len(wrapped.Data) == 0 {
102		return json.Marshal(bridgeResponse{RequestID: requestID, Success: true})
103	}
104
105	var nestedFields map[string]json.RawMessage
106	if err := json.Unmarshal(wrapped.Data, &nestedFields); err == nil {
107		nestedSuccess, nestedHasSuccess := nestedFields["success"]
108		_, nestedHasRequestID := nestedFields["requestId"]
109		_, nestedHasError := nestedFields["error"]
110		successIsBool := false
111		if nestedHasSuccess {
112			var successValue bool
113			successIsBool = json.Unmarshal(nestedSuccess, &successValue) == nil
114		}
115		hasNestedEnvelope := successIsBool || (nestedHasRequestID && nestedHasError)
116		if hasNestedEnvelope {
117			var nested bridgeResponse
118			if err := json.Unmarshal(wrapped.Data, &nested); err == nil {
119				if nested.RequestID == "" {
120					// Nested envelopes from direct API wrappers also need caller correlation.
121					nested.RequestID = requestID
122				}
123				return json.Marshal(nested)
124			}
125		}
126	}
127
128	return json.Marshal(bridgeResponse{
129		RequestID: requestID,
130		Success:   true,
131		Data:      wrapped.Data,
132	})
133}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close