diff --git a/site/pro-reference-baseline.json b/site/pro-reference-baseline.json index 767eac61..43f68284 100644 --- a/site/pro-reference-baseline.json +++ b/site/pro-reference-baseline.json @@ -1,6 +1,6 @@ { "module": "echo.labstack.com/pro", - "version": "v0.1.0", + "version": "v0.2.0", "configs": { "admin.Config": [ { @@ -1113,7 +1113,7 @@ }, { "name": "Mode", - "type": "ExecutionMode", + "type": "Mode", "doc": "Mode empty selects ExternalMode; TransactionalMode is explicit.", "default": "Mode empty selects ExternalMode; TransactionalMode is explicit." } @@ -2783,7 +2783,7 @@ "quota.MiddlewareConfig.ToMiddleware": "func (cfg MiddlewareConfig) ToMiddleware() (echo.MiddlewareFunc, error)", "quota.New": "func New(cfg Config) (*Limiter, error)", "quota.NewMemoryStore": "func NewMemoryStore(cfg MemoryConfig) *MemoryStore", - "quota.NewTopK": "func NewTopK(k int) *TopK", + "quota.NewTopK": "func NewTopK(k int) (*TopK, error)", "quotaadmin.New": "func New(cfg Config) (*Panel, error)", "quotaadmin.NewDenialRecorder": "func NewDenialRecorder(size int) (*DenialRecorder, error)", "quotamcp.MiddlewareConfig.ToMiddleware": "func (c MiddlewareConfig) ToMiddleware() (mcp.Middleware, error)", diff --git a/site/pro-source/config-fields.json b/site/pro-source/config-fields.json index 21ebeb1d..3f14c769 100644 --- a/site/pro-source/config-fields.json +++ b/site/pro-source/config-fields.json @@ -1,6 +1,6 @@ { "module": "echo.labstack.com/pro", - "revision": "v0.1.0", + "revision": "v0.2.0", "configs": [ { "name": "admin.Config", @@ -669,7 +669,7 @@ "name": "auth.APIKey", "file": "auth/apikey.go", "line": 205, - "doc": "APIKey configures a key-store scheme. Header sets an optional alternative to\nAuthorization: ApiKey. Supplying credentials in both headers is rejected.\nLookupTimeout defaults to 2s (1ms–30s). The store stays caller-owned and must honor\ncancellation. No goroutines start. Construction copies the pepper ring and key bytes.\nCancellation and lookup failures return a sanitized *Failure without exposing store or\ncontext errors.", + "doc": "APIKey configures a key-store scheme. Header sets an optional alternative to\nAuthorization: ApiKey. Supplying credentials in both headers is rejected.\nLookupTimeout defaults to 2s (1ms–30s). The store stays caller-owned and must honor\ncancellation. No goroutines start. Construction copies the pepper ring and key bytes.\nCancellation and lookup failures return a sanitized *FailureError without exposing store or\ncontext errors.", "fields": [ { "name": "Namespace", @@ -1230,7 +1230,7 @@ { "name": "execution.Limits", "file": "execution/types.go", - "line": 87, + "line": 88, "doc": "Limits are immutable per namespace; zero values select documented defaults.", "fields": [ { @@ -1238,35 +1238,35 @@ "type": "int", "doc": "MaxResultBytes defaults to 64 KiB; valid range 1 byte–1 MiB.", "default": "MaxResultBytes defaults to 64 KiB; valid range 1 byte–1 MiB.", - "line": 87 + "line": 88 }, { "name": "MaxUnresolvedPerTenant", "type": "int", "doc": "MaxUnresolvedPerTenant defaults to 1000 and must be positive.", "default": "MaxUnresolvedPerTenant defaults to 1000 and must be positive.", - "line": 87 + "line": 88 }, { "name": "MaxNamespaceRecords", "type": "int64", "doc": "MaxNamespaceRecords defaults to 5000000 and must be positive.", "default": "MaxNamespaceRecords defaults to 5000000 and must be positive.", - "line": 87 + "line": 88 }, { "name": "MaxNamespaceBytes", "type": "int64", "doc": "MaxNamespaceBytes defaults to 20 GiB of logical charges and must be positive.", "default": "MaxNamespaceBytes defaults to 20 GiB of logical charges and must be positive.", - "line": 87 + "line": 88 }, { "name": "BudgetShards", "type": "int", "doc": "BudgetShards defaults to 16; valid range 1–256.", "default": "BudgetShards defaults to 16; valid range 1–256.", - "line": 87 + "line": 88 } ] }, @@ -1372,7 +1372,7 @@ { "name": "execution.Policy", "file": "execution/types.go", - "line": 77, + "line": 78, "doc": "Policy declares effects and their safety rules. New owns copied slices. Zero tunables\nselect Lease=1 minute, ResultRetention=24 hours, KeyRetention=7 days. Effects require\nexplicit timeouts. Backend selection comes only from the injected Store; a policy\ncarries no storage selector. An empty fingerprint selects http/v1; nil Headers selects\nIf-Match, If-None-Match, If-Modified-Since, If-Unmodified-Since, Content-Encoding and\nContent-Language, while an explicit empty slice selects none. An empty Mode selects\nExternalMode; TransactionalMode requires no effects, a zero Lease and an empty\nReconcilerVersion.", "fields": [ { @@ -1380,63 +1380,63 @@ "type": "string", "doc": "SafetyVersion is required and versions the operation's safety rules.", "default": "SafetyVersion is required and versions the operation's safety rules.", - "line": 77 + "line": 78 }, { "name": "Scope", "type": "string", "doc": "Scope empty defaults to the configured operation name in Journal.New.", "default": "Scope empty defaults to the configured operation name in Journal.New.", - "line": 77 + "line": 78 }, { "name": "ReconcilerVersion", "type": "string", "doc": "ReconcilerVersion is required for external mode and empty for transactional mode.", "default": "ReconcilerVersion is required for external mode and empty for transactional mode.", - "line": 77 + "line": 78 }, { "name": "Effects", "type": "[]Effect", "doc": "Effects requires 1–8 entries in external mode; transactional mode requires none.", "default": "Effects requires 1–8 entries in external mode; transactional mode requires none.", - "line": 77 + "line": 78 }, { "name": "Fingerprint", "type": "FingerprintSpec", "doc": "Fingerprint at zero selects http/v1 with its default header set.", "default": "Fingerprint at zero selects http/v1 with its default header set.", - "line": 77 + "line": 78 }, { "name": "Lease", "type": "time.Duration", "doc": "Lease defaults to 1m in external mode; transactional mode requires zero.", "default": "Lease defaults to 1m in external mode; transactional mode requires zero.", - "line": 77 + "line": 78 }, { "name": "ResultRetention", "type": "time.Duration", "doc": "ResultRetention defaults to 24h and must be positive.", "default": "ResultRetention defaults to 24h and must be positive.", - "line": 77 + "line": 78 }, { "name": "KeyRetention", "type": "time.Duration", "doc": "KeyRetention defaults to 7 days and must be at least ResultRetention.", "default": "KeyRetention defaults to 7 days and must be at least ResultRetention.", - "line": 77 + "line": 78 }, { "name": "Mode", - "type": "ExecutionMode", + "type": "Mode", "doc": "Mode empty selects ExternalMode; TransactionalMode is explicit.", "default": "Mode empty selects ExternalMode; TransactionalMode is explicit.", - "line": 77 + "line": 78 } ] }, @@ -2787,7 +2787,7 @@ { "name": "stripeadmin.Config", "file": "quota/quotastripe/stripeadmin/panel.go", - "line": 37, + "line": 36, "doc": "Config selects the caller-owned status reader. Status and optional readers\nare retained for the panel lifetime, never closed; all must be concurrency\nsafe and honor cancellation.", "fields": [ { @@ -2795,63 +2795,63 @@ "type": "StatusReader", "doc": "Status is required, retained and caller-owned; it must honor ctx and\nbe concurrency safe.", "default": "Status is required, retained and caller-owned; it must honor ctx and be concurrency safe.", - "line": 40 + "line": 39 }, { "name": "Inspection", "type": "InspectionReader", "doc": "Inspection enables bounded shared-store diagnostic views. Nil omits them.", "default": "Nil omits them.", - "line": 42 + "line": 41 }, { "name": "Reconcile", "type": "ReconcileReader", "doc": "Reconcile enables a probe that compares exported totals with Stripe usage. Nil omits\nthe action.", "default": "Nil omits the action.", - "line": 45 + "line": 44 }, { "name": "Flush", "type": "FlushRunner", "doc": "Flush enables an action that seals and dispatches eligible usage through the local\ndispatcher. The request is saved durably for retries. Nil omits the action.", "default": "Nil omits the action.", - "line": 48 + "line": 47 }, { "name": "Budget", "type": "quotastripe.InspectionBudget", "doc": "Budget bounds inspection; zero uses quotastripe defaults.", "default": "Budget bounds inspection; zero uses quotastripe defaults.", - "line": 50 + "line": 49 }, { "name": "StripePages", "type": "int", "doc": "StripePages defaults to 10 and must be 1–100. Shared across Probe retries.", "default": "StripePages defaults to 10 and must be 1–100.", - "line": 52 + "line": 51 }, { "name": "ID", "type": "string", "doc": "ID defaults to stripe; use 1–32 lowercase ASCII letters, digits or\nhyphens, starting with a letter, excluding api and assets.", "default": "ID defaults to stripe; use 1–32 lowercase ASCII letters, digits or hyphens, starting with a letter, excluding api and assets.", - "line": 55 + "line": 54 }, { "name": "BacklogAlarm", "type": "time.Duration", "doc": "BacklogAlarm is a display threshold; defaults to 30m, nonzero values\nmust be positive.", "default": "BacklogAlarm is a display threshold; defaults to 30m, nonzero values must be positive.", - "line": 58 + "line": 57 }, { "name": "Now", "type": "func() time.Time", "doc": "Now is retained and must be concurrency safe; nil uses time.Now.", "default": "Now is retained and must be concurrency safe; nil uses time.Now.", - "line": 60 + "line": 59 } ] }, @@ -3722,9 +3722,9 @@ }, { "name": "quota.NewTopK", - "signature": "func NewTopK(k int) *TopK", + "signature": "func NewTopK(k int) (*TopK, error)", "file": "quota/topk.go", - "line": 62 + "line": 63 }, { "name": "quotaadmin.New", @@ -3808,7 +3808,7 @@ "name": "stripeadmin.New", "signature": "func New(cfg Config) (*Panel, error)", "file": "quota/quotastripe/stripeadmin/panel.go", - "line": 68 + "line": 67 }, { "name": "tenant.AuthorizeConfig.ToMiddleware", diff --git a/site/pro-source/examples/admin-demo/main.go b/site/pro-source/examples/admin-demo/main.go index 4f81124a..c7515440 100644 --- a/site/pro-source/examples/admin-demo/main.go +++ b/site/pro-source/examples/admin-demo/main.go @@ -31,6 +31,13 @@ import ( ) func main() { + if err := run(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func run() error { // Do not put the URL in flag defaults: -help must never disclose credentials. var databaseURL string flag.StringVar( @@ -46,13 +53,13 @@ func main() { } ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() - if err := run(ctx, databaseURL, *addr); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) + if err := runWithContext(ctx, databaseURL, *addr); err != nil { + return err } + return nil } -func run(ctx context.Context, databaseURL, addr string) error { +func runWithContext(ctx context.Context, databaseURL, addr string) error { if databaseURL == "" { return errors.New( "provide -postgres-url or ADMIN_DEMO_POSTGRES_URL; use the " + @@ -121,7 +128,7 @@ func run(ctx context.Context, databaseURL, addr string) error { Maintenance: prefix + "_maintenance", Reader: prefix + "_reader", } - if err = createRoles(ctx, pool, roles); err != nil { + if err := createRoles(ctx, pool, roles); err != nil { return err } } @@ -247,20 +254,20 @@ func run(ctx context.Context, databaseURL, addr string) error { Origins: []string{"http://" + demoBrowserAuthority(authority)}, } //nolint:contextcheck // Mount registers routes; request contexts originate in Echo. - if err = cfg.Mount(e, "/admin"); err != nil { + if err := cfg.Mount(e, "/admin"); err != nil { return err } // executionadmin binds one namespace. A second console exposes the other. cfg.Panels = []admin.Panel{qp, sp, op, fp, wp, ap} //nolint:contextcheck // Mount registers routes; request contexts originate in Echo. - if err = cfg.Mount(e, "/admin-fulfillment"); err != nil { + if err := cfg.Mount(e, "/admin-fulfillment"); err != nil { return err } - if err = mountSamples(ctx, e, lim, op, append(records, fulfillment...)); err != nil { + if err := mountSamples(ctx, e, lim, op, append(records, fulfillment...)); err != nil { return err } for _, mount := range []string{"/admin", "/admin-fulfillment"} { - if err = admin.VerifyRoutes(e, mount); err != nil { + if err := admin.VerifyRoutes(e, mount); err != nil { return err } } diff --git a/site/pro-source/examples/admin-demo/quota.go b/site/pro-source/examples/admin-demo/quota.go index 89813a17..ea59ed2d 100644 --- a/site/pro-source/examples/admin-demo/quota.go +++ b/site/pro-source/examples/admin-demo/quota.go @@ -17,7 +17,10 @@ func seedQuota(ctx context.Context) (*quota.Limiter, *quotaadmin.Panel, error) { if err != nil { return nil, nil, err } - top := quota.NewTopK(10) + top, err := quota.NewTopK(10) + if err != nil { + return nil, nil, err + } //nolint:contextcheck // Construction has no context API; no Outbox is configured. lim, err := quota.New(quota.Config{Store: quota.NewMemoryStore(quota.MemoryConfig{}), Hooks: quota.JoinHooks(top.Hooks(), denials.Hooks()), Plans: []quota.Plan{ {ID: "team", Limits: []quota.Limit{quota.PerDay("daily", 100).Meter("requests"), quota.Credits("credits").Meter("tokens")}}, diff --git a/site/pro-source/examples/admin-demo/reports.go b/site/pro-source/examples/admin-demo/reports.go index e1e13bdb..66224991 100644 --- a/site/pro-source/examples/admin-demo/reports.go +++ b/site/pro-source/examples/admin-demo/reports.go @@ -83,7 +83,7 @@ func seedStripe(ctx context.Context) (*stripeadmin.Panel, error) { } for i := range 24 { event := quota.ConsumptionEvent{ID: fmt.Sprintf("offline-usage-%03d", i), Identity: quota.Identity{Tenant: demoTenants[i%4]}, Plan: "team", Meter: "tokens", Amount: int64(80 + i*19), At: time.Now().UTC().Add(-time.Duration(i*11+1) * time.Minute).Truncate(time.Millisecond)} - if err = exporter.Deliver(ctx, quota.Event{ID: event.ID, Consumption: &event}); err != nil { + if err := exporter.Deliver(ctx, quota.Event{ID: event.ID, Consumption: &event}); err != nil { return nil, err } } diff --git a/site/pro-source/examples/admin-demo/webhook.go b/site/pro-source/examples/admin-demo/webhook.go index af8e5dbf..4c2d92ee 100644 --- a/site/pro-source/examples/admin-demo/webhook.go +++ b/site/pro-source/examples/admin-demo/webhook.go @@ -124,7 +124,7 @@ func seedWebhooks(ctx context.Context, pool *pgxpool.Pool, schema string) (resul if admitErr != nil { return nil, admitErr } - if err = webhookSnapshot(ctx, pool, schema, receipt.Ref, direction, state); err != nil { + if err := webhookSnapshot(ctx, pool, schema, receipt.Ref, direction, state); err != nil { return nil, err } } diff --git a/site/pro-source/examples/admin/main.go b/site/pro-source/examples/admin/main.go index e962754d..fd310b08 100644 --- a/site/pro-source/examples/admin/main.go +++ b/site/pro-source/examples/admin/main.go @@ -23,7 +23,7 @@ import ( "echo.labstack.com/pro/quota/quotaadmin" ) -func run(ctx context.Context) error { +func runWithContext(ctx context.Context) error { //nolint:contextcheck // Construction has no context API; no Outbox is configured. l, err := quota.New(quota.Config{Store: quota.NewMemoryStore( quota.MemoryConfig{}, @@ -75,10 +75,10 @@ func run(ctx context.Context) error { }, nil }} //nolint:contextcheck // Mount only registers routes; Echo supplies request contexts. - if err = cfg.Mount(e, prefix); err != nil { + if err := cfg.Mount(e, prefix); err != nil { return err } - if err = admin.VerifyRoutes(e, prefix); err != nil { + if err := admin.VerifyRoutes(e, prefix); err != nil { return err } server := &http.Server{ @@ -108,10 +108,18 @@ func run(ctx context.Context) error { } } func main() { - ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) - defer cancel() - if err := run(ctx); err != nil { + if err := run(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } + +func run() error { + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) + defer cancel() + if err := runWithContext(ctx); err != nil { + return err + } + + return nil +} diff --git a/site/pro-source/examples/getting-started/main.go b/site/pro-source/examples/getting-started/main.go index 774533ca..1d78cbea 100644 --- a/site/pro-source/examples/getting-started/main.go +++ b/site/pro-source/examples/getting-started/main.go @@ -32,6 +32,13 @@ import ( ) func main() { + if err := run(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func run() error { browser := flag.Bool( "browser", false, @@ -45,14 +52,15 @@ func main() { flag.Parse() ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) defer cancel() - if err := run(ctx, runConfig{ + if err := runWithContext(ctx, runConfig{ keyFile: *keyFile, browser: *browser, address: "127.0.0.1:8080", }); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) + return err } + + return nil } type runConfig struct { @@ -63,7 +71,7 @@ type runConfig struct { verified func() } -func run(ctx context.Context, cfg runConfig) (result error) { +func runWithContext(ctx context.Context, cfg runConfig) (result error) { keyFile := cfg.keyFile if keyFile == "" { return errors.New( diff --git a/site/pro-source/examples/quota-otel/main.go b/site/pro-source/examples/quota-otel/main.go index 6e892e41..d7f62f03 100644 --- a/site/pro-source/examples/quota-otel/main.go +++ b/site/pro-source/examples/quota-otel/main.go @@ -29,7 +29,7 @@ import ( "echo.labstack.com/pro/quota/quotaotel" ) -func run(ctx context.Context, output io.Writer) (result error) { +func runWithContext(ctx context.Context, output io.Writer) (result error) { spanExporter, err := stdouttrace.New(stdouttrace.WithWriter(output)) if err != nil { return err @@ -150,12 +150,7 @@ func run(ctx context.Context, output io.Writer) (result error) { result = errors.Join(result, err) } }() - request, err := http.NewRequestWithContext( - ctx, - http.MethodGet, - "http://"+listener.Addr().String()+"/", - nil, - ) + request, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://"+listener.Addr().String()+"/", http.NoBody) if err != nil { return err } @@ -273,10 +268,18 @@ func toolCall( } func main() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - if err := run(ctx, os.Stdout); err != nil { + if err := run(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } + +func run() error { + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + if err := runWithContext(ctx, os.Stdout); err != nil { + return err + } + + return nil +} diff --git a/site/pro-source/examples/site-docs/commands.sh b/site/pro-source/examples/site-docs/commands.sh index 78fce55e..7d2848de 100644 --- a/site/pro-source/examples/site-docs/commands.sh +++ b/site/pro-source/examples/site-docs/commands.sh @@ -12,7 +12,7 @@ install_pro() { go env -w GOPRIVATE=echo.labstack.com # docs:end private-pattern # docs:start get-version - go get echo.labstack.com/pro@v0.1.0 + go get echo.labstack.com/pro@v0.2.0 # docs:end get-version go mod verify # docs:end install @@ -21,7 +21,7 @@ install_pro() { probe_registry() { # docs:start proxy-probe curl --silent --show-error --netrc-file "$HOME/.netrc" --output /dev/null --write-out '%{http_code}\n' \ - https://go.labstack.io/echo.labstack.com/pro/@v/v0.1.0.info + https://go.labstack.io/echo.labstack.com/pro/@v/v0.2.0.info # docs:end proxy-probe } @@ -125,7 +125,7 @@ test_packages() { build_application() { # docs:start docker-build - docker build --secret id=netrc,src="$HOME/.netrc" -t invoice-api:v0.1.0 . + docker build --secret id=netrc,src="$HOME/.netrc" -t invoice-api:v0.2.0 . # docs:end docker-build # docs:start vendor go mod vendor diff --git a/site/pro-source/examples/source/auth/example_test.go b/site/pro-source/examples/source/auth/example_test.go index 32b686bc..fd3212e3 100644 --- a/site/pro-source/examples/source/auth/example_test.go +++ b/site/pro-source/examples/source/auth/example_test.go @@ -54,8 +54,8 @@ func ExampleNewPrincipal() { if err != nil { panic(err) } - copy := p.Scopes() - copy[0] = "invoice:write" + cloned := p.Scopes() + cloned[0] = "invoice:write" fmt.Println(p.ID(), p.Scopes()[0]) // Output: employees:alice invoice:read } diff --git a/site/pro-source/examples/source/execution/example_test.go b/site/pro-source/examples/source/execution/example_test.go index 589ab080..87bfdb3e 100644 --- a/site/pro-source/examples/source/execution/example_test.go +++ b/site/pro-source/examples/source/execution/example_test.go @@ -44,7 +44,7 @@ func ExampleJournal_Do_transfer() { // request, with SDK retries/redirects disabled. The example omits network I/O. deadline := start.Add(permit.ValidFor) if !time.Now().Before(deadline) { - return execution.Result{}, &execution.Ambiguity{} + return execution.Result{}, &execution.AmbiguityError{} } fmt.Println(name, permit.SendID == sendID) } diff --git a/site/pro-source/examples/source/execution/executiontest/example_test.go b/site/pro-source/examples/source/execution/executiontest/example_test.go index f3af5e93..12a0aa31 100644 --- a/site/pro-source/examples/source/execution/executiontest/example_test.go +++ b/site/pro-source/examples/source/execution/executiontest/example_test.go @@ -21,12 +21,16 @@ import ( // docs:start ExampleCheckStore func ExampleCheckStore() { newStore := func(t *testing.T, now func() time.Time) execution.Store { + t.Helper() + store := executiontest.NewModel(now) t.Cleanup(func() {}) // Release per-case fixtures here. return store } // Call from func TestStoreConformance(t *testing.T). conformance := func(t *testing.T) { + t.Helper() + executiontest.CheckStore(t, newStore) executiontest.CheckScheduler(t, newStore) } diff --git a/site/pro-source/examples/source/execution/http_example_test.go b/site/pro-source/examples/source/execution/http_example_test.go index 35f4dfa3..9251498c 100644 --- a/site/pro-source/examples/source/execution/http_example_test.go +++ b/site/pro-source/examples/source/execution/http_example_test.go @@ -203,7 +203,7 @@ func ExampleAttemptFrom() { } // A refusal never authorizes a send. deadline := start.Add(permit.ValidFor) if !time.Now().Before(deadline) { - return &execution.Ambiguity{} + return &execution.AmbiguityError{} } // One identical provider request uses permit.Key and this deadline, with // SDK retries/redirects disabled. Here the provider succeeds, then the diff --git a/site/pro-source/examples/source/execution/problem_example_test.go b/site/pro-source/examples/source/execution/problem_example_test.go index a03f0ba9..58dbc1ee 100644 --- a/site/pro-source/examples/source/execution/problem_example_test.go +++ b/site/pro-source/examples/source/execution/problem_example_test.go @@ -17,11 +17,11 @@ import ( "echo.labstack.com/pro/problem/echoadapter" ) -// orderRejected is an application error; any type with Problem() qualifies. -type orderRejected struct{} +// orderRejectedError is an application error; any type with Problem() qualifies. +type orderRejectedError struct{} -func (orderRejected) Error() string { return "order rejected" } -func (orderRejected) Problem() problem.Details { +func (orderRejectedError) Error() string { return "order rejected" } +func (orderRejectedError) Problem() problem.Details { return problem.Details{ Type: "https://shop.example/problems/order-rejected", Title: "Order rejected", @@ -62,7 +62,7 @@ func ExampleMiddlewareConfig_ToMiddleware_problemAdapter() { if err := execution.Classify(c, execution.Failed); err != nil { return err } - return orderRejected{} + return orderRejectedError{} }, mw) show := func(w *httptest.ResponseRecorder) { h := w.Header() diff --git a/site/pro-source/examples/source/execution/scheduler_example_test.go b/site/pro-source/examples/source/execution/scheduler_example_test.go index ec1a4da1..6694eb00 100644 --- a/site/pro-source/examples/source/execution/scheduler_example_test.go +++ b/site/pro-source/examples/source/execution/scheduler_example_test.go @@ -107,7 +107,8 @@ func ExampleNewReconcileScheduler() { }}, PollInterval: time.Second, AuthorizeNamespace: func(context.Context, string) error { - if passes++; passes > 1 { + passes++ + if passes > 1 { cancel() // Stop after one pass, as an application shutdown would. } return nil diff --git a/site/pro-source/examples/source/quota/quotastripe/example_test.go b/site/pro-source/examples/source/quota/quotastripe/example_test.go index 6a5d1e75..51ecef91 100644 --- a/site/pro-source/examples/source/quota/quotastripe/example_test.go +++ b/site/pro-source/examples/source/quota/quotastripe/example_test.go @@ -100,7 +100,7 @@ func exampleExporter() (*quotastripe.Exporter, error) { type exampleTransport struct{} func (exampleTransport) RoundTrip(r *http.Request) (*http.Response, error) { - return &http.Response{StatusCode: 200, Header: make(http.Header), Body: io.NopCloser(strings.NewReader(`{"object":"list","has_more":false,"data":[]}`)), Request: r}, nil + return &http.Response{StatusCode: http.StatusOK, Header: make(http.Header), Body: io.NopCloser(strings.NewReader(`{"object":"list","has_more":false,"data":[]}`)), Request: r}, nil } // docs:start ExampleExporter_Status diff --git a/site/pro-source/examples/source/quota/shadow_admin_example_test.go b/site/pro-source/examples/source/quota/shadow_admin_example_test.go index e5fc1cb3..0cd5ad34 100644 --- a/site/pro-source/examples/source/quota/shadow_admin_example_test.go +++ b/site/pro-source/examples/source/quota/shadow_admin_example_test.go @@ -41,7 +41,7 @@ func ExampleLimiter_ShadowReport() { // docs:start ExampleTopK func ExampleTopK() { ctx := context.Background() - top := quota.NewTopK(5) + top := quota.MustNewTopK(5) l := quota.MustNew(quota.Config{Store: quota.NewMemoryStore(quota.MemoryConfig{}), Plans: []quota.Plan{{ID: "team", Limits: []quota.Limit{quota.PerDay("requests", 100)}}}, Hooks: quota.JoinHooks(top.Hooks())}) defer func() { if err := l.Close(ctx); err != nil { diff --git a/site/pro-source/examples/source/webhook/http_example_test.go b/site/pro-source/examples/source/webhook/http_example_test.go index 6170e7cb..bd75bdda 100644 --- a/site/pro-source/examples/source/webhook/http_example_test.go +++ b/site/pro-source/examples/source/webhook/http_example_test.go @@ -25,14 +25,14 @@ func ExampleHTTPSenderConfig_ToSendFunc() { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(io.LimitReader(r.Body, webhook.MaxPayloadBytes+1)) if err != nil { - w.WriteHeader(400) + w.WriteHeader(http.StatusBadRequest) return } if _, err = verify(r.Context(), r, body); err != nil { - w.WriteHeader(400) + w.WriteHeader(http.StatusBadRequest) return } - w.WriteHeader(204) + w.WriteHeader(http.StatusNoContent) })) defer server.Close() send, err := (webhook.HTTPSenderConfig{Endpoints: map[string]webhook.Endpoint{ diff --git a/site/pro-source/examples/source/webhook/webhookpg/example_test.go b/site/pro-source/examples/source/webhook/webhookpg/example_test.go index 22489881..efd116f7 100644 --- a/site/pro-source/examples/source/webhook/webhookpg/example_test.go +++ b/site/pro-source/examples/source/webhook/webhookpg/example_test.go @@ -232,7 +232,7 @@ VALUES ($1, $2, 'grant', $3) ON CONFLICT DO NOTHING`, } if tag.RowsAffected() == 0 { var recorded int64 - if err = tx.QueryRow( + if err := tx.QueryRow( ctx, "SELECT amount FROM app.entitlements WHERE tenant = $1 AND "+ "invoice = $2 AND action = 'grant'", diff --git a/site/pro-source/examples/source/webhook/webhookpg/recipe_example_test.go b/site/pro-source/examples/source/webhook/webhookpg/recipe_example_test.go index 82a3b666..8f96346b 100644 --- a/site/pro-source/examples/source/webhook/webhookpg/recipe_example_test.go +++ b/site/pro-source/examples/source/webhook/webhookpg/recipe_example_test.go @@ -269,7 +269,7 @@ VALUES ($1, $2, 'grant', $3) ON CONFLICT DO NOTHING`, } if tag.RowsAffected() == 0 { var recorded int64 - if err = tx.QueryRow( + if err := tx.QueryRow( ctx, "SELECT amount FROM app.entitlements WHERE tenant = $1 AND "+ "invoice = $2 AND action = 'grant'", @@ -473,7 +473,7 @@ func runHTTPOutbox( }, }, } - if err = webhookpg.Migrate(ctx, ownerPool, cfg); err != nil { + if err := webhookpg.Migrate(ctx, ownerPool, cfg); err != nil { return err } store, err := webhookpg.New(ctx, runtimePool, cfg) @@ -494,7 +494,7 @@ func runHTTPOutbox( if err != nil { return err } - if err = enqueueHTTPGrant(ctx, runtimePool, store, p); err != nil { + if err := enqueueHTTPGrant(ctx, runtimePool, store, p); err != nil { return err } worker, err := webhookpg.NewWorker(store, webhookpg.WorkerConfig{Concurrency: 8}) @@ -548,7 +548,7 @@ VALUES ($1, $2, 'grant', $3) ON CONFLICT DO NOTHING`, } if tag.RowsAffected() == 0 { var amount int64 - if err = tx.QueryRow( + if err := tx.QueryRow( ctx, `SELECT amount FROM app.entitlements WHERE tenant=$1 AND invoice=$2 AND action='grant'`, diff --git a/site/pro-source/examples/stripe/main.go b/site/pro-source/examples/stripe/main.go index e39f90f1..30fc4ec7 100644 --- a/site/pro-source/examples/stripe/main.go +++ b/site/pro-source/examples/stripe/main.go @@ -22,6 +22,13 @@ import ( ) func main() { + if err := run(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func run() error { online := flag.Bool( "online", false, @@ -31,9 +38,10 @@ func main() { ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) defer stop() if err := demo(ctx, *online); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) + return err } + + return nil } func demo(ctx context.Context, online bool) (resultErr error) { @@ -125,7 +133,7 @@ func demo(ctx context.Context, online bool) (resultErr error) { } // Stop all source admissions. This example has no reservations or future // settlements; Close joins outbox processing before affirming catch-up. - if err = lim.Close(ctx); err != nil { + if err := lim.Close(ctx); err != nil { return err } closed = true diff --git a/site/pro-source/pages/install.mdx b/site/pro-source/pages/install.mdx index 9d1e55b6..eed06a60 100644 --- a/site/pro-source/pages/install.mdx +++ b/site/pro-source/pages/install.mdx @@ -11,7 +11,7 @@ sidebar: Use this page before your first `go get`, and when you set up CI, Docker or an offline build. -Install `echo.labstack.com/pro v0.1.0` through `go.labstack.io`. You need Go 1.26 or later, an application module and an organisation download token. The module requires Echo v5.4.2. You do not need access to a private Git repository. The token only authenticates module downloads. Your application never checks it at run time. +Install `echo.labstack.com/pro v0.2.0` through `go.labstack.io`. You need Go 1.26 or later, an application module and an organisation download token. The module requires Echo v5.4.2. You do not need access to a private Git repository. The token only authenticates module downloads. Your application never checks it at run time. ## Set up local development @@ -56,7 +56,7 @@ An always-run step removes the credential. The workflow pins each Action to a fu ```yaml file=site/pro-source/examples/site-docs/github-actions.yml ``` -Save the workflow in your application's workflow directory. Before you enable the job, commit `go.mod` (pinned to v0.1.0) and `go.sum`. Run this job only for trusted code. Any step in the job can read the secret. Fork pull requests do not receive repository secrets by default. +Save the workflow in your application's workflow directory. Before you enable the job, commit `go.mod` (pinned to v0.2.0) and `go.sum`. Run this job only for trusted code. Any step in the job can read the secret. Fork pull requests do not receive repository secrets by default. Run checks for fork pull requests in a separate workflow that has no secret. If you turn on module caching later, restrict the cache to licensed users. `NETRC` points Go at the temporary credential file. The token appears only in the credential-setup step's environment. That step turns off shell tracing before it writes the file. The cleanup step runs even when the download fails. When you copy the workflow, update the Action pins under your own update policy. @@ -93,7 +93,7 @@ Probe the exact pinned metadata URL without printing the credential or response ```sh file=site/pro-source/examples/site-docs/commands.sh#proxy-probe ``` -A 200 confirms that this credential can read v0.1.0 metadata. A 401 means missing or invalid credentials. Check four things: the machine name is `go.labstack.io`; `password` holds the token; Go reads the netrc you expect (`NETRC` or your home directory); the token is not revoked. The registry ignores `login` and checks only `password`. A wrong slug does not cause 401. Do not put the token in a URL or paste it into diagnostics. +A 200 confirms that this credential can read v0.2.0 metadata. A 401 means missing or invalid credentials. Check four things: the machine name is `go.labstack.io`; `password` holds the token; Go reads the netrc you expect (`NETRC` or your home directory); the token is not revoked. The registry ignores `login` and checks only `password`. A wrong slug does not cause 401. Do not put the token in a URL or paste it into diagnostics. A 403 means the token is valid, but your organisation is not entitled to that version. Check the version you requested. Then ask support to check your organisation's status. After a subscription lapses, the token can still download versions you were entitled to. A revoked organisation gets 403. Clearing the module cache or turning off checksums does not fix a 403. diff --git a/site/pro-source/pages/problems/credit-conflict.mdx b/site/pro-source/pages/problems/credit-conflict.mdx index ad444245..cee2da93 100644 --- a/site/pro-source/pages/problems/credit-conflict.mdx +++ b/site/pro-source/pages/problems/credit-conflict.mdx @@ -71,7 +71,7 @@ Notes: - Fields are written in alphabetical order. - The console rewrites every 404 response to `admin_not_found` with an empty `detail`. -- The console renders this problem in its own format when a panel returns it as an `admin.Problem` outside an action commit. Its title is `Conflict` unless the panel supplies one. +- The console renders this problem in its own format when a panel returns it as an `admin.ProblemError` outside an action commit. Its title is `Conflict` unless the panel supplies one. - When a credit action conflicts at commit, the console does not return a problem. It returns an `application/json` action result with `audit.code` set to `credit_conflict` and outcome `refused`. ## Related behavior diff --git a/site/pro-source/pages/reference/admin.mdx b/site/pro-source/pages/reference/admin.mdx index dac68100..13f4d935 100644 --- a/site/pro-source/pages/reference/admin.mdx +++ b/site/pro-source/pages/reference/admin.mdx @@ -7,7 +7,7 @@ description: Fields, defaults and constructors for admin. import ConfigReference from '../../../../components/ConfigReference.astro'; -`echo.labstack.com/pro` v0.1.0. See the [task guide](/pro/guides/admin/) for setup examples. +`echo.labstack.com/pro` v0.2.0. See the [task guide](/pro/guides/admin/) for setup examples. ## Required fields @@ -44,6 +44,11 @@ When every expensive-view slot is busy, the console returns 503 with code `admin The console never closes panel backends, authentication resources or the audit sink. +## Error types + +Console errors use `*admin.ProblemError`. OpenAPI chain errors use +`*openapi.ProblemError`. + ## Related pages [Package explanation](/pro/admin/) · [Task guide](/pro/guides/admin/) · [Problem catalog](/pro/problems/) diff --git a/site/pro-source/pages/reference/audit.mdx b/site/pro-source/pages/reference/audit.mdx index 699a6fd8..b0d2e3fa 100644 --- a/site/pro-source/pages/reference/audit.mdx +++ b/site/pro-source/pages/reference/audit.mdx @@ -7,7 +7,7 @@ description: Fields, defaults and constructors for audit. import ConfigReference from '../../../../components/ConfigReference.astro'; -`echo.labstack.com/pro` v0.1.0. See the [task guide](/pro/guides/audit/) for setup examples. +`echo.labstack.com/pro` v0.2.0. See the [task guide](/pro/guides/audit/) for setup examples. ## Required fields diff --git a/site/pro-source/pages/reference/auth.mdx b/site/pro-source/pages/reference/auth.mdx index b86e719b..b92e3f6d 100644 --- a/site/pro-source/pages/reference/auth.mdx +++ b/site/pro-source/pages/reference/auth.mdx @@ -7,7 +7,7 @@ description: Fields, defaults and constructors for auth. import ConfigReference from '../../../../components/ConfigReference.astro'; -`echo.labstack.com/pro` v0.1.0. See the [task guide](/pro/guides/auth/) for setup examples. +`echo.labstack.com/pro` v0.2.0. See the [task guide](/pro/guides/auth/) for setup examples. ## Required fields @@ -87,6 +87,10 @@ See the [compiled SQL sketch](/pro/guides/auth/#replace-the-memory-key-store). Stores, clients, clocks and loggers must support concurrent calls. +## Error types + +Authentication and scope refusals use `*auth.FailureError`. + ## Related pages [Package explanation](/pro/auth/) · [Task guide](/pro/guides/auth/) · [Problem catalog](/pro/problems/) diff --git a/site/pro-source/pages/reference/execution.mdx b/site/pro-source/pages/reference/execution.mdx index d9205276..b2340256 100644 --- a/site/pro-source/pages/reference/execution.mdx +++ b/site/pro-source/pages/reference/execution.mdx @@ -7,7 +7,7 @@ description: Fields, defaults and constructors for execution. import ConfigReference from '../../../../components/ConfigReference.astro'; -`echo.labstack.com/pro` v0.1.0. See the [task guide](/pro/guides/execution/) for setup examples. +`echo.labstack.com/pro` v0.2.0. See the [task guide](/pro/guides/execution/) for setup examples. ## Required fields @@ -77,6 +77,11 @@ Import `echo.labstack.com/pro/execution/pgstore`. Keep the store open until requests, recovery calls and the scheduler have stopped. +## Error types + +Business failures use `*execution.FailureError`; uncertain external effects +use `*execution.AmbiguityError`. The execution mode type is `execution.Mode`. + ## Related pages [Package explanation](/pro/execution/) · [Task guide](/pro/guides/execution/) · [Problem catalog](/pro/problems/) diff --git a/site/pro-source/pages/reference/index.mdx b/site/pro-source/pages/reference/index.mdx index 227b61b3..4ec41924 100644 --- a/site/pro-source/pages/reference/index.mdx +++ b/site/pro-source/pages/reference/index.mdx @@ -1,13 +1,13 @@ --- title: Configuration reference -description: Look up fields, defaults, limits and constructor signatures for Echo Pro v0.1.0. +description: Look up fields, defaults, limits and constructor signatures for Echo Pro v0.2.0. --- {/* Diataxis: reference */} Look up a field's type, default and limits here. For setup steps, use the [task guides](/pro/guides/). -Look up fields, defaults, limits and constructor signatures for Echo Pro v0.1.0. +Look up fields, defaults, limits and constructor signatures for Echo Pro v0.2.0. - [Auth](/pro/reference/auth/) - [Tenant](/pro/reference/tenant/) diff --git a/site/pro-source/pages/reference/quota.mdx b/site/pro-source/pages/reference/quota.mdx index 474550e0..868b6016 100644 --- a/site/pro-source/pages/reference/quota.mdx +++ b/site/pro-source/pages/reference/quota.mdx @@ -7,7 +7,7 @@ description: Fields, defaults and constructors for quota. import ConfigReference from '../../../../components/ConfigReference.astro'; -`echo.labstack.com/pro` v0.1.0. See the [task guide](/pro/guides/quota/) for setup examples. +`echo.labstack.com/pro` v0.2.0. See the [task guide](/pro/guides/quota/) for setup examples. ## Required fields @@ -140,6 +140,12 @@ Import `echo.labstack.com/pro/quota/quotastripe/stripeadmin`. Call `Limiter.Close` before closing its store or Redis client. +## Top-K tracker + +`quota.NewTopK(k)` returns `(*quota.TopK, error)` and rejects k outside +1..1000 with a `*quota.ConfigError`. `quota.MustNewTopK(k)` panics instead, for +fixed configuration. + ## Related pages [Package explanation](/pro/quota/) · [Task guide](/pro/guides/quota/) · [Problem catalog](/pro/problems/) diff --git a/site/pro-source/pages/reference/tenant.mdx b/site/pro-source/pages/reference/tenant.mdx index d61fcb1f..a36fbb07 100644 --- a/site/pro-source/pages/reference/tenant.mdx +++ b/site/pro-source/pages/reference/tenant.mdx @@ -7,7 +7,7 @@ description: Fields, defaults and constructors for tenant. import ConfigReference from '../../../../components/ConfigReference.astro'; -`echo.labstack.com/pro` v0.1.0. See the [task guide](/pro/guides/tenant/) for setup examples. +`echo.labstack.com/pro` v0.2.0. See the [task guide](/pro/guides/tenant/) for setup examples. ## Required fields @@ -62,6 +62,10 @@ Import `echo.labstack.com/pro/tenant`. Keep the authorization client open while requests are using it. +## Error types + +Tenant selection refusals use `*tenant.FailureError`. + ## Related pages [Package explanation](/pro/tenant/) · [Task guide](/pro/guides/tenant/) · [Problem catalog](/pro/problems/) diff --git a/site/pro-source/pages/reference/webhook.mdx b/site/pro-source/pages/reference/webhook.mdx index a11bfa6d..f12ad228 100644 --- a/site/pro-source/pages/reference/webhook.mdx +++ b/site/pro-source/pages/reference/webhook.mdx @@ -7,7 +7,7 @@ description: Fields, defaults and constructors for webhook. import ConfigReference from '../../../../components/ConfigReference.astro'; -`echo.labstack.com/pro` v0.1.0. See the [task guide](/pro/guides/webhook/) for setup examples. +`echo.labstack.com/pro` v0.2.0. See the [task guide](/pro/guides/webhook/) for setup examples. ## Required fields diff --git a/site/pro-source/pro-source.json b/site/pro-source/pro-source.json index f49e5a40..164cac38 100644 --- a/site/pro-source/pro-source.json +++ b/site/pro-source/pro-source.json @@ -1,20 +1,20 @@ { "module": "echo.labstack.com/pro", - "version": "v0.1.0", - "commit": "2c149e09085cc06f54df7c7064363ebb8ab0d681", + "version": "v0.2.0", + "commit": "cff49e6f4e402d78d5e54a0b17a42ce58ccd5b84", "hashes": { - "config-fields.json": "621970f1cd7de6a9bc3a6fe20dbad5dde2ab670f446cac16e0692e04f838a19b", + "config-fields.json": "0b54cb3291832384f3f9cd5c269151b2c6a0a96acd734ed42feba6324bc76e99", "examples/admin-demo/audit.go": "e332591665875ea7a9d36e756089cf34c8eca947ded969bac6014472c0d206a3", "examples/admin-demo/auth.go": "a0d6bd7bbfaf80d98d78a34a4b38492ecb9fab7fc4ea53bdb52a376328d1596d", "examples/admin-demo/execution.go": "d7306b7f581273d70f31341116c477c4c011d01be1a6d16afbac9089ce3ac343", - "examples/admin-demo/main.go": "85e93c55e0af6f5a97927e49ffbccaedd19e2a2f6344e3aa8369c30ebf1d127a", - "examples/admin-demo/quota.go": "235aa329e18f1cb13351906b06e20f8655a91e6d9498de01609e4debb1ae7803", - "examples/admin-demo/reports.go": "fb23b2ab40d760f1fcf89a9d23bb7d6107d2a5a46a692536183bd13bdc385a51", + "examples/admin-demo/main.go": "f672349bda0b0aa80dd0dc358745f5249f09de84d30d8e2eb59a37fc4c93e8dc", + "examples/admin-demo/quota.go": "9f6094fcf236db85f105a26e427b9cca1bfb6af4e65b33cf70be48eff7c0cb86", + "examples/admin-demo/reports.go": "71da765dc2a60d4a973fb8b93aad48723a324068b6306a3dae3e2ede20f32661", "examples/admin-demo/server.go": "7c464c7545b5e953e88d76c96ab1e4bde57a318d83166b70dde001c1388bfbf4", "examples/admin-demo/throughput.go": "ce7f6960d01dfb96861c32cec1d6049fbd5ab3366a8d579ad38625fa4ae07562", - "examples/admin-demo/webhook.go": "921b52dd3221fdd463ff4aac9747285148b5e415fafad65f88a3506710517957", - "examples/admin/main.go": "bacf939d084087e13f7a0725eb1824c3bf5a0fe2f0fc067b3b8c9319d0aedae6", - "examples/getting-started/main.go": "8992c293c0e69ab73c9e82aa8a348c6a1e05d24782b4f54cf75f454fbfd96cef", + "examples/admin-demo/webhook.go": "8565aa5ffe2a0bfbc90e384846081cfae293c6f7b33f27405e0ac737f758dca8", + "examples/admin/main.go": "60a1fd09f3a5b114f17be7dfa5afbdd5fb8d6195a9db1b68033c1a4c835fb337", + "examples/getting-started/main.go": "9c0b142cf0fd5a464acedd6657b24c8e36fcccbe63593b134489dd5e363c5bcd", "examples/problems/admin-action-id-reused-409.http": "a84723b3bd6b70ed13e971e84edac59792f772a5afbdc34c1ce4a24fb660efdb", "examples/problems/admin-action-id-reused-409.json": "9a65c62ae4e8c9642c24dfd8b28e888177f3c7ac9052bcdaad273c61ef94b9b6", "examples/problems/admin-audit-unavailable-503.http": "3914c5ef86fe9762f853b1e4ae1a98c1fdfd9da1457397e5a4c0a7ec02866488", @@ -107,9 +107,9 @@ "examples/problems/unsupported-media-type-415.json": "a6d11043902688a4c6f902f616013af0cab3ff3afc546b2c5527a1401ef1bd20", "examples/problems/validation-failed-400.http": "877410483c96024bb42c58c2a10e8b2ea49ba17389013c38bb7db47d7bbe6f8c", "examples/problems/validation-failed-400.json": "9421fbf962b6becd2832087e0befd948cb9944701508469111288dedc143b1c4", - "examples/quota-otel/main.go": "d3846b93806a1b38e1297d1a7f01d16179872987122fbb07027f1043ac69d843", + "examples/quota-otel/main.go": "4378c90b25b15418cc91f0a95650c8408c74fb40ef8270bee66505980f073872", "examples/site-docs/Dockerfile": "623de8452e2deb57993c07a18091753726e73fc23fd2a2e943bb4893c6906ff4", - "examples/site-docs/commands.sh": "72b161c240e9b8a499958c05c45a5c5578df65881d0ca0b6835d5228077aa6d9", + "examples/site-docs/commands.sh": "900e9bdd0594fba0eab04cd3cde8f05d5d2e4795ef2deeadb40a94082a55045f", "examples/site-docs/github-actions.yml": "0ecef950af71006c065ead2e358b052996904fcc7240539696cc284a160c8df0", "examples/site-docs/netrc.example": "f3e5951fa221bdbb29d142ec93646eadc62482b1313eb139aa3c00385857e144", "examples/source/admin/admintest/example_test.go": "99aa99711483ad7975802729b45251d071b079ec1cf9787e7d174b1e28555957", @@ -123,19 +123,19 @@ "examples/source/audit/quickstart_example_test.go": "1a06d54832664ba5142865cdfcea162fd4d5560040190c1b7391938b2598be83", "examples/source/audit/reader_example_test.go": "61556da5b91c1591c38edb6cd32d413506ae7da93523959973ee4e5981cf8f6b", "examples/source/auth/authbind/example_test.go": "43326ec9f716316ee26b57603e07f0567e9debc83e550a1803ff12bed9d8e2ec", - "examples/source/auth/example_test.go": "f2a1a06dd91d648a81e75d55d8d25bdab0f8d0cc0a361183038c81361d4715f0", + "examples/source/auth/example_test.go": "6ec934babb25197370fc83fc96f9461cde1f8d91c02538bff94dfb07f9e2de72", "examples/source/auth/keystore_example_test.go": "4d4fe528ea22c59b8e3d249eb8bb81fc7a7ecf0713dce5f9e8b82bfa556deb1c", "examples/source/auth/quickstart_example_test.go": "f4275e8ab6730402221a6105e853eff7ccd3c49950e232ae69d631777a313444", "examples/source/auth/site_example_test.go": "77ba565ac41665d8e1133e18a6edf38105db1c7c3f0ccfefeec540ea9ccc5430", "examples/source/execution/call_example_test.go": "c99decaac867d7390022380922993a7e742379bca6b629ac6279c01460b7c078", - "examples/source/execution/example_test.go": "6db4dd6bfc71c6434cb3ad6b191cb8f63ed425afe654ab96dcc6f20275fe61d8", + "examples/source/execution/example_test.go": "f3c5e2f9ab54c01d25fe222aa2c545324dd8213a54c4e29d0162620daca3739b", "examples/source/execution/executionadmin/example_test.go": "ec153d8217f9fd703bfa1ffd3ebe5e2b3fc610b42570e1ca664cedb88d958083", - "examples/source/execution/executiontest/example_test.go": "22dd5fce413843fd0eb2b21b20e5121ebc264f6d9df7ac8e862fc6ff78e1b7bb", - "examples/source/execution/http_example_test.go": "b1f777e1907140360c33d58b0babf64afe2d7bc009762c140ec3a7d314657079", + "examples/source/execution/executiontest/example_test.go": "bb4d2ccb7354510abbf6f78f65f0b9a81d625365705831cc0ca970635dad0a03", + "examples/source/execution/http_example_test.go": "79c504d96c29f13d8ecf9eb1fbb980a1540b3cdd285bc8c9593b530639b32ec0", "examples/source/execution/pgstore/example_test.go": "e562e2992ddc09c7cab9d663786cf7e04d7dbdeee6245e81b598aca2f90273f1", "examples/source/execution/pgstore/transactional_example_test.go": "3af91f981179966768a3b3cc7896ab4f9127ebae8365adc5ae9679f21f492b84", - "examples/source/execution/problem_example_test.go": "034494cf1ce1f5179d778541bca5c1a4f1886df1a97748695dc687b5d7ca5e0f", - "examples/source/execution/scheduler_example_test.go": "097168174518551ce80b1f686dce6fb1597947ca47c435741465d4fe21ccc6da", + "examples/source/execution/problem_example_test.go": "14c42d27cf61f766c64f441dc6aaaa6aa3ffaf69d17cb1e23cf3d52d075c587e", + "examples/source/execution/scheduler_example_test.go": "56c727e24814e46b5240511524f524b1e5fc3849878814409fdf5466d61c928a", "examples/source/quota/accounting_example_test.go": "8d8edf20f6955e0e8e23c3ffe9f7c847ae75254a62c17f794f5b6bc2d26b01f8", "examples/source/quota/auth_example_test.go": "981db5e80b6797cab9a10cb1d437619b460a846a09cd47deb94c4751971a3e4a", "examples/source/quota/example_test.go": "8404974f93786027c47b5607e6a4c8034250848596cb1974294c248fa651fd81", @@ -148,20 +148,20 @@ "examples/source/quota/quotaotel/lifecycle_example_test.go": "9262d0d1972ab785387b36e97d441ee6d2a2b7aaa6938554deae1f2342b56d57", "examples/source/quota/quotaotel/tracing_example_test.go": "3250313585f7d84e8f316999b25bf9dd020be2c90cdbc8f160995c7af95f2797", "examples/source/quota/quotaprom/lifecycle_example_test.go": "b526216a45567e52898740d3375005f6076b0adce24e6b92d8a08c5457ec888d", - "examples/source/quota/quotastripe/example_test.go": "1be146f4c1df2efebf3ae7c99c489ebcdda616b2a7ceb9895ab53fbf1344ea89", + "examples/source/quota/quotastripe/example_test.go": "057fb6f63ff17cdf78574cb3254c1246a88ff1a95b0a23c7aa7c540823152612", "examples/source/quota/redisstore/example_test.go": "cc18c48da452b751e40c574d4729b134d96641e19602251d9f623bf54dff059e", "examples/source/quota/reset_example_test.go": "28796db3bed9c06f6193d9c1fb8b114638b7df7e8feb4cc84dded4fad138ac44", - "examples/source/quota/shadow_admin_example_test.go": "20e6dcb7f17a1dd4a66bdd344fa58005dd5e408750ab31335d79b87de47a9d8f", + "examples/source/quota/shadow_admin_example_test.go": "49d21daaae17113835e0a8f8de98074e80e5b01f29a26de7efd22c452d53015d", "examples/source/quota/site_example_test.go": "83992985808c65cf64884cca435493636f41a520269efeae173f76a76fc3c243", "examples/source/tenant/example_test.go": "87f020ebc1c351896f4fbc026aa232f555037b4f23c0cfa3d6f061dd048cac6d", "examples/source/tenant/site_example_test.go": "97055f408e82c6c0e4bcbb4e7996a7052a43577089f2835e0aabdc6f2d42ff27", "examples/source/webhook/example_test.go": "902671df2faa0573e3eb239b70cad699bd8b4ef6e4cdda623dc6440f1864a37d", - "examples/source/webhook/http_example_test.go": "a39f2e451ea88aefb3aebea8818e8a4a18f540d7f6cd5cdb83ea4964b78be654", + "examples/source/webhook/http_example_test.go": "af0aa7e3dcf9debb0b6bd39188bc9d97bd53cc7a664c615fcd506622259a56fb", "examples/source/webhook/webhookadmin/example_test.go": "f0019f0d436d5c9cd2369bb9f6d7b5ea5dbf54a01f056b541fb3f03dc294bebb", "examples/source/webhook/webhookmem/example_test.go": "d2233ff0882ac1446aea84f10f8ac9c6f2407a09e17baa2ae8f16199f453b059", - "examples/source/webhook/webhookpg/example_test.go": "95ed0ae6c5dc6a659f220e055421d93d156390a1d5c51e75b12877b50795660a", - "examples/source/webhook/webhookpg/recipe_example_test.go": "ebf02f54faace58590fefa2016d10a7e90d2a503fd85b3f342f2aa2ab7cd00bf", - "examples/stripe/main.go": "7a90744fbb82862e6e8c4e4a91da0c24f5728c6d5afe75718333e69dde0e568e", + "examples/source/webhook/webhookpg/example_test.go": "41d048557781645689623deb46597559b15865afbc529d149be476645f6aed8e", + "examples/source/webhook/webhookpg/recipe_example_test.go": "c8913d7f1fa8855da4111f94bcedd53f4b41290c8c661eef46fdac613ee6615d", + "examples/stripe/main.go": "9a276a754e25ce92c5ac9aa7b56bf99a7bdaaa9e9e5aa720dbd96a8172cabb4b", "go.mod": "a15972f4fabcb3e4bb557961a5871308b5716a0d84c0520daeb7d093e5a06c22", "pages/admin.mdx": "4d54be230046665206d032d6f8b8b5e52dd2f7383880c788559ac9292bf58302", "pages/audit.mdx": "b4c0ea5522c0f15e2e54d4940d0441d2a8e8085b1c6d1fab0d06f8be3bdb7c95", @@ -181,7 +181,7 @@ "pages/guides/quota.mdx": "3556fdb67c53499ea178dd51b626506eb79889a8705dd7f9e5d364c6a8a4b228", "pages/guides/tenant.mdx": "d43344709a946d32032a31c3b9315fa43a2e643f6f6744e94791320bc1e83517", "pages/guides/webhook.mdx": "96d9a7a4b54216b2e9f8508e9f983e886cac4facfc5b804e92767496a9feaac6", - "pages/install.mdx": "8c23b7caa0f905d1590ff75b9659104e842477861ff33061339b51cabac44046", + "pages/install.mdx": "eaf8bc4f94290cfcb1c1b74a24c808c95e2a75b5240e7ebf31bdb2560b7003ac", "pages/problems/admin-action-id-reused.mdx": "3108f882f18e18692f8cc69716bac6de0a9ddd49e59e3bdc8b78ced296722f06", "pages/problems/admin-audit-unavailable.mdx": "92d366716545bf18a2d2c3e39ddfc7a8df03515b8a375b45873469a0a987a28d", "pages/problems/admin-backend-unavailable.mdx": "dff14442476efdcfaa093de6fe8e9f2d6396f95c80775148bd8a38421ce16953", @@ -204,7 +204,7 @@ "pages/problems/binding-failed.mdx": "7d8e08db0133b3947c9b42c901433bd209bab0d273335350a55be2af5a580f21", "pages/problems/body-too-large.mdx": "7fe72bd04e199c90d2d6f29bd50394d6818592cb7cc09a9ebbebcb78fc40acd3", "pages/problems/cost-exceeds-limit.mdx": "4002230cdba35faec650cc7544256b7454bb42f39f15f4ded9123f20d956225d", - "pages/problems/credit-conflict.mdx": "d3e59f6dcc5620e50c63b6bb0d80a3ea5dcaa070612028401d724fa1231dca4d", + "pages/problems/credit-conflict.mdx": "aed99580a234e4413c508aacf90f4652b17f22f139a178b8a6bb8ddd0f7611dd", "pages/problems/execution-capacity.mdx": "4c41f682f1225a3dce7bad1fabb69e6c0d9881d73b0b39f4a873491d90f70dc3", "pages/problems/execution-unavailable.mdx": "1fee19e036fb6bf876d6bd29ab42640c032aa2742dd3082c1eb600fc6a699be5", "pages/problems/forbidden.mdx": "5fe38da180a76d5339050b607330917b29f398698d10d5e4c2d277c2580cc61a", @@ -229,14 +229,14 @@ "pages/problems/unsupported-media-type.mdx": "76f61b7b55d7574b0c7c1e4c86a287ec287cc95ccc75278377cbfe00cfbd32f6", "pages/problems/validation-failed.mdx": "8072ed19048a3f04e2fc7149efc0c0f2a5011b48942cadc9717be8e2984ae372", "pages/quota.mdx": "3a86dd62a1ce30482a09120ed5cded86212bd865204140cf7acbbd7735d95dfd", - "pages/reference/admin.mdx": "aece9484f58f18ca3852f4f03a8a9f0c9b21af8345f847060bf5ec8a414fb138", - "pages/reference/audit.mdx": "ce057400c95177f5a43c1aca0aa2fdeb26805d9e316a63dbff682790822b8601", - "pages/reference/auth.mdx": "f52084f9d5f619198fd83a1adc7b4511b9f3a2332ae12931a51ff5c89f0d7751", - "pages/reference/execution.mdx": "bf5a4161612c34f53454561bd06b3e4fcc9af7d4c629b7ec8a1c2ded9d05bd9b", - "pages/reference/index.mdx": "83d7b52f7ea5998dba9b4b506404e68cf7513d21f60a328071651136f0c70a67", - "pages/reference/quota.mdx": "2c0277868b8eb55aa7e4f1c62ee91b26adea76aa8db16d2d38b47501a1c3745c", - "pages/reference/tenant.mdx": "8101cdce561611394a893cbd5af087399e9e58eea41eda4b09126ab570c270b4", - "pages/reference/webhook.mdx": "d0967f140e3712490303a7d63b5157add9aac2ca23d9b9129d99159c092c64b5", + "pages/reference/admin.mdx": "c06f83bb3aaef2d4e235f2165ca2eb2a56743a9dca86b5d71c39023ad1469d7d", + "pages/reference/audit.mdx": "e73804ca11d413d455ee1c2932c4ee4770a616df90755453a5fc46ac92a9816b", + "pages/reference/auth.mdx": "6a7b650d80426ee83bcb3fa211d45d5cdce70cd9054c65da6ae2fcc5c9a0c513", + "pages/reference/execution.mdx": "0945b3f7222b74d43fcae9e86b959ce135a1de3d82e09d244d9bfc5174558fc5", + "pages/reference/index.mdx": "82b058f79937fd053ef7f5110429b0daac34f3fca71e3d741587b7f322e817af", + "pages/reference/quota.mdx": "c38ad98f1d286484665755c2e1aa288155b88100dd965ded9fd6e5e472cbfc3b", + "pages/reference/tenant.mdx": "f53e8f8f6ba1e6c07168e10b21e8e6e1a8021e856ff3ad5ba329a6a9d16b0ef4", + "pages/reference/webhook.mdx": "967cb6c6e8a41dedfa8601cf55392e50ad10826f0c95144365705a25b673eb2d", "pages/tenant.mdx": "06c4720f08fd92f59b7bdddd07399fd3df5645bb7f1076d652a24097cfae1a15", "pages/webhook.mdx": "ac3e9406ca82f2ec976ed99e29d87679a6a433ef1c7d0d3c32064230a9302c7f", "problems.json": "ed5ed459f7970446a1ca2941a96824a86b3bc4e1996d5f64880ed8060a37fce4" diff --git a/site/scripts/pro-source.test.mjs b/site/scripts/pro-source.test.mjs index be4a3432..440f022d 100644 --- a/site/scripts/pro-source.test.mjs +++ b/site/scripts/pro-source.test.mjs @@ -8,7 +8,7 @@ import { readPro, apiSnapshot, siteDir } from './pro-source.mjs'; test('Pro snapshot has complete catalog coverage and source-owned programs', () => { const bundle = readPro(); - assert.equal(bundle.provenance.version, 'v0.1.0'); + assert.match(bundle.provenance.version, /^v\d+\.\d+\.\d+$/); for (const [name, content] of bundle.files) { if (name.startsWith('pages/') && content.toString().includes('package main')) assert.fail(`${name} pasted a program`); } diff --git a/site/src/pages/pro.astro b/site/src/pages/pro.astro index 65e96e1f..84602d0a 100644 --- a/site/src/pages/pro.astro +++ b/site/src/pages/pro.astro @@ -413,7 +413,7 @@ const head = [