From dafede870acee1c90815f07781745c955e516ac1 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 13 Aug 2026 14:23:03 +0200 Subject: [PATCH] refactor: lay the tree out by mechanism role MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The top level is the x402 role grid: facilitator/ verify, settle, and the facilitator service server/exact/ the SchemeNetworkServer a seller mounts (package exact) client/ the SchemeNetworkClient, sources in src/, emit in dist/ cmd/ examples/ e2e/ Import paths lose the go/ segment: x402/go/facilitator → x402/facilitator x402/go/mechanisms/gno/exact/server → x402/server/exact x402/go/e2e → x402/e2e The scheme keeps a segment of its own because a second scheme would need one; the namespace does not, since every mechanism here is gno. The JS buyer lives with the test that drives it, as e2e/buyer.mjs beside pay_test.go. It imports the mechanism by package name, so the exports map stays covered. gofmt runs over . rather than a named tree. e2e remains its own module, so `go test ./...` cannot start a chain. --- .github/workflows/ci.yml | 6 ++-- .goreleaser.yaml | 2 +- Makefile | 36 ++++++++++--------- README.md | 30 ++++++++-------- {js => client}/README.md | 15 ++++---- {js => client}/src/exact/client.test.ts | 0 {js => client}/src/exact/client.ts | 0 {js => client}/src/signing.test.ts | 0 {js => client}/src/signing.ts | 0 {go/cmd => cmd}/gnofacilitator/main.go | 2 +- js/buy.mjs => e2e/buyer.mjs | 10 ++++-- {go/e2e => e2e}/go.mod | 4 +-- {go/e2e => e2e}/go.sum | 0 {go/e2e => e2e}/pay_test.go | 26 +++++++------- .../no_charge_when_the_resource_fails.txtar | 0 .../testdata/pay_binds_the_invoice_memo.txtar | 0 {go/e2e => e2e}/testdata/pay_weather.txtar | 0 {go/examples => examples}/weather/README.md | 9 ++--- {go/examples => examples}/weather/main.go | 2 +- {go/facilitator => facilitator}/account.go | 0 .../account_test.go | 0 .../conformance_test.go | 0 .../facilitator.go | 0 .../facilitator_test.go | 0 .../interop_test.go | 2 +- .../logcapture_test.go | 0 {go/facilitator => facilitator}/node.go | 0 {go/facilitator => facilitator}/node_test.go | 0 {go/facilitator => facilitator}/ratelimit.go | 0 .../ratelimit_test.go | 0 .../testdata/gnojs/.gitignore | 0 .../testdata/gnojs/package.json | 0 .../testdata/gnojs/regen.sh | 0 .../testdata/gnojs/sign.mjs | 0 .../testdata/gnojs_signed_send.b64 | 0 {go/facilitator => facilitator}/types.go | 0 {go/facilitator => facilitator}/types_test.go | 0 {go/facilitator => facilitator}/verify.go | 0 .../verify_test.go | 0 package.json | 8 ++--- .../server => server/exact}/canonical_test.go | 6 ++-- .../server => server/exact}/flow_test.go | 4 +-- .../exact/server => server/exact}/scheme.go | 4 +-- .../server => server/exact}/scheme_test.go | 4 +-- tsconfig.json | 4 +-- tsdown.config.ts | 6 ++-- 46 files changed, 92 insertions(+), 88 deletions(-) rename {js => client}/README.md (86%) rename {js => client}/src/exact/client.test.ts (100%) rename {js => client}/src/exact/client.ts (100%) rename {js => client}/src/signing.test.ts (100%) rename {js => client}/src/signing.ts (100%) rename {go/cmd => cmd}/gnofacilitator/main.go (99%) rename js/buy.mjs => e2e/buyer.mjs (88%) rename {go/e2e => e2e}/go.mod (98%) rename {go/e2e => e2e}/go.sum (100%) rename {go/e2e => e2e}/pay_test.go (90%) rename {go/e2e => e2e}/testdata/no_charge_when_the_resource_fails.txtar (100%) rename {go/e2e => e2e}/testdata/pay_binds_the_invoice_memo.txtar (100%) rename {go/e2e => e2e}/testdata/pay_weather.txtar (100%) rename {go/examples => examples}/weather/README.md (93%) rename {go/examples => examples}/weather/main.go (98%) rename {go/facilitator => facilitator}/account.go (100%) rename {go/facilitator => facilitator}/account_test.go (100%) rename {go/facilitator => facilitator}/conformance_test.go (100%) rename {go/facilitator => facilitator}/facilitator.go (100%) rename {go/facilitator => facilitator}/facilitator_test.go (100%) rename {go/facilitator => facilitator}/interop_test.go (98%) rename {go/facilitator => facilitator}/logcapture_test.go (100%) rename {go/facilitator => facilitator}/node.go (100%) rename {go/facilitator => facilitator}/node_test.go (100%) rename {go/facilitator => facilitator}/ratelimit.go (100%) rename {go/facilitator => facilitator}/ratelimit_test.go (100%) rename {go/facilitator => facilitator}/testdata/gnojs/.gitignore (100%) rename {go/facilitator => facilitator}/testdata/gnojs/package.json (100%) rename {go/facilitator => facilitator}/testdata/gnojs/regen.sh (100%) rename {go/facilitator => facilitator}/testdata/gnojs/sign.mjs (100%) rename {go/facilitator => facilitator}/testdata/gnojs_signed_send.b64 (100%) rename {go/facilitator => facilitator}/types.go (100%) rename {go/facilitator => facilitator}/types_test.go (100%) rename {go/facilitator => facilitator}/verify.go (100%) rename {go/facilitator => facilitator}/verify_test.go (100%) rename {go/mechanisms/gno/exact/server => server/exact}/canonical_test.go (97%) rename {go/mechanisms/gno/exact/server => server/exact}/flow_test.go (98%) rename {go/mechanisms/gno/exact/server => server/exact}/scheme.go (99%) rename {go/mechanisms/gno/exact/server => server/exact}/scheme_test.go (99%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb0435f..5a3b1db 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,11 +15,11 @@ jobs: with: { go-version: '1.26.5' } - run: go test -short -race ./... - run: go vet ./... - # go/e2e is a separate module, so ./... never reaches it. Vet it here or the + # e2e is a separate module, so ./... never reaches it. Vet it here or the # harness rots unnoticed until someone runs it. - run: go vet ./... - working-directory: go/e2e - - run: gofmt -l -d go | tee /tmp/fmt && test ! -s /tmp/fmt + working-directory: e2e + - run: gofmt -l -d . | tee /tmp/fmt && test ! -s /tmp/fmt # A dependency imported directly but recorded as indirect still builds, so # nothing else here would notice the manifest drifting from the imports. - run: go mod tidy -diff diff --git a/.goreleaser.yaml b/.goreleaser.yaml index ce6c328..97147d1 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -11,7 +11,7 @@ version: 2 builds: - id: gnofacilitator - main: ./go/cmd/gnofacilitator + main: ./cmd/gnofacilitator binary: gnofacilitator # Static: the image is distroless, which has no libc to link against. env: [CGO_ENABLED=0] diff --git a/Makefile b/Makefile index 1d007d8..7eb688b 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,18 @@ # gnoverse/x402 — the gno payment mechanism for x402, its facilitator, and the # harnesses that prove a payment happens. # -# Sources are laid out by language, go/ and js/, but both manifests live at the -# root where tooling expects them: `go test ./...` and `npm test` work without -# changing directory, and setup-go, dependabot and pkg.go.dev all find them. +# Sources are laid out by mechanism role — server/, facilitator/, client/ — and +# both manifests live at the root where tooling expects them: `go test ./...` and +# `npm test` work without changing directory, and setup-go, dependabot and +# pkg.go.dev all find them. # # The test layers are separate on purpose, cheapest first: # test the library. No network, no chain, no npm. # js-test the buyer mechanism, including the sign doc byte-equality check. # test-e2e one real payment, through a real in-memory node and the JS buyer. # -# go/e2e is its own Go module, so `test` cannot start a chain by accident and -# needs no build tag to stay out of the way. +# e2e is its own Go module, so `test` cannot start a chain by accident and needs +# no build tag to stay out of the way. # A failed `go build -o` or `npm run build` must not leave a partial artifact that # a later run mistakes for finished work. @@ -22,12 +23,12 @@ SHELL := /bin/sh GO ?= go NPM ?= npm -E2E := go/e2e +E2E := e2e BIN := bin FACILITATOR := $(BIN)/gnofacilitator -# The JS mechanism emits under js/, not the root dist/ that goreleaser owns. -JS_DIST := js/dist +# The client mechanism emits under client/, not the root dist/ goreleaser owns. +JS_DIST := client/dist JS_CLIENT := $(JS_DIST)/client.mjs # A released binary is stamped by goreleaser; a local one says where it came from. @@ -44,7 +45,7 @@ all: build ## Build everything (default) # cache already decides what to recompile, and a manual source list goes stale. build: ## Build the facilitator into bin/ mkdir -p $(BIN) - $(GO) build -ldflags "$(LDFLAGS)" -o $(FACILITATOR) ./go/cmd/gnofacilitator + $(GO) build -ldflags "$(LDFLAGS)" -o $(FACILITATOR) ./cmd/gnofacilitator test: ## Run the library tests $(GO) test ./... @@ -53,9 +54,10 @@ test: ## Run the library tests test-e2e: $(JS_CLIENT) ## Pay a gno seller end to end against a real in-memory node cd $(E2E) && $(GO) test -count=1 ./... -# The buyers in js/ are stock @x402/* clients. They are opt-in so a Go-only -# checkout is never blocked on npm. buy.mjs imports the mechanism by package name, -# the way a stranger would, so the package must be built and not merely installed. +# The buyer the payment test drives is a stock @x402/* client. These targets are +# opt-in so a Go-only checkout is never blocked on npm. e2e/buyer.mjs imports the +# mechanism by package name, the way a stranger would, so the package must be +# built and not merely installed. js: $(JS_CLIENT) ## Install and build the JS mechanism js-test: $(JS_CLIENT) ## Run the JS mechanism's tests @@ -66,9 +68,9 @@ node_modules: package.json package-lock.json touch $@ # Both levels are listed because Make's $(wildcard) does not recurse, and the -# sources sit one directory deep: js/src/exact/ mirrors the scheme/role subpath +# sources sit one directory deep: client/src/exact/ mirrors the scheme/role subpath # the package publishes. -TS_SOURCES := $(wildcard js/src/*.ts) $(wildcard js/src/exact/*.ts) +TS_SOURCES := $(wildcard client/src/*.ts) $(wildcard client/src/exact/*.ts) # The emitted client, not the directory that holds it. A directory's mtime says # nothing about whether the build finished — and .DELETE_ON_ERROR: cannot rescue @@ -81,10 +83,10 @@ $(JS_CLIENT): node_modules tsconfig.json tsdown.config.ts $(TS_SOURCES) lint: ## Vet both Go modules and check formatting $(GO) vet ./... cd $(E2E) && $(GO) vet ./... - gofmt -l go | tee /dev/stderr | (! read -r first) + gofmt -l . | tee /dev/stderr | (! read -r first) fmt: ## Format the Go sources - gofmt -w go + gofmt -w . # node_modules is a fetched dependency rather than an artifact, and refetching it # is expensive, so it survives. Remove it by hand to start over. @@ -92,7 +94,7 @@ clean: ## Remove build artifacts rm -rf $(BIN) $(JS_DIST) dist install: ## Install the facilitator into GOPATH/bin - $(GO) install -ldflags "$(LDFLAGS)" ./go/cmd/gnofacilitator + $(GO) install -ldflags "$(LDFLAGS)" ./cmd/gnofacilitator help: ## List the targets @grep -hE '^[a-zA-Z0-9_.$$()-]+:.*## ' $(MAKEFILE_LIST) \ diff --git a/README.md b/README.md index 396c70d..2e54af5 100644 --- a/README.md +++ b/README.md @@ -64,12 +64,12 @@ carries them there, not in the body: }] } ``` -Runnable, against a facilitator and a real chain → **[go/examples/weather](go/examples/weather)**. +Runnable, against a facilitator and a real chain → **[examples/weather](examples/weather)**. ## Pay for it ```js -import { ExactGnoScheme } from "./js/dist/client.mjs"; +import { ExactGnoScheme } from "./client/dist/client.mjs"; const client = new x402Client().register("gno:*", new ExactGnoScheme(wallet)); const paid = wrapFetchWithPayment(fetch, client); @@ -78,30 +78,28 @@ const res = await paid("https://api.example.com/weather"); Recognising the 402, selecting an entry, encoding `PAYMENT-SIGNATURE` and retrying are `@x402/fetch`'s own code, unmodified. The payment is signed against the chain id the **offer** names -rather than the one the wallet's node reports — see [js/README.md](js/README.md) for what that does -and does not buy you. +rather than the one the wallet's node reports — see [client/README.md](client/README.md) for what +that does and does not buy you. ## What's in here An x402 mechanism has three roles — **server** (the seller), **facilitator**, **client** (the buyer) -— and the ecosystem names them the same way in both languages: `@x402/evm` publishes +— and the ecosystem names them the same way in every language: `@x402/evm` publishes `./exact/server`, `./exact/facilitator` and `./exact/client`, and its Go module has the matching -directories. This fills the same grid for gno; which language a role is written in is an -implementation detail of that role. +directories. The top level here is that grid. Which language a role is written in is an +implementation detail of the role, so no directory names one. | Role | Path | What | |------|------|------| -| server | `go/mechanisms/gno/exact/server/` | upstream's `SchemeNetworkServer`, implemented for gno | -| facilitator | `go/facilitator/` | verification, settlement, and the `/verify` `/settle` `/supported` service | -| client | `js/src/exact/client.ts` | upstream's `SchemeNetworkClient`, TypeScript | +| server | `server/exact/` | upstream's `SchemeNetworkServer`, implemented for gno | +| facilitator | `facilitator/` | verification, settlement, and the `/verify` `/settle` `/supported` service | +| client | `client/src/exact/client.ts` | upstream's `SchemeNetworkClient`, TypeScript | -Plus `go/cmd/gnofacilitator/` (the binary), `go/examples/weather/` (a priced endpoint you can run -and curl) and `go/e2e/` (one real payment through a real node — its own Go module). +Plus `cmd/gnofacilitator/` (the binary), `examples/weather/` (a priced endpoint you can run and +curl) and `e2e/` (one real payment through a real node — its own Go module). -Under that, sources split by language into `go/` and `js/` the way -[x402-foundation/x402](https://github.com/x402-foundation/x402) splits its own. Both manifests — -`go.mod` and `package.json` — live at the root, so `go test ./...`, `npm test` and every `make` -target run from here. +Both manifests — `go.mod` and `package.json` — live at the root, so `go test ./...`, `npm test` and +every `make` target run from here. ## Payment model diff --git a/js/README.md b/client/README.md similarity index 86% rename from js/README.md rename to client/README.md index d6c3d77..d4a1cf1 100644 --- a/js/README.md +++ b/client/README.md @@ -3,12 +3,13 @@ Pay a gno.land chain with x402. One line on a stock client — no fork, no patch, no upstream change. Build it from the repository root with `make js`. There is no registry release, so a consumer works -against this repository — `buy.mjs` beside these sources is a working buyer that does exactly that. +against this repository — [`e2e/buyer.mjs`](../e2e/buyer.mjs), the buyer the payment test drives, is +a working example of exactly that. ```js import { x402Client } from "@x402/core/client"; import { wrapFetchWithPayment } from "@x402/fetch"; -import { ExactGnoScheme } from "./js/dist/client.mjs"; +import { ExactGnoScheme } from "./client/dist/client.mjs"; const client = new x402Client(); client.register("gno:*", new ExactGnoScheme(wallet)); // ← the only gno-aware line @@ -21,8 +22,8 @@ const res = await paid("https://api.example.com/weather"); required: a gno sequence is sequential, so only the chain knows the next one. Inside this repository the same import resolves through the manifest's `exports` map instead of the -built path, which is what `buy.mjs` does — so the map is exercised by the payment test rather than -only on a future install. +built path, which is what `e2e/buyer.mjs` does — so the map is exercised by the payment test rather +than only on a future install. ## What it is @@ -72,11 +73,11 @@ asset that is not `ugnot`, an amount that is not a positive integer, an absent ` ## Development -Sources are here in `js/src`, laid out by the subpath they publish; the manifest and the emit are at -the repository root, so these run from there. +Sources are here in `client/src`, laid out by the subpath they publish, and the emit lands beside +them; the manifest is at the repository root, so these run from there. ```sh npm run typecheck # TypeScript 7 (the native compiler) npm test # vitest -npm run build # typecheck, then emit js/dist/ +npm run build # typecheck, then emit client/dist/ ``` diff --git a/js/src/exact/client.test.ts b/client/src/exact/client.test.ts similarity index 100% rename from js/src/exact/client.test.ts rename to client/src/exact/client.test.ts diff --git a/js/src/exact/client.ts b/client/src/exact/client.ts similarity index 100% rename from js/src/exact/client.ts rename to client/src/exact/client.ts diff --git a/js/src/signing.test.ts b/client/src/signing.test.ts similarity index 100% rename from js/src/signing.test.ts rename to client/src/signing.test.ts diff --git a/js/src/signing.ts b/client/src/signing.ts similarity index 100% rename from js/src/signing.ts rename to client/src/signing.ts diff --git a/go/cmd/gnofacilitator/main.go b/cmd/gnofacilitator/main.go similarity index 99% rename from go/cmd/gnofacilitator/main.go rename to cmd/gnofacilitator/main.go index f61ccb2..a7f0c79 100644 --- a/go/cmd/gnofacilitator/main.go +++ b/cmd/gnofacilitator/main.go @@ -15,7 +15,7 @@ import ( gnoclient "github.com/gnolang/gno/gno.land/pkg/gnoclient" rpcclient "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" - "github.com/gnoverse/x402/go/facilitator" + "github.com/gnoverse/x402/facilitator" ) // version is stamped at build time with -ldflags "-X main.version=…". A binary diff --git a/js/buy.mjs b/e2e/buyer.mjs similarity index 88% rename from js/buy.mjs rename to e2e/buyer.mjs index acf34a8..49e96de 100644 --- a/js/buy.mjs +++ b/e2e/buyer.mjs @@ -1,12 +1,16 @@ -// A stock x402 client buys from a gno seller. +// A stock x402 client buys from a gno seller. This is the buyer the payment test +// drives: pay_test.go hands it to the txtar scenarios as $X402_BUYER. Running it +// by hand against a live seller is the same path, with a real node. // // The only gno-aware line is the register() call. Everything the protocol // requires — issuing the request, recognising the 402, decoding the offer, // selecting an accepts[] entry, encoding PAYMENT-SIGNATURE, retrying — is // @x402/fetch's own code, unmodified, exactly as it does for Base or Solana. // -// Prints a JSON report on stdout describing what happened, so a caller checks -// the outcome rather than trusting an exit code. Diagnostics go to stderr. +// Prints a JSON report on stdout describing what happened, so a caller checks the +// outcome rather than trusting an exit code. The scenarios assert on status, +// payer and transaction, so those field names are a contract. Diagnostics go to +// stderr. import { GnoJSONRPCProvider, GnoWallet } from "@gnolang/gno-js-client"; import { decodePaymentResponseHeader, wrapFetchWithPayment, x402Client } from "@x402/fetch"; diff --git a/go/e2e/go.mod b/e2e/go.mod similarity index 98% rename from go/e2e/go.mod rename to e2e/go.mod index 2530ae2..de26c34 100644 --- a/go/e2e/go.mod +++ b/e2e/go.mod @@ -7,14 +7,14 @@ // // A nested module is excluded from the parent's ./... , so `go test ./...` at the // root never starts a chain. `make e2e` runs this. -module github.com/gnoverse/x402/go/e2e +module github.com/gnoverse/x402/e2e go 1.26.5 // Never published, and nothing ever `go get`s it — so a relative replace is the // honest way to reach the library under test rather than a version that does not // exist yet. -replace github.com/gnoverse/x402 => ../.. +replace github.com/gnoverse/x402 => .. require ( github.com/gnolang/gno v0.0.0-20260624072255-1822034ddd17 diff --git a/go/e2e/go.sum b/e2e/go.sum similarity index 100% rename from go/e2e/go.sum rename to e2e/go.sum diff --git a/go/e2e/pay_test.go b/e2e/pay_test.go similarity index 90% rename from go/e2e/pay_test.go rename to e2e/pay_test.go index 8206d36..4829368 100644 --- a/go/e2e/pay_test.go +++ b/e2e/pay_test.go @@ -27,8 +27,8 @@ import ( x402http "github.com/x402-foundation/x402/go/v2/http" nethttpmw "github.com/x402-foundation/x402/go/v2/http/nethttp" - "github.com/gnoverse/x402/go/facilitator" - gnoexact "github.com/gnoverse/x402/go/mechanisms/gno/exact/server" + "github.com/gnoverse/x402/facilitator" + gnoexact "github.com/gnoverse/x402/server/exact" ) // The harness's in-memory node serves this chain, so it is the CAIP-2 reference @@ -58,29 +58,27 @@ func TestAStockClientPaysAGnoSeller(t *testing.T) { testscript.Run(t, params) } -// buyerScript locates the JS buyer, two levels up: this module is go/e2e and the -// JS tree is the repository's own js/, a sibling of go/. +// buyerScript is the buyer this harness drives, a sibling of this file: the whole +// claim under test is that a stock client pays, so the buyer is part of the +// harness rather than something borrowed from elsewhere in the tree. // -// A missing buy.mjs FAILS rather than skips. It is committed, so its absence means -// the path is wrong — which is how a repository reshuffle turns this whole test -// into a silent pass. Only the built output is allowed to be absent, because -// building it needs npm and a Go-only checkout should not be blocked on that. +// A missing buyer.mjs FAILS rather than skips, because it is committed. Only the +// built mechanism is allowed to be absent, since building it needs npm and a +// Go-only checkout should not be blocked on that. func buyerScript(t *testing.T) string { t.Helper() wd, err := os.Getwd() require.NoError(t, err) - repo := filepath.Join(wd, "..", "..") - - buyer := filepath.Join(repo, "js", "buy.mjs") + buyer := filepath.Join(wd, "buyer.mjs") _, err = os.Stat(buyer) require.NoError(t, err, "the JS buyer is committed, so this path is wrong") - // buy.mjs imports the mechanism by package name, so the package has to be + // buyer.mjs imports the mechanism by package name, so the package has to be // installed and built, not merely present in the tree. The emit lands in - // js/dist, which the root package.json's exports map points at. - if _, err := os.Stat(filepath.Join(repo, "js", "dist", "client.mjs")); err != nil { + // client/dist, which the root package.json's exports map points at. + if _, err := os.Stat(filepath.Join(wd, "..", "client", "dist", "client.mjs")); err != nil { t.Skipf("the JS mechanism is not built (%v); run `make js`", err) } return buyer diff --git a/go/e2e/testdata/no_charge_when_the_resource_fails.txtar b/e2e/testdata/no_charge_when_the_resource_fails.txtar similarity index 100% rename from go/e2e/testdata/no_charge_when_the_resource_fails.txtar rename to e2e/testdata/no_charge_when_the_resource_fails.txtar diff --git a/go/e2e/testdata/pay_binds_the_invoice_memo.txtar b/e2e/testdata/pay_binds_the_invoice_memo.txtar similarity index 100% rename from go/e2e/testdata/pay_binds_the_invoice_memo.txtar rename to e2e/testdata/pay_binds_the_invoice_memo.txtar diff --git a/go/e2e/testdata/pay_weather.txtar b/e2e/testdata/pay_weather.txtar similarity index 100% rename from go/e2e/testdata/pay_weather.txtar rename to e2e/testdata/pay_weather.txtar diff --git a/go/examples/weather/README.md b/examples/weather/README.md similarity index 93% rename from go/examples/weather/README.md rename to examples/weather/README.md index ee6e67c..66538bd 100644 --- a/go/examples/weather/README.md +++ b/examples/weather/README.md @@ -88,11 +88,12 @@ omitted. ## Paying it -There is no Go client mechanism yet, so a Go buyer cannot pay this. The JS buyer in `js/` can: +There is no Go client mechanism yet, so a Go buyer cannot pay this. The JS buyer the payment test +drives can, and it is the same buyer either way: ```sh make js # install and build the client mechanism -X402_SELLER_URL=http://localhost:8080/weather X402_GNO_RPC= node js/buy.mjs +X402_SELLER_URL=http://localhost:8080/weather X402_GNO_RPC= node e2e/buyer.mjs ``` It registers `ExactGnoScheme` with a stock `@x402/fetch` client — that one `register("gno:*", …)` @@ -100,8 +101,8 @@ call is the only gno-aware line — and signs with the well-known test1 mnemonic first**, and point `X402_GNO_RPC` at the same node the facilitator uses: the buyer reads its account sequence from the chain, so this leg needs a real node rather than the unreachable one a 402 tolerates. -The paid loop itself is covered: `go/e2e` runs this exact configuration — the same middleware, the -same `accepts[]` entry, the same 250000ugnot price, this same `js/buy.mjs` — against an in-process +The paid loop itself is covered: `e2e` runs this exact configuration — the same middleware, the +same `accepts[]` entry, the same 250000ugnot price, this same `buyer.mjs` — against an in-process node on every pull request, and asserts the seller's balance moved. What has not been run is the command sequence on this page: three processes started by hand against a live chain, with a funded account. diff --git a/go/examples/weather/main.go b/examples/weather/main.go similarity index 98% rename from go/examples/weather/main.go rename to examples/weather/main.go index 0ae2a13..6633a11 100644 --- a/go/examples/weather/main.go +++ b/examples/weather/main.go @@ -24,7 +24,7 @@ import ( x402http "github.com/x402-foundation/x402/go/v2/http" nethttpmw "github.com/x402-foundation/x402/go/v2/http/nethttp" - gnoexact "github.com/gnoverse/x402/go/mechanisms/gno/exact/server" + gnoexact "github.com/gnoverse/x402/server/exact" ) // The facilitator is queried while the middleware is being built, so this bounds diff --git a/go/facilitator/account.go b/facilitator/account.go similarity index 100% rename from go/facilitator/account.go rename to facilitator/account.go diff --git a/go/facilitator/account_test.go b/facilitator/account_test.go similarity index 100% rename from go/facilitator/account_test.go rename to facilitator/account_test.go diff --git a/go/facilitator/conformance_test.go b/facilitator/conformance_test.go similarity index 100% rename from go/facilitator/conformance_test.go rename to facilitator/conformance_test.go diff --git a/go/facilitator/facilitator.go b/facilitator/facilitator.go similarity index 100% rename from go/facilitator/facilitator.go rename to facilitator/facilitator.go diff --git a/go/facilitator/facilitator_test.go b/facilitator/facilitator_test.go similarity index 100% rename from go/facilitator/facilitator_test.go rename to facilitator/facilitator_test.go diff --git a/go/facilitator/interop_test.go b/facilitator/interop_test.go similarity index 98% rename from go/facilitator/interop_test.go rename to facilitator/interop_test.go index 58478ec..df5340c 100644 --- a/go/facilitator/interop_test.go +++ b/facilitator/interop_test.go @@ -46,7 +46,7 @@ func interopRequirements() PaymentRequirements { func interopPayload(t *testing.T) SchemePayload { t.Helper() raw, err := os.ReadFile(filepath.Join("testdata", interopFixture)) - require.NoError(t, err, "interop fixture missing — regenerate with go generate ./go/facilitator") + require.NoError(t, err, "interop fixture missing — regenerate with go generate ./facilitator") return SchemePayload{Transaction: strings.TrimSpace(string(raw))} } diff --git a/go/facilitator/logcapture_test.go b/facilitator/logcapture_test.go similarity index 100% rename from go/facilitator/logcapture_test.go rename to facilitator/logcapture_test.go diff --git a/go/facilitator/node.go b/facilitator/node.go similarity index 100% rename from go/facilitator/node.go rename to facilitator/node.go diff --git a/go/facilitator/node_test.go b/facilitator/node_test.go similarity index 100% rename from go/facilitator/node_test.go rename to facilitator/node_test.go diff --git a/go/facilitator/ratelimit.go b/facilitator/ratelimit.go similarity index 100% rename from go/facilitator/ratelimit.go rename to facilitator/ratelimit.go diff --git a/go/facilitator/ratelimit_test.go b/facilitator/ratelimit_test.go similarity index 100% rename from go/facilitator/ratelimit_test.go rename to facilitator/ratelimit_test.go diff --git a/go/facilitator/testdata/gnojs/.gitignore b/facilitator/testdata/gnojs/.gitignore similarity index 100% rename from go/facilitator/testdata/gnojs/.gitignore rename to facilitator/testdata/gnojs/.gitignore diff --git a/go/facilitator/testdata/gnojs/package.json b/facilitator/testdata/gnojs/package.json similarity index 100% rename from go/facilitator/testdata/gnojs/package.json rename to facilitator/testdata/gnojs/package.json diff --git a/go/facilitator/testdata/gnojs/regen.sh b/facilitator/testdata/gnojs/regen.sh similarity index 100% rename from go/facilitator/testdata/gnojs/regen.sh rename to facilitator/testdata/gnojs/regen.sh diff --git a/go/facilitator/testdata/gnojs/sign.mjs b/facilitator/testdata/gnojs/sign.mjs similarity index 100% rename from go/facilitator/testdata/gnojs/sign.mjs rename to facilitator/testdata/gnojs/sign.mjs diff --git a/go/facilitator/testdata/gnojs_signed_send.b64 b/facilitator/testdata/gnojs_signed_send.b64 similarity index 100% rename from go/facilitator/testdata/gnojs_signed_send.b64 rename to facilitator/testdata/gnojs_signed_send.b64 diff --git a/go/facilitator/types.go b/facilitator/types.go similarity index 100% rename from go/facilitator/types.go rename to facilitator/types.go diff --git a/go/facilitator/types_test.go b/facilitator/types_test.go similarity index 100% rename from go/facilitator/types_test.go rename to facilitator/types_test.go diff --git a/go/facilitator/verify.go b/facilitator/verify.go similarity index 100% rename from go/facilitator/verify.go rename to facilitator/verify.go diff --git a/go/facilitator/verify_test.go b/facilitator/verify_test.go similarity index 100% rename from go/facilitator/verify_test.go rename to facilitator/verify_test.go diff --git a/package.json b/package.json index 400212a..10c323b 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "description": "The gno payment mechanism for the x402 'exact' scheme. Register it on a stock x402 client and it signs payments for the gno chain an offer names.", "license": "Apache-2.0", "homepage": "https://gno.land/", - "//repository": "npm resolves the package page's relative links against this, and the published page is the repository root README rather than js/README.md.", + "//repository": "npm resolves the package page's relative links against this, and the published page is the repository root README rather than client/README.md.", "repository": { "type": "git", "url": "git+https://github.com/gnoverse/x402.git" @@ -18,13 +18,13 @@ "payments" ], "files": [ - "js/dist/" + "client/dist/" ], "//exports": "The subpath is the ecosystem's own grid — @x402/evm publishes ./exact/client, ./exact/server and ./exact/facilitator, so a buyer that already imports one reaches for the same path here. gno's server half is Go, so ./exact/client is the only cell this package fills.", "exports": { "./exact/client": { - "types": "./js/dist/client.d.mts", - "default": "./js/dist/client.mjs" + "types": "./client/dist/client.d.mts", + "default": "./client/dist/client.mjs" }, "./package.json": "./package.json" }, diff --git a/go/mechanisms/gno/exact/server/canonical_test.go b/server/exact/canonical_test.go similarity index 97% rename from go/mechanisms/gno/exact/server/canonical_test.go rename to server/exact/canonical_test.go index f210bcd..b15b013 100644 --- a/go/mechanisms/gno/exact/server/canonical_test.go +++ b/server/exact/canonical_test.go @@ -1,4 +1,4 @@ -package server_test +package exact_test import ( "context" @@ -15,8 +15,8 @@ import ( x402http "github.com/x402-foundation/x402/go/v2/http" nethttpmw "github.com/x402-foundation/x402/go/v2/http/nethttp" - "github.com/gnoverse/x402/go/facilitator" - gnoexact "github.com/gnoverse/x402/go/mechanisms/gno/exact/server" + "github.com/gnoverse/x402/facilitator" + gnoexact "github.com/gnoverse/x402/server/exact" ) // keylessNode stands in for the chain. The 402 is decided from the seller's own diff --git a/go/mechanisms/gno/exact/server/flow_test.go b/server/exact/flow_test.go similarity index 98% rename from go/mechanisms/gno/exact/server/flow_test.go rename to server/exact/flow_test.go index df112ca..617804b 100644 --- a/go/mechanisms/gno/exact/server/flow_test.go +++ b/server/exact/flow_test.go @@ -1,4 +1,4 @@ -package server_test +package exact_test import ( "encoding/base64" @@ -16,7 +16,7 @@ import ( nethttpmw "github.com/x402-foundation/x402/go/v2/http/nethttp" "github.com/x402-foundation/x402/go/v2/types" - gnoexact "github.com/gnoverse/x402/go/mechanisms/gno/exact/server" + gnoexact "github.com/gnoverse/x402/server/exact" ) const ( diff --git a/go/mechanisms/gno/exact/server/scheme.go b/server/exact/scheme.go similarity index 99% rename from go/mechanisms/gno/exact/server/scheme.go rename to server/exact/scheme.go index 48bdb82..9c0f93c 100644 --- a/go/mechanisms/gno/exact/server/scheme.go +++ b/server/exact/scheme.go @@ -3,7 +3,7 @@ // // The resource is any endpoint and the chain is only the payment rail, so nothing // here knows about realms or contract calls. -package server +package exact import ( "context" @@ -11,7 +11,7 @@ import ( "fmt" "github.com/gnolang/gno/tm2/pkg/std" - "github.com/gnoverse/x402/go/facilitator" + "github.com/gnoverse/x402/facilitator" x402 "github.com/x402-foundation/x402/go/v2" "github.com/x402-foundation/x402/go/v2/types" ) diff --git a/go/mechanisms/gno/exact/server/scheme_test.go b/server/exact/scheme_test.go similarity index 99% rename from go/mechanisms/gno/exact/server/scheme_test.go rename to server/exact/scheme_test.go index 26d0c07..901b8ee 100644 --- a/go/mechanisms/gno/exact/server/scheme_test.go +++ b/server/exact/scheme_test.go @@ -1,11 +1,11 @@ -package server +package exact import ( "context" "strings" "testing" - "github.com/gnoverse/x402/go/facilitator" + "github.com/gnoverse/x402/facilitator" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" x402 "github.com/x402-foundation/x402/go/v2" diff --git a/tsconfig.json b/tsconfig.json index 39daf22..b7725f8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,8 +22,8 @@ "skipLibCheck": true, "declaration": true, - "outDir": "js/dist", + "outDir": "client/dist", "noEmit": true }, - "include": ["js/src"] + "include": ["client/src"] } diff --git a/tsdown.config.ts b/tsdown.config.ts index 9c70070..2001fa3 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -5,14 +5,14 @@ import { defineConfig } from "tsdown"; // // Dependencies and peers stay external: bundling them would ship a second copy of // the wallet, and of the x402 client the consumer already has. -// The emit lands under js/, beside the sources it is built from, because the +// The emit lands under client/, beside the sources it is built from, because the // repository root's dist/ belongs to goreleaser: it writes artifacts.json there // and CI reads it, while `clean: true` below empties whatever directory this // names. Two tools owning one directory means each run destroys the other's // output, and a missing client is a skipped payment test rather than a failure. export default defineConfig({ - entry: ["js/src/exact/client.ts"], - outDir: "js/dist", + entry: ["client/src/exact/client.ts"], + outDir: "client/dist", format: ["esm"], dts: true, clean: true,