Inspector API Hybrid Service Guide

Build inspector workflows with the new pkg/inspectorapi facade and cleanly separate UI adapters from analysis/runtime logic.

Sections

Terminology & Glossary
πŸ“– Documentation
Navigation
31 sectionsv0.1
πŸ“„ Inspector API Hybrid Service Guide β€” glaze help inspectorapi-hybrid-service-guide
inspectorapi-hybrid-service-guide

Inspector API Hybrid Service Guide

Build inspector workflows with the new pkg/inspectorapi facade and cleanly separate UI adapters from analysis/runtime logic.

Topicinspectorapiarchitecturegojatoolingsmalltalk-inspectorinspectorgoja-repl

pkg/inspectorapi is the user-facing facade for inspector workflows. It wraps reusable analysis, runtime, navigation, and tree helpers into task-oriented methods so adapters can stay thin and deterministic.

Why This Layer Exists

The hybrid layer exists to stop UI models from owning business orchestration. Before pkg/inspectorapi, command models were directly composing pkg/jsparse, pkg/inspector/analysis, and pkg/inspector/runtime calls. That worked, but the same workflow logic was duplicated in adapter code and was difficult to reuse for REST or LSP endpoints.

The facade solves this by centralizing use cases:

  • document session lifecycle (open, update, close)
  • global/member listing and declaration jumps
  • runtime global merge and REPL declaration extraction
  • source/tree sync and tree row generation

This keeps adapter responsibilities focused on interaction and rendering.

Package Overview

The package is intentionally small and explicit. contracts.go defines user-facing request/response DTOs and service errors. service.go implements use cases over the existing extracted packages.

Import path:

import "github.com/go-go-golems/go-go-goja/pkg/inspectorapi"

Core constructor:

svc := inspectorapi.NewService()

The service stores document sessions in-memory and exposes methods keyed by DocumentID.

Core Workflow

Most integrations follow a simple lifecycle: open a document, run static/rich queries, optionally merge runtime data, then close the document session. This keeps stateful context explicit and makes adapter behavior testable.

1) Open a document

resp, err := svc.OpenDocument(inspectorapi.OpenDocumentRequest{
    Filename: "sample.js",
    Source:   source,
})
if err != nil {
    return err
}

docID := resp.DocumentID
if len(resp.Diagnostics) > 0 {
    // parse diagnostics are still available even if analysis exists
}

2) List globals and members

globalsResp, err := svc.ListGlobals(inspectorapi.ListGlobalsRequest{DocumentID: docID})
if err != nil {
    return err
}

for _, g := range globalsResp.Globals {
    fmt.Printf("%s (%s)\n", g.Name, g.Kind.String())
}

membersResp, err := svc.ListMembers(inspectorapi.ListMembersRequest{
    DocumentID: docID,
    GlobalName: "Foo",
}, nil)
if err != nil {
    return err
}

3) Jump to declarations

bindingDecl, err := svc.BindingDeclarationLine(inspectorapi.BindingDeclarationRequest{
    DocumentID: docID,
    Name:       "Foo",
})
if err != nil {
    return err
}
if bindingDecl.Found {
    fmt.Printf("Foo declared at line %d\n", bindingDecl.Line)
}

4) Merge runtime globals

rt := inspectorruntime.NewSession()
_ = rt.Load(source)

declared := inspectorapi.DeclaredBindingsFromSource(`const fromRepl = 1`)
merged, err := svc.MergeRuntimeGlobals(inspectorapi.MergeRuntimeGlobalsRequest{
    DocumentID: docID,
    Declared:   declared,
}, rt)
if err != nil {
    return err
}

_ = merged

5) Close document

_ = svc.CloseDocument(inspectorapi.CloseDocumentRequest{DocumentID: docID})

API Surface Reference

The service API is designed around use cases rather than parser internals. Each method takes a typed request struct and returns a typed response struct.

Document lifecycle:

  • OpenDocument
  • OpenDocumentFromAnalysis
  • UpdateDocument
  • CloseDocument
  • Analysis

Static analysis use cases:

  • ListGlobals
  • ListMembers
  • BindingDeclarationLine
  • MemberDeclarationLine

Runtime-aware helpers:

  • DeclaredBindingsFromSource (package-level helper)
  • MergeRuntimeGlobals

Navigation/tree wrappers:

  • BuildTreeRows
  • SyncSourceToTree
  • SyncTreeToSource

Coordinate and State Conventions

A reliable integration depends on understanding coordinate and state expectations. The facade follows existing inspector conventions and keeps them explicit in request/response contracts.

  • SyncSourceToTreeRequest.CursorLine and CursorCol are 0-based.
  • BindingDeclarationLine and MemberDeclarationLine return 1-based source lines.
  • Tree sync methods operate over current Index.VisibleNodes() ordering.
  • SyncSourceToTree may expand index ancestors (ExpandTo) to maintain visibility semantics.

When building adapters, avoid ad hoc conversion logic in key handlers. Keep conversions in one boundary function.

Adapter Integration Pattern

The preferred adapter design is: state model calls service methods, then maps DTOs to view state. The model should not re-implement analysis orchestration.

Recommended split:

  • Adapter model: focus mode, key routing, view composition, and component state.
  • Service calls: globals/members/jumps/merge/sync operations.
  • View rendering: consume model state, not low-level analysis packages directly.

This is the same pattern used in the current cmd/smalltalk-inspector cutover.

Testing Strategy

pkg/inspectorapi has focused service tests in pkg/inspectorapi/service_test.go. These validate end-to-end behavior for the new facade, including runtime merge and sync wrappers.

For changes to this package, run:

go test ./pkg/inspectorapi/... -count=1
go test ./cmd/smalltalk-inspector/... -count=1
go test ./... -count=1

If you change contracts, update both service tests and adapter tests in the same commit.

Troubleshooting

ProblemCauseSolution
ErrDocumentNotFound from service callsDocument was never opened or already closedStore returned DocumentID from OpenDocument and close only when done
ListMembers returns empty on value globalsRuntime session not provided for runtime-derived membersPass *runtime.Session to ListMembers
Jump methods return Found=falseName is missing, unresolved, or runtime-onlyVerify global/member name comes from ListGlobals/ListMembers output
Source/tree sync not foundCursor line/column out of range or no node at cursorClamp cursor in adapter and retry with current source state
Runtime merge misses REPL symbolsDeclarations not extracted from snippet or runtime value undefinedUse inspectorapi.DeclaredBindingsFromSource and ensure symbol exists in runtime

Migration Notes (Clean Cutover)

If you are migrating an older adapter, remove direct usage of inspector/analysis.Session orchestration from the UI model and replace it with service calls. Do not add temporary compatibility wrappers unless explicitly required by a ticket.

Clean-cutover checklist:

  1. Add inspectorapi.Service to model state.
  2. Register/open document on file-load.
  3. Replace global/member/jump orchestration with service methods.
  4. Replace REPL declaration extraction with inspectorapi.DeclaredBindingsFromSource.
  5. Replace runtime merge call with MergeRuntimeGlobals.
  6. Update tests to construct/open document sessions via service.

See Also

  • glaze help jsparse-framework-reference
  • glaze help inspector-example-user-guide
  • goja-repl help repl-usage
  • glaze help creating-modules