Documentation
¶
Overview ¶
Package vabc is a small, dependency-light client for Virginia ABC (Virginia Alcoholic Beverage Control) — live product search, per-store inventory, the statewide warehouse, the limited-availability ("lottery") hook, and the store locator.
It is the importable core of the vabc CLI: the command-line tool in cmd/vabc is one consumer of this package, so anything the CLI can do is also available to Go programs via the Client interface.
Design notes:
- Everything is plain HTTP+JSON against undocumented but public, unauthenticated endpoints. No auth, no secrets, no heavy dependencies — `go get` pulls a tiny HTTP+JSON client only.
- Product search (SearchProducts) queries the site's Coveo index, which covers the full web catalog; results carry the 6-digit inventory product code.
The endpoints are reverse-engineered and may change without notice; pin behavior to this package's typed contract.
Index ¶
Constants ¶
const ( // DefaultBaseURL hosts the /webapi/inventory/* and /webapi/limitedavailability/* routes. DefaultBaseURL = "https://www.abc.virginia.gov" // DefaultStoresURL is the Virginia VGIN ArcGIS FeatureServer for the store locator. DefaultStoresURL = "https://services9.arcgis.com/6EuFgO4fLTqfNOhu/arcgis/rest/services/Virginia_ABC_Stores/FeatureServer/0/query" // DefaultUserAgent identifies the tool politely to an undocumented backend. DefaultUserAgent = "vabc (+https://github.com/rnwolfe/vabc)" )
Endpoint defaults. The inventory routes are undocumented but public, unauthenticated, and (today) exempt from the site's Cloudflare challenge.
const SchemaVersion = 1
SchemaVersion is the output-contract version reported by `vabc schema --json`. Bump only on a breaking change to the JSON field contracts in this package.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type APIError ¶
type APIError struct {
Kind ErrKind
Status int // HTTP status, when applicable (0 otherwise)
Msg string // human-readable summary
RetryAfter time.Duration // for KindRateLimited: suggested wait before retrying
Err error // wrapped cause, if any
}
APIError is a typed, classified error from the Virginia ABC client.
func (*APIError) RetryAfterSeconds ¶
RetryAfterSeconds returns the suggested retry delay in whole seconds (0 if none), for surfacing to an agent that wants to schedule a retry.
type Client ¶
type Client interface {
// StoreNearby returns the anchor store's stock of a product plus other nearby
// stores that carry it, ranked by distance (/webapi/inventory/storeNearby).
StoreNearby(ctx context.Context, storeNumber int, productCode string) (InventoryResult, error)
// MyStore returns just one store's stock of a product (/webapi/inventory/mystore).
MyStore(ctx context.Context, storeNumber int, productCode string) (StoreStock, error)
// Warehouse returns statewide central-warehouse stock (/webapi/inventory/store).
Warehouse(ctx context.Context, productCode string) (WarehouseResult, error)
// Stores returns all Virginia ABC retail stores (ArcGIS FeatureServer).
Stores(ctx context.Context) ([]Store, error)
// StoreNear returns stores nearest a point, ranked by distance.
StoreNear(ctx context.Context, lat, lng float64, limit int) ([]Store, error)
// LimitedAvailability returns the lottery/allocated event hook for a product
// (/webapi/limitedavailability/eventLinks).
LimitedAvailability(ctx context.Context, productCode string) (LotteryResult, error)
// SearchProducts runs a live product search against the site's Coveo index,
// which covers the full web catalog (more complete and current than the
// downloadable price list). Results carry the inventory product code.
SearchProducts(ctx context.Context, query string, limit int) ([]Product, error)
}
Client is the live Virginia ABC API surface. All methods are reads; there are no mutations. Implementations are safe for an agent's fresh-process-per-call usage (see the throttle).
type Envelope ¶
type Envelope struct {
SchemaVersion int `json:"schemaVersion"`
Scope string `json:"scope,omitempty"`
Data any `json:"data"`
NextCursor string `json:"nextCursor,omitempty"`
}
Envelope is the stable response wrapper for in-band scope/version metadata. SchemaVersion lets agents detect contract changes; Scope declares partial or cached results (e.g. "catalog snapshot 2026-06-01; live inventory") so a caller never mistakes a cached catalog for live stock.
type ErrKind ¶
type ErrKind string
ErrKind classifies an API failure so the CLI can map it to a stable exit code without parsing messages. Importers can switch on it too.
const ( // KindNotFound: the target reports the resource does not exist (e.g. an invalid // store number returns HTTP 400 "No Store exists ..."). KindNotFound ErrKind = "not_found" // KindRateLimited: throttled or blocked (HTTP 429, a WAF challenge, or the local // circuit-breaker). RetryAfter, when set, says how long to wait. KindRateLimited ErrKind = "rate_limited" // KindRetryable: a transient upstream/network error (5xx, timeout) worth retrying. KindRetryable ErrKind = "retryable" // KindSchemaDrift: the response did not match the expected shape — an upstream // change, surfaced explicitly instead of as a decode panic. KindSchemaDrift ErrKind = "schema_drift" )
type InventoryResult ¶
type InventoryResult struct {
ProductCode string `json:"productCode"`
Store StoreStock `json:"store"`
NearbyStores []StoreStock `json:"nearbyStores"`
}
InventoryResult is the per-store availability of a product, with nearby stores that also stock it, ranked by distance (the /webapi/inventory/storeNearby shape).
type LotteryEvent ¶
type LotteryEvent struct {
Title string `json:"title,omitempty"`
URL string `json:"url,omitempty"`
}
LotteryEvent is one limited-availability event link. Its text/URL are CMS-authored free text — treat as untrusted (the CLI fences it in agent mode).
type LotteryResult ¶
type LotteryResult struct {
ProductCode string `json:"productCode"`
Allocated bool `json:"allocated"`
Active bool `json:"active"`
EventLinks []LotteryEvent `json:"eventLinks"`
}
LotteryResult is the limited-availability ("lottery") status for a product. Allocated comes from the catalog flag; Active/EventLinks from the live hook.
type Option ¶
type Option func(*httpClient)
Option configures the HTTP client.
func WithBaseURL ¶
WithBaseURL overrides the inventory/lottery host.
func WithHTTPClient ¶
WithHTTPClient injects a custom *http.Client (e.g. for tests or a tuned transport).
func WithMinInterval ¶
WithMinInterval sets the minimum spacing between requests (politeness throttle).
func WithStatePath ¶
WithStatePath overrides the throttle state file (mainly for tests).
func WithStoresURL ¶
WithStoresURL overrides the ArcGIS store-locator endpoint.
func WithUserAgent ¶
WithUserAgent overrides the request User-Agent.
type Product ¶
type Product struct {
ProductCode string `json:"productCode"`
Name string `json:"name"`
Category string `json:"category,omitempty"`
Type string `json:"type,omitempty"`
Proof *float64 `json:"proof,omitempty"`
Size string `json:"size,omitempty"`
RetailPrice *float64 `json:"retailPrice,omitempty"`
DiscountPrice *float64 `json:"discountPrice,omitempty"`
Allocated bool `json:"allocated"`
OnlineOrderable bool `json:"onlineOrderable"`
New bool `json:"new"`
UPC []string `json:"upc,omitempty"`
URL string `json:"url,omitempty"`
}
Product is a catalog record, keyed by ProductCode (6-digit, zero-padded). Populated from the catalog snapshot, not from a live API. Optional numeric fields are pointers so "unknown" is distinct from zero.
type Store ¶
type Store struct {
StoreNumber int `json:"storeNumber"`
Name string `json:"name,omitempty"`
Address string `json:"address,omitempty"`
Address1 string `json:"address1,omitempty"`
Address2 string `json:"address2,omitempty"`
City string `json:"city,omitempty"`
State string `json:"state,omitempty"`
Zip string `json:"zip,omitempty"`
Phone string `json:"phone,omitempty"`
Lat float64 `json:"lat,omitempty"`
Lng float64 `json:"lng,omitempty"`
Hours string `json:"hours,omitempty"`
ShoppingCenter string `json:"shoppingCenter,omitempty"`
URL string `json:"url,omitempty"`
// Distance in miles from a query point, when relevant. A pointer so a genuine
// 0.0 (the nearest store) renders, while "not computed" is omitted (not null=0).
Distance *float64 `json:"distance,omitempty"`
}
Store is a Virginia ABC retail store. StoreNumber is the small integer ABC store number (the locator's "ABC Store 088" → 88), which is also the value the inventory API expects. Distance is miles from a query point, when relevant.
type StoreStock ¶
StoreStock is a store plus the on-hand quantity of a specific product.
type WarehouseResult ¶
type WarehouseResult struct {
ProductCode string `json:"productCode"`
WarehouseInventory int `json:"warehouseInventory"`
}
WarehouseResult is the statewide central-warehouse stock for a product.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
vabc
command
Command vabc is the agent-cli-factory Go reference implementation.
|
Command vabc is the agent-cli-factory Go reference implementation. |
|
internal
|
|
|
cli
Package cli wires the kong grammar, the runtime, and the exit-code mapping.
|
Package cli wires the kong grammar, the runtime, and the exit-code mapping. |
|
errs
Package errs defines the stable exit-code table and the structured CLI error type.
|
Package errs defines the stable exit-code table and the structured CLI error type. |
|
geocode
Package geocode resolves a location string (a "lat,lng" pair, a 5-digit ZIP, or a street address) into coordinates, so distances can be measured from the user's actual location rather than snapped to a store.
|
Package geocode resolves a location string (a "lat,lng" pair, a 5-digit ZIP, or a street address) into coordinates, so distances can be measured from the user's actual location rather than snapped to a store. |
|
output
Package output is the single output contract: data to stdout, human chatter to stderr, stable JSON, --format json|plain|tsv, --select projection, and --limit bounding.
|
Package output is the single output contract: data to stdout, human chatter to stderr, stable JSON, --format json|plain|tsv, --select projection, and --limit bounding. |
|
skill
Package skill embeds the agent-facing SKILL.md into the binary so the `agent` subcommand can print it with no repo or network access.
|
Package skill embeds the agent-facing SKILL.md into the binary so the `agent` subcommand can print it with no repo or network access. |