@gambar/bearvision-sdk (0.2.11)
Installation
@gambar:registry=npm install @gambar/bearvision-sdk@0.2.11"@gambar/bearvision-sdk": "0.2.11"About this package
BearVision — Telemetry SDK
A lightweight observability SDK: error tracking, analytics, and performance metrics in one protobuf-first pipeline.
Quick start
Go
# ~/.netrc — one-time setup (replace <token> with your Forgejo token)
echo "machine git.gamriel.com login <user> password <token>" >> ~/.netrc
# Tell Go to use the private proxy + skip checksum DB
go env -w GOPROXY=https://git.gamriel.com/api/packages/GamBar/go/,https://proxy.golang.org,direct
go env -w GONOSUMCHECK=git.gamriel.com
go env -w GONOSUMDB=git.gamriel.com
go get git.gamriel.com/GamBar/BearVision-Proto/sdk/go@latest
import "git.gamriel.com/GamBar/BearVision-Proto/sdk/go"
client := bearvision.New("my-project", "sk-abc123", "https://ingest.example.com/events")
client.Error("nil pointer", "stack trace...")
client.Track("page_view", nil)
client.Track("signup", map[string]string{"plan": "pro"})
client.Perf("db_query", 42.5)
client.Track("click", map[string]string{"label": "signup"}, map[string]string{"experiment": "A"})
JS/TS
npm config set @gambar:registry https://git.gamriel.com/api/packages/GamBar/npm/
npm config set //git.gamriel.com/api/packages/GamBar/npm/:_authToken=your_token
npm install @gambar/bearvision-sdk
import { BearVisionClient, Level } from "@gambar/bearvision-sdk";
const client = new BearVisionClient({
projectId: "my-project",
apiKey: "sk-abc123",
endpoint: "https://ingest.example.com/events",
});
await client.error("nil pointer", "stack trace...");
await client.error("crash", "stack...", Level.WARNING, { env: "staging" });
await client.track("page_view", { source: "email" });
await client.perf("page_load", 1234.5);
// Optional bag as last arg:
await client.track("click", { label: "signup" }, { experiment: "A" });
// Get server-echoed ID from errors:
const id = await client.error("crash", "at line 42");
Script embed — zero config
<script
src="https://track.gamriel.com/scripts/bv.js"
data-project="my-project"
data-key="sk-abc123"
data-endpoint="https://ingest.example.com/events"
></script>
Captures errors, page views, web vitals automatically. Manual API:
<script>
bv.track("button_click");
bv.track("signup", { plan: "premium" });
bv.error("it broke");
bv.error(new Error("network failure"), { component: "FormField" });
bv.perf("custom_api_call", 342);
</script>
Go SDK
Creating a client
// Minimum config — endpoint is required.
client := bearvision.New("my-project", "sk-abc123", "https://ingest.example.com/events")
// With custom options:
client := bearvision.New("my-project", "sk-abc123", "https://ingest.example.com/events")
client.Encoding = bearvision.EncodingJSON // default: binary protobuf
client.HTTPClient = &http.Client{Timeout: 5 * time.Second}
Sending events
// Error — message, stacktrace, level, optional bag
client.Error("nil pointer dereference", "at invoice.go:213", bearvision.Level_ERROR)
client.Error("crash", "stack...", bearvision.Level_WARNING, map[string]string{"env": "staging"})
// Track — action, optional data, optional bag
client.Track("page_view", nil)
client.Track("signup", map[string]string{"plan": "pro"}, map[string]string{"experiment": "A"})
// Perf — metric, value, optional unit (default "ms")
client.Perf("db_query", 42.5)
client.Perf("page_load", 1230.0, "s")
All three auto-generate an ID and timestamp. Error() returns the server-echoed event ID for cross-referencing:
id, err := client.Error("crash", "at line 42")
// id is confirmed by the server — use it to look up the event later
Track() and Perf() return only an error. Use client.Send(ctx, evt) for advanced use cases (context, client info, custom error levels).
Advanced usage
For context, bag, client info, or custom error levels, build an Event directly:
evt := &bearvision.Event{
Id: myID,
Timestamp: time.Now().UnixMilli(),
Context: &bearvision.Context{
Environment: "production",
Release: "v2.14.3",
SessionId: "sess_abc123",
},
Client: &bearvision.ClientInfo{
Browser: "Chrome 124",
Country: "US",
},
Bag: map[string]string{"experiment": "new-checkout"},
Payload: &bearvision.Event_Error{
Error: &bearvision.ErrorEvent{
Message: "crash",
Stacktrace: "goroutine 1 [running]:\nmain.go:42",
ErrorType: "RuntimePanic",
Level: bearvision.Level_FATAL,
},
},
}
client.Send(context.Background(), evt)
Server — decoding events
import (
bv "git.gamriel.com/GamBar/BearVision-Proto/sdk/go"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
)
func eventsHandler(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
var evt bv.Event
switch r.Header.Get("Content-Type") {
case "application/json":
protojson.Unmarshal(body, &evt)
default:
proto.Unmarshal(body, &evt)
}
if evt.Id == "" {
http.Error(w, "missing id", 400)
return
}
// Determine event type from the oneof payload
switch {
case evt.GetError() != nil:
// handle error
case evt.GetAnalytics() != nil:
// handle analytics
case evt.GetPerformance() != nil:
// handle performance
}
}
JS/TS SDK
Creating a client
const client = new BearVisionClient({
projectId: "my-project",
apiKey: "sk-abc123",
endpoint: "https://ingest.example.com/events",
encoding: "binary", // or "json" (default: "binary")
});
Sending events
// Error with optional stacktrace, level, and bag
await client.error("nil pointer", "at line 42");
await client.error("crash", "stack...", Level.WARNING, { env: "staging" });
// Track with optional data and bag
await client.track("page_view", { source: "email" });
await client.track("click", { label: "signup" }, { experiment: "A" });
// Perf with optional unit and bag
await client.perf("page_load", 1234.5);
await client.perf("db_query", 42.0, "ms", { db: "postgres" });
All three auto-generate an ID and timestamp. client.error() returns the server-echoed event ID:
const id = await client.error("crash", "at line 42");
// id is confirmed by the server — use it to look up the event later
client.track() and client.perf() return Promise<void>. Use client.send() for advanced cases
(it also returns the echoed event ID):
import { create } from "@bufbuild/protobuf";
import { EventSchema, ContextSchema, ClientInfoSchema, ErrorEventSchema, Level } from "@gambar/bearvision-sdk";
const evt = create(EventSchema, {
context: create(ContextSchema, { environment: "production", release: "v2.14.3" }),
client: create(ClientInfoSchema, { browser: "Chrome 124", country: "US" }),
bag: { experiment: "new-checkout" },
payload: { case: "error", value: create(ErrorEventSchema, { message: "crash", level: Level.FATAL }) },
});
await client.send(evt);
Script embed — zero-config for websites
One <script> tag. No build step, no npm. 3.6 KB minified.
<script
src="https://track.gamriel.com/scripts/bv.js"
data-project="my-project"
data-key="sk-abc123"
data-endpoint="https://ingest.example.com/events"
></script>
What it captures automatically
| Signal | Event | Details |
|---|---|---|
window.onerror |
ErrorEvent |
message, stacktrace, file, line, column |
unhandledrejection |
ErrorEvent |
reason message + stack |
| Navigation timing | PerformanceEvent |
domComplete, domInteractive, ttfb, dnsTime, tcpTime, tlsTime, total |
| First contentful paint | PerformanceEvent |
FCP value in ms |
| Largest contentful paint | PerformanceEvent |
LCP value (2s timeout fallback) |
| Page view | AnalyticsEvent |
path, title, url, referrer |
Manual API
<script>
// Track — action + optional data (→ AnalyticsEvent.data)
bv.track("button_click");
bv.track("signup", { plan: "premium", source: "banner" });
// Error — string or Error + optional bag (→ Event.bag), returns server-echoed event ID
const id = await bv.error("it broke");
bv.error(new Error("network failure"));
bv.error("validation failed", { component: "FormField", field: "email" });
// Perf — metric + value (unit is always "ms")
bv.perf("custom_api_call", 342);
// Debug: set window.__bvOnError to receive send failures
window.__bvOnError = (err) => console.error("[BearVision]", err);
</script>
Proto reference
The single proto/event.proto file defines the wire format:
Event — envelope (id, project, timestamp, context, client, bag)
├── ErrorEvent — crashes, exceptions, stack traces
├── AnalyticsEvent — page views, button clicks, behavioural signals
└── PerformanceEvent — latencies, db timings, web vitals
EventResponse — server response (echoes the event id for cross-referencing)
Servers must respond with EventResponse { string id = 1 } using the same Content-Type as the request. The SDKs parse the echoed ID — client.Error() and bv.error() return it, confirming the event was received.
Key design decisions:
- No PII — no IP, user agent, cookies, or visitor IDs. Privacy-first by default.
- Client-side parsing —
ClientInfoholds parsed values (browser, OS, device), never raw user-agent strings. - Server-side fingerprinting — errors are grouped by fingerprint computed on the server, so clients can't poison grouping.
- No EventType enum — the event type is determined by which
oneof payloadis set, eliminating enum/oneof drift. - Flat bag — a single
map<string, string>on Event for arbitrary attributes, instead of a nested Metadata wrapper.
Edit proto/event.proto → run just proto → Go + JS stubs regenerate automatically.
Development
just proto # regenerate Go + JS protobuf stubs
just test # proto → typecheck → Go tests → JS tests → sync check
just typecheck # TypeScript checking only
just check-sync # verify Go and JS SDK method signatures are in sync
just bundle # build embed script (minified) from src/embed.ts
just all # proto + typecheck + tests + bundle
Layout
proto/ # Protobuf schema
event.proto
sdk/go/ # Go SDK
client.go # BearVisionClient + Error/Track/Perf
event.go # Event constructors
event.pb.go # generated protobuf types
sdk/js/ # JS/TS SDK
src/client.ts # BearVisionClient (npm package)
src/embed.ts # embed entrypoint (window.bv)
src/proto/event_pb.js # generated protobuf types
dist/bv.js # bundled embed script (minified)
Dependencies
Development dependencies
| ID | Version |
|---|---|
| @bufbuild/protobuf | ^2.12.1 |
| jsdom | ^29.1.1 |
| rolldown | ^1.1.3 |
| ts-patch | ^3.3.0 |
| typescript | ^5.8.0 |
| vitest | ^3.0.0 |