From 4bfc9dcd2e0ab7818500359827ddfdd3bc49cbcd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2026 18:32:42 +0000 Subject: [PATCH 01/37] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 80762ab..d6227e8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 22 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/stainless%2Fstainless-v0-ef33241ec003fafbf4f2ffe434c3d8c2ac0ba929137942185663ff59974d2138.yml openapi_spec_hash: 87bd0d9c684517522cbbbd48bbe8ad83 -config_hash: 4b44da9496c775d2294758cd233f4ecd +config_hash: 038aa9961f259a8ee53e61dfb2454763 From e3cdd774fec0ca4730b49a29b9449e6486689538 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Mar 2026 17:24:46 +0000 Subject: [PATCH 02/37] chore: configure new SDK language --- .stats.yml | 2 +- README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index d6227e8..34d4722 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 22 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/stainless%2Fstainless-v0-ef33241ec003fafbf4f2ffe434c3d8c2ac0ba929137942185663ff59974d2138.yml openapi_spec_hash: 87bd0d9c684517522cbbbd48bbe8ad83 -config_hash: 038aa9961f259a8ee53e61dfb2454763 +config_hash: 319ed5c667c4fa3dd8dc57368293e074 diff --git a/README.md b/README.md index 2b480ee..4033931 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ It is generated with [Stainless](https://www.stainless.com/). Use the Stainless MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application. -[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=%40stainless-api%2Fmcp&config=eyJuYW1lIjoiQHN0YWlubGVzcy1hcGkvbWNwIiwidHJhbnNwb3J0IjoiaHR0cCIsInVybCI6Imh0dHBzOi8vc3RhaW5sZXNzLXYwLnN0bG1jcC5jb20iLCJoZWFkZXJzIjp7Ingtc3RhaW5sZXNzLWFwaS1rZXkiOiJNeSBBUEkgS2V5In19) -[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40stainless-api%2Fmcp%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fstainless-v0.stlmcp.com%22%2C%22headers%22%3A%7B%22x-stainless-api-key%22%3A%22My%20API%20Key%22%7D%7D) +[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=%40stainless-api%2Fsdk-mcp&config=eyJuYW1lIjoiQHN0YWlubGVzcy1hcGkvc2RrLW1jcCIsInRyYW5zcG9ydCI6Imh0dHAiLCJ1cmwiOiJodHRwczovL3N0YWlubGVzcy12MC5zdGxtY3AuY29tIiwiaGVhZGVycyI6eyJ4LXN0YWlubGVzcy1hcGkta2V5IjoiTXkgQVBJIEtleSJ9fQ) +[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40stainless-api%2Fsdk-mcp%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fstainless-v0.stlmcp.com%22%2C%22headers%22%3A%7B%22x-stainless-api-key%22%3A%22My%20API%20Key%22%7D%7D) > Note: You may need to set environment variables in your MCP client. From 9ad46ba5f41104261040448983c95be42f6d60c2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Mar 2026 17:25:03 +0000 Subject: [PATCH 03/37] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 34d4722..0b3b470 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 22 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/stainless%2Fstainless-v0-ef33241ec003fafbf4f2ffe434c3d8c2ac0ba929137942185663ff59974d2138.yml openapi_spec_hash: 87bd0d9c684517522cbbbd48bbe8ad83 -config_hash: 319ed5c667c4fa3dd8dc57368293e074 +config_hash: 63178ec4b1d2ea5636c8619cffcf129b From be313f62c0c1ac7bf5351180152ba28b97bead3a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 03:07:12 +0000 Subject: [PATCH 04/37] chore(internal): tweak CI branches --- .github/workflows/ci.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2803ecd..72dc7f7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,14 @@ name: CI on: push: - branches-ignore: - - 'generated' - - 'codegen/**' - - 'integrated/**' - - 'stl-preview-head/**' - - 'stl-preview-base/**' + branches: + - '**' + - '!integrated/**' + - '!stl-preview-head/**' + - '!stl-preview-base/**' + - '!generated' + - '!codegen/**' + - 'codegen/stl/**' pull_request: branches-ignore: - 'stl-preview-head/**' From ac75b85a15fdf1969bc92c55698cfb4793316b84 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2026 02:01:01 +0000 Subject: [PATCH 05/37] refactor(tests): switch from prism to steady --- CONTRIBUTING.md | 2 +- scripts/mock | 26 +++++++++++++------------- scripts/test | 16 ++++++++-------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 07e2002..0d88723 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,7 +46,7 @@ $ go mod edit -replace github.com/stainless-api/stainless-api-go=/path/to/stainl ## Running tests -Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. +Most tests require you to [set up a mock server](https://github.com/dgellow/steady) against the OpenAPI spec to run the tests. ```sh $ ./scripts/mock diff --git a/scripts/mock b/scripts/mock index bcf3b39..38201de 100755 --- a/scripts/mock +++ b/scripts/mock @@ -19,34 +19,34 @@ fi echo "==> Starting mock server with URL ${URL}" -# Run prism mock on the given spec +# Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism --version + npm exec --package=@stdy/cli@0.19.3 -- steady --version - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & + npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets "$URL" &> .stdy.log & - # Wait for server to come online (max 30s) + # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" attempts=0 - while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + while ! curl --silent --fail "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1; do + if ! kill -0 $! 2>/dev/null; then + echo + cat .stdy.log + exit 1 + fi attempts=$((attempts + 1)) if [ "$attempts" -ge 300 ]; then echo - echo "Timed out waiting for Prism server to start" - cat .prism.log + echo "Timed out waiting for Steady server to start" + cat .stdy.log exit 1 fi echo -n "." sleep 0.1 done - if grep -q "✖ fatal" ".prism.log"; then - cat .prism.log - exit 1 - fi - echo else - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" + npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index c26b122..868e2a9 100755 --- a/scripts/test +++ b/scripts/test @@ -9,8 +9,8 @@ GREEN='\033[0;32m' YELLOW='\033[0;33m' NC='\033[0m' # No Color -function prism_is_running() { - curl --silent "http://localhost:4010" >/dev/null 2>&1 +function steady_is_running() { + curl --silent "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1 } kill_server_on_port() { @@ -25,7 +25,7 @@ function is_overriding_api_base_url() { [ -n "$TEST_API_BASE_URL" ] } -if ! is_overriding_api_base_url && ! prism_is_running ; then +if ! is_overriding_api_base_url && ! steady_is_running ; then # When we exit this script, make sure to kill the background mock server process trap 'kill_server_on_port 4010' EXIT @@ -36,19 +36,19 @@ fi if is_overriding_api_base_url ; then echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" echo -elif ! prism_is_running ; then - echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" +elif ! steady_is_running ; then + echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Steady server" echo -e "running against your OpenAPI spec." echo echo -e "To run the server, pass in the path or url of your OpenAPI" - echo -e "spec to the prism command:" + echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.3 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets${NC}" echo exit 1 else - echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" + echo -e "${GREEN}✔ Mock steady server is running with your OpenAPI spec${NC}" echo fi From d07d7c193749d6405dbb22b4e8aea5993efd07d0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 21 Mar 2026 02:08:42 +0000 Subject: [PATCH 06/37] chore(tests): bump steady to v0.19.4 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 38201de..e1c19e8 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.3 -- steady --version + npm exec --package=@stdy/cli@0.19.4 -- steady --version - npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 868e2a9..06ee1a0 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.3 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.4 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From 1abf6bb3dd5a72b18337c2d7ad557c9dbc5b6240 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 21 Mar 2026 02:16:55 +0000 Subject: [PATCH 07/37] chore(tests): bump steady to v0.19.5 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index e1c19e8..ab814d3 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.4 -- steady --version + npm exec --package=@stdy/cli@0.19.5 -- steady --version - npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 06ee1a0..48d99a6 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.4 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.5 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From 7b2204784376dd5ce909c461dbc7a56e35671415 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 02:09:57 +0000 Subject: [PATCH 08/37] chore(internal): update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c6d0501..8554aff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .prism.log +.stdy.log codegen.log Brewfile.lock.json .idea/ From c3b044efa0112dee1aaddb23bae87622eff99ec8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 02:18:10 +0000 Subject: [PATCH 09/37] chore(tests): bump steady to v0.19.6 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index ab814d3..b319bdf 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.5 -- steady --version + npm exec --package=@stdy/cli@0.19.6 -- steady --version - npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 48d99a6..2e421bc 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.5 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.6 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From 9ff15a28b3fa87a039afbc0530fd1596a2c3628b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 02:08:58 +0000 Subject: [PATCH 10/37] chore(ci): skip lint on metadata-only changes Note that we still want to run tests, as these depend on the metadata. --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 72dc7f7..19a0cb1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,8 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/stainless-v0-go' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: |- github.repository == 'stainless-sdks/stainless-v0-go' && - (github.event_name == 'push' || github.event.pull_request.head.repo.fork) + (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && + (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - uses: actions/checkout@v6 From 9fbed10600a9bf9655c8289b6f0eb18de8a982b6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 02:10:09 +0000 Subject: [PATCH 11/37] chore(tests): bump steady to v0.19.7 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index b319bdf..09eb49f 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.6 -- steady --version + npm exec --package=@stdy/cli@0.19.7 -- steady --version - npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 2e421bc..c875306 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.6 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From 51c93c2ae106ad64285dea87f94f6240f7f56e8a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 02:11:07 +0000 Subject: [PATCH 12/37] chore(internal): support default value struct tag --- build.go | 16 ++++++++-------- builddiagnostic.go | 4 ++-- buildtargetoutput.go | 4 ++-- internal/apiform/tag.go | 26 +++++++++++++++++++++----- internal/apijson/encoder.go | 8 ++++++++ internal/apijson/json_test.go | 17 +++++++++++++++++ internal/apijson/tag.go | 26 +++++++++++++++++++++----- 7 files changed, 79 insertions(+), 22 deletions(-) diff --git a/build.go b/build.go index 33e60ae..a74a77d 100644 --- a/build.go +++ b/build.go @@ -391,7 +391,7 @@ func (r *BuildTargetCommitUnion) UnmarshalJSON(data []byte) error { } type BuildTargetCommitNotStarted struct { - Status constant.NotStarted `json:"status" api:"required"` + Status constant.NotStarted `json:"status" default:"not_started"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { Status respjson.Field @@ -407,7 +407,7 @@ func (r *BuildTargetCommitNotStarted) UnmarshalJSON(data []byte) error { } type BuildTargetCommitQueued struct { - Status constant.Queued `json:"status" api:"required"` + Status constant.Queued `json:"status" default:"queued"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { Status respjson.Field @@ -423,7 +423,7 @@ func (r *BuildTargetCommitQueued) UnmarshalJSON(data []byte) error { } type BuildTargetCommitInProgress struct { - Status constant.InProgress `json:"status" api:"required"` + Status constant.InProgress `json:"status" default:"in_progress"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { Status respjson.Field @@ -448,7 +448,7 @@ type BuildTargetCommitCompleted struct { // "timed_out", "noop", "version_bump". Conclusion string `json:"conclusion" api:"required"` MergeConflictPr BuildTargetCommitCompletedMergeConflictPr `json:"merge_conflict_pr" api:"required"` - Status constant.Completed `json:"status" api:"required"` + Status constant.Completed `json:"status" default:"completed"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { Commit respjson.Field @@ -672,7 +672,7 @@ func (r *CheckStepUnion) UnmarshalJSON(data []byte) error { } type CheckStepNotStarted struct { - Status constant.NotStarted `json:"status" api:"required"` + Status constant.NotStarted `json:"status" default:"not_started"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { Status respjson.Field @@ -688,7 +688,7 @@ func (r *CheckStepNotStarted) UnmarshalJSON(data []byte) error { } type CheckStepQueued struct { - Status constant.Queued `json:"status" api:"required"` + Status constant.Queued `json:"status" default:"queued"` URL string `json:"url" api:"required"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { @@ -706,7 +706,7 @@ func (r *CheckStepQueued) UnmarshalJSON(data []byte) error { } type CheckStepInProgress struct { - Status constant.InProgress `json:"status" api:"required"` + Status constant.InProgress `json:"status" default:"in_progress"` URL string `json:"url" api:"required"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { @@ -729,7 +729,7 @@ type CheckStepCompleted struct { // Any of "success", "failure", "skipped", "cancelled", "action_required", // "neutral", "timed_out". Conclusion string `json:"conclusion" api:"required"` - Status constant.Completed `json:"status" api:"required"` + Status constant.Completed `json:"status" default:"completed"` URL string `json:"url" api:"required"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { diff --git a/builddiagnostic.go b/builddiagnostic.go index c8d2bec..82da63f 100644 --- a/builddiagnostic.go +++ b/builddiagnostic.go @@ -187,7 +187,7 @@ func (r *BuildDiagnosticMoreUnion) UnmarshalJSON(data []byte) error { type BuildDiagnosticMoreMarkdown struct { Markdown string `json:"markdown" api:"required"` - Type constant.Markdown `json:"type" api:"required"` + Type constant.Markdown `json:"type" default:"markdown"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { Markdown respjson.Field @@ -205,7 +205,7 @@ func (r *BuildDiagnosticMoreMarkdown) UnmarshalJSON(data []byte) error { type BuildDiagnosticMoreRaw struct { Raw string `json:"raw" api:"required"` - Type constant.Raw `json:"type" api:"required"` + Type constant.Raw `json:"type" default:"raw"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { Raw respjson.Field diff --git a/buildtargetoutput.go b/buildtargetoutput.go index a735ca5..ea65f1c 100644 --- a/buildtargetoutput.go +++ b/buildtargetoutput.go @@ -133,7 +133,7 @@ func (r *BuildTargetOutputGetResponseUnion) UnmarshalJSON(data []byte) error { } type BuildTargetOutputGetResponseURL struct { - Output constant.URL `json:"output" api:"required"` + Output constant.URL `json:"output" default:"url"` // Any of "node", "typescript", "python", "go", "java", "kotlin", "ruby", // "terraform", "cli", "php", "csharp", "sql", "openapi". Target shared.Target `json:"target" api:"required"` @@ -178,7 +178,7 @@ const ( type BuildTargetOutputGetResponseGit struct { // Temporary GitHub access token Token string `json:"token" api:"required"` - Output constant.Git `json:"output" api:"required"` + Output constant.Git `json:"output" default:"git"` // Git reference (commit SHA, branch, or tag) Ref string `json:"ref" api:"required"` // Any of "node", "typescript", "python", "go", "java", "kotlin", "ruby", diff --git a/internal/apiform/tag.go b/internal/apiform/tag.go index d9915d4..f0c9d14 100644 --- a/internal/apiform/tag.go +++ b/internal/apiform/tag.go @@ -9,13 +9,15 @@ const apiStructTag = "api" const jsonStructTag = "json" const formStructTag = "form" const formatStructTag = "format" +const defaultStructTag = "default" type parsedStructTag struct { - name string - required bool - extras bool - metadata bool - omitzero bool + name string + required bool + extras bool + metadata bool + omitzero bool + defaultValue any } func parseFormStructTag(field reflect.StructField) (tag parsedStructTag, ok bool) { @@ -45,9 +47,23 @@ func parseFormStructTag(field reflect.StructField) (tag parsedStructTag, ok bool } parseApiStructTag(field, &tag) + parseDefaultStructTag(field, &tag) return tag, ok } +func parseDefaultStructTag(field reflect.StructField, tag *parsedStructTag) { + if field.Type.Kind() != reflect.String { + // Only strings are currently supported + return + } + + raw, ok := field.Tag.Lookup(defaultStructTag) + if !ok { + return + } + tag.defaultValue = raw +} + func parseApiStructTag(field reflect.StructField, tag *parsedStructTag) { raw, ok := field.Tag.Lookup(apiStructTag) if !ok { diff --git a/internal/apijson/encoder.go b/internal/apijson/encoder.go index 0decb73..f7eccb9 100644 --- a/internal/apijson/encoder.go +++ b/internal/apijson/encoder.go @@ -12,6 +12,8 @@ import ( "time" "github.com/tidwall/sjson" + + shimjson "github.com/stainless-api/stainless-api-go/internal/encoding/json" ) var encoders sync.Map // map[encoderEntry]encoderFunc @@ -271,6 +273,12 @@ func (e *encoder) newStructTypeEncoder(t reflect.Type) encoderFunc { if err != nil { return nil, err } + if ef.tag.defaultValue != nil && (!field.IsValid() || field.IsZero()) { + encoded, err = shimjson.Marshal(ef.tag.defaultValue) + if err != nil { + return nil, err + } + } if encoded == nil { continue } diff --git a/internal/apijson/json_test.go b/internal/apijson/json_test.go index 19b3614..2853bf9 100644 --- a/internal/apijson/json_test.go +++ b/internal/apijson/json_test.go @@ -614,3 +614,20 @@ func TestEncode(t *testing.T) { }) } } + +type StructWithDefault struct { + Type string `json:"type" default:"foo"` +} + +func TestDefault(t *testing.T) { + value := StructWithDefault{} + expected := `{"type":"foo"}` + + raw, err := Marshal(value) + if err != nil { + t.Fatalf("serialization of %v failed with error %v", value, err) + } + if string(raw) != expected { + t.Fatalf("expected %+#v to serialize to %s but got %s", value, expected, string(raw)) + } +} diff --git a/internal/apijson/tag.go b/internal/apijson/tag.go index 17b2130..efcaf8c 100644 --- a/internal/apijson/tag.go +++ b/internal/apijson/tag.go @@ -8,13 +8,15 @@ import ( const apiStructTag = "api" const jsonStructTag = "json" const formatStructTag = "format" +const defaultStructTag = "default" type parsedStructTag struct { - name string - required bool - extras bool - metadata bool - inline bool + name string + required bool + extras bool + metadata bool + inline bool + defaultValue any } func parseJSONStructTag(field reflect.StructField) (tag parsedStructTag, ok bool) { @@ -42,9 +44,23 @@ func parseJSONStructTag(field reflect.StructField) (tag parsedStructTag, ok bool // the `api` struct tag is only used alongside `json` for custom behaviour parseApiStructTag(field, &tag) + parseDefaultStructTag(field, &tag) return tag, ok } +func parseDefaultStructTag(field reflect.StructField, tag *parsedStructTag) { + if field.Type.Kind() != reflect.String { + // Only strings are currently supported + return + } + + raw, ok := field.Tag.Lookup(defaultStructTag) + if !ok { + return + } + tag.defaultValue = raw +} + func parseApiStructTag(field reflect.StructField, tag *parsedStructTag) { raw, ok := field.Tag.Lookup(apiStructTag) if !ok { From 3440645718d76c103cc8bbc0581f6b6f0701a5da Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 02:15:55 +0000 Subject: [PATCH 13/37] chore(client): fix multipart serialisation of Default() fields --- internal/apiform/encoder.go | 8 ++++++++ internal/apiform/form_test.go | 36 +++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/internal/apiform/encoder.go b/internal/apiform/encoder.go index 40916ad..98c2f83 100644 --- a/internal/apiform/encoder.go +++ b/internal/apiform/encoder.go @@ -265,6 +265,14 @@ func (e *encoder) newStructTypeEncoder(t reflect.Type) encoderFunc { } return typeEncoderFn(key, value, writer) } + } else if ptag.defaultValue != nil { + typeEncoderFn := e.typeEncoder(field.Type) + encoderFn = func(key string, value reflect.Value, writer *multipart.Writer) error { + if value.IsZero() { + return typeEncoderFn(key, reflect.ValueOf(ptag.defaultValue), writer) + } + return typeEncoderFn(key, value, writer) + } } else { encoderFn = e.typeEncoder(field.Type) } diff --git a/internal/apiform/form_test.go b/internal/apiform/form_test.go index a2290b0..3f1e942 100644 --- a/internal/apiform/form_test.go +++ b/internal/apiform/form_test.go @@ -123,6 +123,11 @@ type StructUnion struct { param.APIUnion } +type ConstantStruct struct { + Anchor string `form:"anchor" default:"created_at"` + Seconds int `form:"seconds"` +} + type MultipartMarshalerParent struct { Middle MultipartMarshalerMiddleNext `form:"middle"` } @@ -554,6 +559,37 @@ Content-Disposition: form-data; name="union" Union: UnionTime(time.Date(2010, 05, 23, 0, 0, 0, 0, time.UTC)), }, }, + "constant_zero_value": { + `--xxx +Content-Disposition: form-data; name="anchor" + +created_at +--xxx +Content-Disposition: form-data; name="seconds" + +3600 +--xxx-- +`, + ConstantStruct{ + Seconds: 3600, + }, + }, + "constant_explicit_value": { + `--xxx +Content-Disposition: form-data; name="anchor" + +created_at_override +--xxx +Content-Disposition: form-data; name="seconds" + +3600 +--xxx-- +`, + ConstantStruct{ + Anchor: "created_at_override", + Seconds: 3600, + }, + }, "deeply-nested-struct,brackets": { `--xxx Content-Disposition: form-data; name="middle[middleNext][child]" From 75d6c762a9ff3a61bd85ca3c6849beba49071487 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 02:20:57 +0000 Subject: [PATCH 14/37] fix: prevent duplicate ? in query params --- internal/requestconfig/requestconfig.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/internal/requestconfig/requestconfig.go b/internal/requestconfig/requestconfig.go index 962432c..a8b4e32 100644 --- a/internal/requestconfig/requestconfig.go +++ b/internal/requestconfig/requestconfig.go @@ -122,7 +122,16 @@ func NewRequestConfig(ctx context.Context, method string, u string, body any, ds } params := q.Encode() if params != "" { - u = u + "?" + params + parsed, err := url.Parse(u) + if err != nil { + return nil, err + } + if parsed.RawQuery != "" { + parsed.RawQuery = parsed.RawQuery + "&" + params + u = parsed.String() + } else { + u = u + "?" + params + } } } if body, ok := body.([]byte); ok { From 459c159f2481411fb1295c14570d02a44eeacc66 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 02:26:15 +0000 Subject: [PATCH 15/37] chore: remove unnecessary error check for url parsing --- internal/requestconfig/requestconfig.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/requestconfig/requestconfig.go b/internal/requestconfig/requestconfig.go index a8b4e32..b2f9f0f 100644 --- a/internal/requestconfig/requestconfig.go +++ b/internal/requestconfig/requestconfig.go @@ -122,10 +122,7 @@ func NewRequestConfig(ctx context.Context, method string, u string, body any, ds } params := q.Encode() if params != "" { - parsed, err := url.Parse(u) - if err != nil { - return nil, err - } + parsed, _ := url.Parse(u) if parsed.RawQuery != "" { parsed.RawQuery = parsed.RawQuery + "&" + params u = parsed.String() From fe14b5a12cc6921f134e3cd58f1ddbadbef687d8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 02:27:21 +0000 Subject: [PATCH 16/37] feat(internal): support comma format in multipart form encoding --- internal/apiform/encoder.go | 12 ++++++++++++ scripts/mock | 4 ++-- scripts/test | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/internal/apiform/encoder.go b/internal/apiform/encoder.go index 98c2f83..2ebe896 100644 --- a/internal/apiform/encoder.go +++ b/internal/apiform/encoder.go @@ -183,6 +183,18 @@ func (e *encoder) newPrimitiveTypeEncoder(t reflect.Type) encoderFunc { func (e *encoder) newArrayTypeEncoder(t reflect.Type) encoderFunc { itemEncoder := e.typeEncoder(t.Elem()) keyFn := e.arrayKeyEncoder() + if e.arrayFmt == "comma" { + return func(key string, v reflect.Value, writer *multipart.Writer) error { + if v.Len() == 0 { + return nil + } + elements := make([]string, v.Len()) + for i := 0; i < v.Len(); i++ { + elements[i] = fmt.Sprint(v.Index(i).Interface()) + } + return writer.WriteField(key, strings.Join(elements, ",")) + } + } return func(key string, v reflect.Value, writer *multipart.Writer) error { if keyFn == nil { return fmt.Errorf("apiform: unsupported array format") diff --git a/scripts/mock b/scripts/mock index 09eb49f..290e21b 100755 --- a/scripts/mock +++ b/scripts/mock @@ -24,7 +24,7 @@ if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout npm exec --package=@stdy/cli@0.19.7 -- steady --version - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index c875306..952b743 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 From fc8ba99ac8e0b5911fb116128c95f3b834b6e0eb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 28 Mar 2026 02:22:11 +0000 Subject: [PATCH 17/37] chore(ci): support opting out of skipping builds on metadata-only commits --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19a0cb1..aa38cca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,8 +24,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/stainless-v0-go' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: |- github.repository == 'stainless-sdks/stainless-v0-go' && - (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && - (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') + (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - uses: actions/checkout@v6 From dff036b0396ec75667e1880662a6d7d43cade57d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 28 Mar 2026 02:30:31 +0000 Subject: [PATCH 18/37] chore: update docs for api:"required" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4033931..94b5ea4 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ func main() { The stainless library uses the [`omitzero`](https://tip.golang.org/doc/go1.24#encodingjsonpkgencodingjson) semantics from the Go 1.24+ `encoding/json` release for request fields. -Required primitive fields (`int64`, `string`, etc.) feature the tag \`json:"...,required"\`. These +Required primitive fields (`int64`, `string`, etc.) feature the tag \`api:"required"\`. These fields are always serialized, even their zero values. Optional primitive types are wrapped in a `param.Opt[T]`. These fields can be set with the provided constructors, `stainless.String(string)`, `stainless.Int(int64)`, etc. From 34fa7961079423a9653316c1d2e68023fdf2b76e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 02:29:44 +0000 Subject: [PATCH 19/37] chore(tests): bump steady to v0.20.1 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 290e21b..15c2994 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.7 -- steady --version + npm exec --package=@stdy/cli@0.20.1 -- steady --version - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 952b743..31bb2e8 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 From 5b8d8a7f2074c8cbba1e057cba98c78553a16b48 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 02:35:53 +0000 Subject: [PATCH 20/37] chore(tests): bump steady to v0.20.2 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 15c2994..5cd7c15 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.20.1 -- steady --version + npm exec --package=@stdy/cli@0.20.2 -- steady --version - npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 31bb2e8..c3a0d2c 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.2 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 From 199c263e6b0ad82369d0a957ea3b06856f9cd8b3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 02:49:13 +0000 Subject: [PATCH 21/37] fix: fix for union type names --- build.go | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/build.go b/build.go index a74a77d..87c97c4 100644 --- a/build.go +++ b/build.go @@ -164,17 +164,16 @@ func (r *Build) UnmarshalJSON(data []byte) error { } // BuildDocumentedSpecUnion contains all possible properties and values from -// [BuildDocumentedSpecObject], [BuildDocumentedSpecObject]. +// [BuildDocumentedSpecObject], [BuildDocumentedSpecObject2]. // // Use the methods beginning with 'As' to cast the union to one of its variants. type BuildDocumentedSpecUnion struct { // This field is from variant [BuildDocumentedSpecObject]. Content string `json:"content"` - // This field is from variant [BuildDocumentedSpecObject]. - Type string `json:"type"` - // This field is from variant [BuildDocumentedSpecObject]. + Type string `json:"type"` + // This field is from variant [BuildDocumentedSpecObject2]. Expires time.Time `json:"expires"` - // This field is from variant [BuildDocumentedSpecObject]. + // This field is from variant [BuildDocumentedSpecObject2]. URL string `json:"url"` JSON struct { Content respjson.Field @@ -190,7 +189,7 @@ func (u BuildDocumentedSpecUnion) AsBuildDocumentedSpecObject() (v BuildDocument return } -func (u BuildDocumentedSpecUnion) AsVariant2() (v BuildDocumentedSpecObject) { +func (u BuildDocumentedSpecUnion) AsBuildDocumentedSpecObject2() (v BuildDocumentedSpecObject2) { apijson.UnmarshalRoot(json.RawMessage(u.JSON.raw), &v) return } @@ -221,6 +220,27 @@ func (r *BuildDocumentedSpecObject) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) } +type BuildDocumentedSpecObject2 struct { + Expires time.Time `json:"expires" api:"required" format:"date-time"` + // Any of "url". + Type string `json:"type" api:"required"` + URL string `json:"url" api:"required"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Expires respjson.Field + Type respjson.Field + URL respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r BuildDocumentedSpecObject2) RawJSON() string { return r.JSON.raw } +func (r *BuildDocumentedSpecObject2) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + type BuildObject string const ( From 940e9283bcbf16347fd8cb8f9c6d83cfb08fb227 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 10 Apr 2026 17:42:00 +0000 Subject: [PATCH 22/37] chore: update probot --- .stats.yml | 2 +- build.go | 65 ++++++++++++++++++++++++++++++++---- shared/constant/constants.go | 3 ++ 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0b3b470..a706af6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 22 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/stainless%2Fstainless-v0-ef33241ec003fafbf4f2ffe434c3d8c2ac0ba929137942185663ff59974d2138.yml -openapi_spec_hash: 87bd0d9c684517522cbbbd48bbe8ad83 +openapi_spec_hash: f4a68de1cb28c33de769c08f68f543ab config_hash: 63178ec4b1d2ea5636c8619cffcf129b diff --git a/build.go b/build.go index 87c97c4..d00ba1b 100644 --- a/build.go +++ b/build.go @@ -318,14 +318,15 @@ func (r *BuildTarget) UnmarshalJSON(data []byte) error { } // BuildTargetCommitUnion contains all possible properties and values from -// [BuildTargetCommitNotStarted], [BuildTargetCommitQueued], -// [BuildTargetCommitInProgress], [BuildTargetCommitCompleted]. +// [BuildTargetCommitNotStarted], [BuildTargetCommitWaiting], +// [BuildTargetCommitQueued], [BuildTargetCommitInProgress], +// [BuildTargetCommitCompleted]. // // Use the [BuildTargetCommitUnion.AsAny] method to switch on the variant. // // Use the methods beginning with 'As' to cast the union to one of its variants. type BuildTargetCommitUnion struct { - // Any of "not_started", "queued", "in_progress", "completed". + // Any of "not_started", "waiting", "queued", "in_progress", "completed". Status string `json:"status"` // This field is from variant [BuildTargetCommitCompleted]. Commit shared.Commit `json:"commit"` @@ -355,6 +356,7 @@ type anyBuildTargetCommit interface { } func (BuildTargetCommitNotStarted) implBuildTargetCommitUnion() {} +func (BuildTargetCommitWaiting) implBuildTargetCommitUnion() {} func (BuildTargetCommitQueued) implBuildTargetCommitUnion() {} func (BuildTargetCommitInProgress) implBuildTargetCommitUnion() {} func (BuildTargetCommitCompleted) implBuildTargetCommitUnion() {} @@ -363,6 +365,7 @@ func (BuildTargetCommitCompleted) implBuildTargetCommitUnion() {} // // switch variant := BuildTargetCommitUnion.AsAny().(type) { // case stainless.BuildTargetCommitNotStarted: +// case stainless.BuildTargetCommitWaiting: // case stainless.BuildTargetCommitQueued: // case stainless.BuildTargetCommitInProgress: // case stainless.BuildTargetCommitCompleted: @@ -373,6 +376,8 @@ func (u BuildTargetCommitUnion) AsAny() anyBuildTargetCommit { switch u.Status { case "not_started": return u.AsNotStarted() + case "waiting": + return u.AsWaiting() case "queued": return u.AsQueued() case "in_progress": @@ -388,6 +393,11 @@ func (u BuildTargetCommitUnion) AsNotStarted() (v BuildTargetCommitNotStarted) { return } +func (u BuildTargetCommitUnion) AsWaiting() (v BuildTargetCommitWaiting) { + apijson.UnmarshalRoot(json.RawMessage(u.JSON.raw), &v) + return +} + func (u BuildTargetCommitUnion) AsQueued() (v BuildTargetCommitQueued) { apijson.UnmarshalRoot(json.RawMessage(u.JSON.raw), &v) return @@ -426,6 +436,22 @@ func (r *BuildTargetCommitNotStarted) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) } +type BuildTargetCommitWaiting struct { + Status constant.Waiting `json:"status" default:"waiting"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Status respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r BuildTargetCommitWaiting) RawJSON() string { return r.JSON.raw } +func (r *BuildTargetCommitWaiting) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + type BuildTargetCommitQueued struct { Status constant.Queued `json:"status" default:"queued"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. @@ -606,14 +632,14 @@ const ( ) // CheckStepUnion contains all possible properties and values from -// [CheckStepNotStarted], [CheckStepQueued], [CheckStepInProgress], -// [CheckStepCompleted]. +// [CheckStepNotStarted], [CheckStepWaiting], [CheckStepQueued], +// [CheckStepInProgress], [CheckStepCompleted]. // // Use the [CheckStepUnion.AsAny] method to switch on the variant. // // Use the methods beginning with 'As' to cast the union to one of its variants. type CheckStepUnion struct { - // Any of "not_started", "queued", "in_progress", "completed". + // Any of "not_started", "waiting", "queued", "in_progress", "completed". Status string `json:"status"` URL string `json:"url"` // This field is from variant [CheckStepCompleted]. @@ -636,6 +662,7 @@ type anyCheckStep interface { } func (CheckStepNotStarted) implCheckStepUnion() {} +func (CheckStepWaiting) implCheckStepUnion() {} func (CheckStepQueued) implCheckStepUnion() {} func (CheckStepInProgress) implCheckStepUnion() {} func (CheckStepCompleted) implCheckStepUnion() {} @@ -644,6 +671,7 @@ func (CheckStepCompleted) implCheckStepUnion() {} // // switch variant := CheckStepUnion.AsAny().(type) { // case stainless.CheckStepNotStarted: +// case stainless.CheckStepWaiting: // case stainless.CheckStepQueued: // case stainless.CheckStepInProgress: // case stainless.CheckStepCompleted: @@ -654,6 +682,8 @@ func (u CheckStepUnion) AsAny() anyCheckStep { switch u.Status { case "not_started": return u.AsNotStarted() + case "waiting": + return u.AsWaiting() case "queued": return u.AsQueued() case "in_progress": @@ -669,6 +699,11 @@ func (u CheckStepUnion) AsNotStarted() (v CheckStepNotStarted) { return } +func (u CheckStepUnion) AsWaiting() (v CheckStepWaiting) { + apijson.UnmarshalRoot(json.RawMessage(u.JSON.raw), &v) + return +} + func (u CheckStepUnion) AsQueued() (v CheckStepQueued) { apijson.UnmarshalRoot(json.RawMessage(u.JSON.raw), &v) return @@ -707,6 +742,24 @@ func (r *CheckStepNotStarted) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) } +type CheckStepWaiting struct { + Status constant.Waiting `json:"status" default:"waiting"` + URL string `json:"url" api:"required"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Status respjson.Field + URL respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r CheckStepWaiting) RawJSON() string { return r.JSON.raw } +func (r *CheckStepWaiting) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + type CheckStepQueued struct { Status constant.Queued `json:"status" default:"queued"` URL string `json:"url" api:"required"` diff --git a/shared/constant/constants.go b/shared/constant/constants.go index 7aa9ba1..416006b 100644 --- a/shared/constant/constants.go +++ b/shared/constant/constants.go @@ -26,6 +26,7 @@ type NotStarted string // Always "not_started" type Queued string // Always "queued" type Raw string // Always "raw" type URL string // Always "url" +type Waiting string // Always "waiting" func (c Completed) Default() Completed { return "completed" } func (c Git) Default() Git { return "git" } @@ -35,6 +36,7 @@ func (c NotStarted) Default() NotStarted { return "not_started" } func (c Queued) Default() Queued { return "queued" } func (c Raw) Default() Raw { return "raw" } func (c URL) Default() URL { return "url" } +func (c Waiting) Default() Waiting { return "waiting" } func (c Completed) MarshalJSON() ([]byte, error) { return marshalString(c) } func (c Git) MarshalJSON() ([]byte, error) { return marshalString(c) } @@ -44,6 +46,7 @@ func (c NotStarted) MarshalJSON() ([]byte, error) { return marshalString(c) } func (c Queued) MarshalJSON() ([]byte, error) { return marshalString(c) } func (c Raw) MarshalJSON() ([]byte, error) { return marshalString(c) } func (c URL) MarshalJSON() ([]byte, error) { return marshalString(c) } +func (c Waiting) MarshalJSON() ([]byte, error) { return marshalString(c) } type constant[T any] interface { Constant[T] From 4743a516f61b85041965a089a1277a2cc86de7f7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 11 Apr 2026 02:38:02 +0000 Subject: [PATCH 23/37] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index a706af6..b25db43 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 22 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/stainless%2Fstainless-v0-ef33241ec003fafbf4f2ffe434c3d8c2ac0ba929137942185663ff59974d2138.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/stainless%2Fstainless-v0-539ddca207b01349e653c0061c35a9169f655e91f0f024008f93cca943c23e3f.yml openapi_spec_hash: f4a68de1cb28c33de769c08f68f543ab config_hash: 63178ec4b1d2ea5636c8619cffcf129b From a5e75636ac7bf44dfb46792921e22e0f161f96f0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 02:41:07 +0000 Subject: [PATCH 24/37] chore(tests): bump steady to v0.22.1 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 5cd7c15..feebe5e 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.20.2 -- steady --version + npm exec --package=@stdy/cli@0.22.1 -- steady --version - npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index c3a0d2c..14cb1af 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.2 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.22.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 From 7a3c829bc1b041174a381b0123cb75a9eee24125 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 02:13:52 +0000 Subject: [PATCH 25/37] chore(internal): more robust bootstrap script --- scripts/bootstrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/bootstrap b/scripts/bootstrap index 5ab3066..46547f1 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,7 +4,7 @@ set -e cd "$(dirname "$0")/.." -if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "${SKIP_BREW:-}" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { echo -n "==> Install Homebrew dependencies? (y/N): " read -r response From b0fcaa21da3ce9784ab5df50f2ad53c1624eee63 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 02:02:20 +0000 Subject: [PATCH 26/37] feat(go): add default http client with timeout --- client.go | 2 +- default_http_client.go | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 default_http_client.go diff --git a/client.go b/client.go index 98ecebd..2b2c1cf 100644 --- a/client.go +++ b/client.go @@ -26,7 +26,7 @@ type Client struct { // DefaultClientOptions read from the environment (STAINLESS_API_KEY, // STAINLESS_BASE_URL). This should be used to initialize new clients. func DefaultClientOptions() []option.RequestOption { - defaults := []option.RequestOption{option.WithEnvironmentProduction()} + defaults := []option.RequestOption{option.WithHTTPClient(defaultHTTPClient()), option.WithEnvironmentProduction()} if o, ok := os.LookupEnv("STAINLESS_BASE_URL"); ok { defaults = append(defaults, option.WithBaseURL(o)) } diff --git a/default_http_client.go b/default_http_client.go new file mode 100644 index 0000000..c0b9fe4 --- /dev/null +++ b/default_http_client.go @@ -0,0 +1,24 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package stainless + +import ( + "net/http" + "time" +) + +// defaultResponseHeaderTimeout bounds the time between a fully written request +// and the server's response headers. It does not apply to the response body, +// so long-running streams are unaffected. Without this, a server that accepts +// the connection but never responds would hang the request indefinitely. +const defaultResponseHeaderTimeout = 10 * time.Minute + +// defaultHTTPClient returns an [*http.Client] used when the caller does not +// supply one via [option.WithHTTPClient]. It clones [http.DefaultTransport] +// and adds a [http.Transport.ResponseHeaderTimeout] so stuck connections +// fail fast instead of compounding across retries. +func defaultHTTPClient() *http.Client { + transport := http.DefaultTransport.(*http.Transport).Clone() + transport.ResponseHeaderTimeout = defaultResponseHeaderTimeout + return &http.Client{Transport: transport} +} From 49cf293f1cba3628ff6690f853178c01e2560c85 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 02:08:18 +0000 Subject: [PATCH 27/37] feat: support setting headers via env --- client.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/client.go b/client.go index 2b2c1cf..766c7b2 100644 --- a/client.go +++ b/client.go @@ -7,6 +7,7 @@ import ( "net/http" "os" "slices" + "strings" "github.com/stainless-api/stainless-api-go/internal/requestconfig" "github.com/stainless-api/stainless-api-go/option" @@ -33,6 +34,14 @@ func DefaultClientOptions() []option.RequestOption { if o, ok := os.LookupEnv("STAINLESS_API_KEY"); ok { defaults = append(defaults, option.WithAPIKey(o)) } + if o, ok := os.LookupEnv("STAINLESS_CUSTOM_HEADERS"); ok { + for _, line := range strings.Split(o, "\n") { + colon := strings.Index(line, ":") + if colon >= 0 { + defaults = append(defaults, option.WithHeader(strings.TrimSpace(line[:colon]), strings.TrimSpace(line[colon+1:]))) + } + } + } return defaults } From 20d5b363d37468b42cced3c63cd71240f1d6947b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 02:22:08 +0000 Subject: [PATCH 28/37] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index b25db43..3cfe0e7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 22 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/stainless%2Fstainless-v0-539ddca207b01349e653c0061c35a9169f655e91f0f024008f93cca943c23e3f.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/stainless/stainless-v0-539ddca207b01349e653c0061c35a9169f655e91f0f024008f93cca943c23e3f.yml openapi_spec_hash: f4a68de1cb28c33de769c08f68f543ab config_hash: 63178ec4b1d2ea5636c8619cffcf129b From fce5a8bf1c64ea3823899b426b4ba6919ce6cc21 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 02:12:47 +0000 Subject: [PATCH 29/37] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 3cfe0e7..21cca53 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 22 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/stainless/stainless-v0-539ddca207b01349e653c0061c35a9169f655e91f0f024008f93cca943c23e3f.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/stainless/stainless-v0-4dbed62ce27b115068721f69e83b644ca0b7922e5be12b6469c0c054a371a170.yml openapi_spec_hash: f4a68de1cb28c33de769c08f68f543ab config_hash: 63178ec4b1d2ea5636c8619cffcf129b From ed95de5b4950f55c49a33c9f7aa0762e5b9a7eca Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 02:15:00 +0000 Subject: [PATCH 30/37] chore: avoid embedding reflect.Type for dead code elimination --- internal/apiform/encoder.go | 4 ++-- internal/apijson/decoder.go | 4 ++-- internal/apijson/encoder.go | 4 ++-- internal/apiquery/encoder.go | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/apiform/encoder.go b/internal/apiform/encoder.go index 2ebe896..f8bbc16 100644 --- a/internal/apiform/encoder.go +++ b/internal/apiform/encoder.go @@ -58,7 +58,7 @@ type encoderField struct { } type encoderEntry struct { - reflect.Type + typ reflect.Type dateFormat string arrayFmt string root bool @@ -76,7 +76,7 @@ func (e *encoder) marshal(value any, writer *multipart.Writer) error { func (e *encoder) typeEncoder(t reflect.Type) encoderFunc { entry := encoderEntry{ - Type: t, + typ: t, dateFormat: e.dateFormat, arrayFmt: e.arrayFmt, root: e.root, diff --git a/internal/apijson/decoder.go b/internal/apijson/decoder.go index a96464a..465a3cc 100644 --- a/internal/apijson/decoder.go +++ b/internal/apijson/decoder.go @@ -80,7 +80,7 @@ type decoderField struct { } type decoderEntry struct { - reflect.Type + typ reflect.Type dateFormat string root bool } @@ -108,7 +108,7 @@ func (d *decoderBuilder) unmarshalWithExactness(raw []byte, to any) (exactness, func (d *decoderBuilder) typeDecoder(t reflect.Type) decoderFunc { entry := decoderEntry{ - Type: t, + typ: t, dateFormat: d.dateFormat, root: d.root, } diff --git a/internal/apijson/encoder.go b/internal/apijson/encoder.go index f7eccb9..ed0b4f3 100644 --- a/internal/apijson/encoder.go +++ b/internal/apijson/encoder.go @@ -46,7 +46,7 @@ type encoderField struct { } type encoderEntry struct { - reflect.Type + typ reflect.Type dateFormat string root bool } @@ -63,7 +63,7 @@ func (e *encoder) marshal(value any) ([]byte, error) { func (e *encoder) typeEncoder(t reflect.Type) encoderFunc { entry := encoderEntry{ - Type: t, + typ: t, dateFormat: e.dateFormat, root: e.root, } diff --git a/internal/apiquery/encoder.go b/internal/apiquery/encoder.go index a843d58..ebd476c 100644 --- a/internal/apiquery/encoder.go +++ b/internal/apiquery/encoder.go @@ -29,7 +29,7 @@ type encoderField struct { } type encoderEntry struct { - reflect.Type + typ reflect.Type dateFormat string root bool settings QuerySettings @@ -42,7 +42,7 @@ type Pair struct { func (e *encoder) typeEncoder(t reflect.Type) encoderFunc { entry := encoderEntry{ - Type: t, + typ: t, dateFormat: e.dateFormat, root: e.root, settings: e.settings, From 27ed10ebca2c7b1c7ac6d97997d0f829d3b77bad Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 8 May 2026 02:12:04 +0000 Subject: [PATCH 31/37] chore: redact api-key headers in debug logs --- option/middleware.go | 46 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/option/middleware.go b/option/middleware.go index 8ec9dd6..4be0987 100644 --- a/option/middleware.go +++ b/option/middleware.go @@ -8,6 +8,10 @@ import ( "net/http/httputil" ) +// sensitiveLogHeaders are redacted before request and response content is +// written to the debug logger. +var sensitiveLogHeaders = []string{"authorization", "api-key", "x-api-key", "cookie", "set-cookie"} + // WithDebugLog logs the HTTP request and response content. // If the logger parameter is nil, it uses the default logger. // @@ -20,7 +24,7 @@ func WithDebugLog(logger *log.Logger) RequestOption { logger = log.Default() } - if reqBytes, err := httputil.DumpRequest(req, true); err == nil { + if reqBytes, err := dumpRedactedRequest(req); err == nil { logger.Printf("Request Content:\n%s\n", reqBytes) } @@ -29,10 +33,48 @@ func WithDebugLog(logger *log.Logger) RequestOption { return resp, err } - if respBytes, err := httputil.DumpResponse(resp, true); err == nil { + if respBytes, err := dumpRedactedResponse(resp); err == nil { logger.Printf("Response Content:\n%s\n", respBytes) } return resp, err }) } + +// dumpRedactedRequest dumps req with sensitive headers replaced. The +// original headers are restored via defer so a panic in DumpRequest cannot +// leak the placeholder map into the live request sent downstream. +func dumpRedactedRequest(req *http.Request) ([]byte, error) { + origHeaders := req.Header + req.Header = redactDebugHeaders(origHeaders) + defer func() { req.Header = origHeaders }() + return httputil.DumpRequest(req, true) +} + +func dumpRedactedResponse(resp *http.Response) ([]byte, error) { + origHeaders := resp.Header + resp.Header = redactDebugHeaders(origHeaders) + defer func() { resp.Header = origHeaders }() + return httputil.DumpResponse(resp, true) +} + +func redactDebugHeaders(headers http.Header) http.Header { + var redacted http.Header + for _, name := range sensitiveLogHeaders { + values := headers.Values(name) + if len(values) == 0 { + continue + } + if redacted == nil { + redacted = headers.Clone() + } + redacted.Del(name) + for range values { + redacted.Add(name, "***") + } + } + if redacted == nil { + return headers + } + return redacted +} From 7eaefa2b69e4f6a98a5baa1dd6a78ce334a3394a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 8 May 2026 02:12:51 +0000 Subject: [PATCH 32/37] fix(go): avoid panic when http.DefaultTransport is wrapped MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit defaultHTTPClient performed an unchecked type assertion on http.DefaultTransport, which panicked for any caller that had wrapped the global transport (e.g. otelhttp.NewTransport for distributed tracing). When the assertion fails, fall back to the wrapped RoundTripper as-is — preserving the caller's wrapping at the cost of ResponseHeaderTimeout, which is strictly better than panicking. --- default_http_client.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/default_http_client.go b/default_http_client.go index c0b9fe4..436bf5a 100644 --- a/default_http_client.go +++ b/default_http_client.go @@ -14,11 +14,17 @@ import ( const defaultResponseHeaderTimeout = 10 * time.Minute // defaultHTTPClient returns an [*http.Client] used when the caller does not -// supply one via [option.WithHTTPClient]. It clones [http.DefaultTransport] -// and adds a [http.Transport.ResponseHeaderTimeout] so stuck connections -// fail fast instead of compounding across retries. +// supply one via [option.WithHTTPClient]. When [http.DefaultTransport] is the +// stdlib [*http.Transport], it is cloned and a [http.Transport.ResponseHeaderTimeout] +// is set so stuck connections fail fast instead of compounding across retries. +// If [http.DefaultTransport] has been wrapped (for example by otelhttp for +// distributed tracing), the wrapping is preserved and the header timeout is +// skipped. func defaultHTTPClient() *http.Client { - transport := http.DefaultTransport.(*http.Transport).Clone() - transport.ResponseHeaderTimeout = defaultResponseHeaderTimeout - return &http.Client{Transport: transport} + if t, ok := http.DefaultTransport.(*http.Transport); ok { + t = t.Clone() + t.ResponseHeaderTimeout = defaultResponseHeaderTimeout + return &http.Client{Transport: t} + } + return &http.Client{Transport: http.DefaultTransport} } From 0060599fcb6cc027331f9b2cd5308c203d22b220 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 02:05:04 +0000 Subject: [PATCH 33/37] chore(internal): codegen related update --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa38cca..e2a5ee3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,14 +26,14 @@ jobs: github.repository == 'stainless-sdks/stainless-v0-go' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Get GitHub OIDC Token if: |- github.repository == 'stainless-sdks/stainless-v0-go' && !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: core.setOutput('github_token', await core.getIDToken()); @@ -53,10 +53,10 @@ jobs: if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup go - uses: actions/setup-go@v5 + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 with: go-version-file: ./go.mod @@ -68,10 +68,10 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/stainless-v0-go' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup go - uses: actions/setup-go@v5 + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 with: go-version-file: ./go.mod From 0db8f716cdd728dd59b8bac086c65aa050e4d0b8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 02:11:35 +0000 Subject: [PATCH 34/37] chore(internal): codegen related update --- internal/encoding/json/encode.go | 21 ++-- internal/encoding/json/indent.go | 17 ++- internal/encoding/json/opt.go | 24 +++++ internal/encoding/json/stream.go | 53 +++++----- internal/encoding/json/time.go | 2 +- packages/param/encoder.go | 4 +- packages/param/encoder_test.go | 176 +++++++++++++++++++++++++++++++ 7 files changed, 260 insertions(+), 37 deletions(-) create mode 100644 internal/encoding/json/opt.go diff --git a/internal/encoding/json/encode.go b/internal/encoding/json/encode.go index b9a9d4b..2da9f4e 100644 --- a/internal/encoding/json/encode.go +++ b/internal/encoding/json/encode.go @@ -173,15 +173,21 @@ import ( // JSON cannot represent cyclic data structures and Marshal does not // handle them. Passing cyclic structures to Marshal will result in // an error. -func Marshal(v any) ([]byte, error) { +// EDIT(begin): add optimization options +func Marshal(v any, opts ...Option) ([]byte, error) { + // EDIT(end): add optimization options e := newEncodeState() defer encodeStatePool.Put(e) - // SHIM(begin): don't escape HTML by default - err := e.marshal(v, encOpts{escapeHTML: shims.EscapeHTMLByDefault}) + // EDIT(begin): don't escape HTML by default, and apply options + encOpts := encOpts{escapeHTML: shims.EscapeHTMLByDefault} + if opts != nil { + encOpts = encOpts.apply(opts...) + } + err := e.marshal(v, encOpts) // ORIGINAL: // err := e.marshal(v, encOpts{escapeHTML: true}) - // SHIM(end) + // EDIT(end) if err != nil { return nil, err } @@ -352,6 +358,9 @@ type encOpts struct { // EDIT(begin): save the timefmt timefmt string // EDIT(end) + // EDIT(begin): add optimization to skip compaction + skipCompaction bool + // EDIT(end) } type encoderFunc func(e *encodeState, v reflect.Value, opts encOpts) @@ -483,7 +492,7 @@ func marshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) { if err == nil { e.Grow(len(b)) out := e.AvailableBuffer() - out, err = appendCompact(out, b, opts.escapeHTML) + out, err = appendCompact(out, b, opts) e.Buffer.Write(out) } if err != nil { @@ -509,7 +518,7 @@ func addrMarshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) { if err == nil { e.Grow(len(b)) out := e.AvailableBuffer() - out, err = appendCompact(out, b, opts.escapeHTML) + out, err = appendCompact(out, b, opts) e.Buffer.Write(out) } if err != nil { diff --git a/internal/encoding/json/indent.go b/internal/encoding/json/indent.go index 01bfdf6..c9d6ca5 100644 --- a/internal/encoding/json/indent.go +++ b/internal/encoding/json/indent.go @@ -4,7 +4,9 @@ package json -import "bytes" +import ( + "bytes" +) // HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029 // characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029 @@ -41,12 +43,21 @@ func appendHTMLEscape(dst, src []byte) []byte { func Compact(dst *bytes.Buffer, src []byte) error { dst.Grow(len(src)) b := dst.AvailableBuffer() - b, err := appendCompact(b, src, false) + b, err := appendCompact(b, src, encOpts{}) dst.Write(b) return err } -func appendCompact(dst, src []byte, escape bool) ([]byte, error) { +func appendCompact(dst, src []byte, opts encOpts) ([]byte, error) { + // EDIT(begin): optimize for skipCompaction + if opts.skipCompaction { + dst = append(dst, src...) + return dst, nil + } + + escape := opts.escapeHTML + // EDIT(end) + origLen := len(dst) scan := newScanner() defer freeScanner(scan) diff --git a/internal/encoding/json/opt.go b/internal/encoding/json/opt.go new file mode 100644 index 0000000..fd6f8d2 --- /dev/null +++ b/internal/encoding/json/opt.go @@ -0,0 +1,24 @@ +// EDIT(begin): add custom options for JSON encoding +package json + +type Option func(*encOpts) + +// Every time a sub-type of [json.Marshaler] is encountered, +// skip a redundant and costly compaction step, trust it to self-compact. +// +// This is a divergence from the standard library behavior, and is only guaranteed +// safe with SDK types. +func WithSkipCompaction(b bool) Option { + return func(eos *encOpts) { + eos.skipCompaction = true + } +} + +func (eos encOpts) apply(opts ...Option) encOpts { + for _, opt := range opts { + opt(&eos) + } + return eos +} + +// EDIT(end) diff --git a/internal/encoding/json/stream.go b/internal/encoding/json/stream.go index e2d9470..652522c 100644 --- a/internal/encoding/json/stream.go +++ b/internal/encoding/json/stream.go @@ -6,7 +6,6 @@ package json import ( "bytes" - "errors" "io" ) @@ -253,30 +252,34 @@ func (enc *Encoder) SetEscapeHTML(on bool) { enc.escapeHTML = on } -// RawMessage is a raw encoded JSON value. -// It implements [Marshaler] and [Unmarshaler] and can -// be used to delay JSON decoding or precompute a JSON encoding. -type RawMessage []byte - -// MarshalJSON returns m as the JSON encoding of m. -func (m RawMessage) MarshalJSON() ([]byte, error) { - if m == nil { - return []byte("null"), nil - } - return m, nil -} - -// UnmarshalJSON sets *m to a copy of data. -func (m *RawMessage) UnmarshalJSON(data []byte) error { - if m == nil { - return errors.New("json.RawMessage: UnmarshalJSON on nil pointer") - } - *m = append((*m)[0:0], data...) - return nil -} - -var _ Marshaler = (*RawMessage)(nil) -var _ Unmarshaler = (*RawMessage)(nil) +// EDIT(begin): remove RawMessage +// +// // RawMessage is a raw encoded JSON value. +// // It implements [Marshaler] and [Unmarshaler] and can +// // be used to delay JSON decoding or precompute a JSON encoding. +// type RawMessage []byte +// +// // MarshalJSON returns m as the JSON encoding of m. +// func (m RawMessage) MarshalJSON() ([]byte, error) { +// if m == nil { +// return []byte("null"), nil +// } +// return m, nil +// } +// +// // UnmarshalJSON sets *m to a copy of data. +// func (m *RawMessage) UnmarshalJSON(data []byte) error { +// if m == nil { +// return errors.New("json.RawMessage: UnmarshalJSON on nil pointer") +// } +// *m = append((*m)[0:0], data...) +// return nil +// } +// +// var _ Marshaler = (*RawMessage)(nil) +// var _ Unmarshaler = (*RawMessage)(nil) +// +// EDIT(end) // A Token holds a value of one of these types: // diff --git a/internal/encoding/json/time.go b/internal/encoding/json/time.go index 601f941..8e8e3b6 100644 --- a/internal/encoding/json/time.go +++ b/internal/encoding/json/time.go @@ -50,7 +50,7 @@ func timeMarshalEncoder(e *encodeState, v reflect.Value, opts encOpts) bool { if b != nil { e.Grow(len(b)) out := e.AvailableBuffer() - out, _ = appendCompact(out, b, opts.escapeHTML) + out, _ = appendCompact(out, b, opts) e.Buffer.Write(out) return true } diff --git a/packages/param/encoder.go b/packages/param/encoder.go index 7a40451..47b98e4 100644 --- a/packages/param/encoder.go +++ b/packages/param/encoder.go @@ -66,7 +66,7 @@ func MarshalWithExtras[T ParamStruct, R any](f T, underlying any, extras map[str } else if ovr, ok := f.Overrides(); ok { return shimjson.Marshal(ovr) } else { - return shimjson.Marshal(underlying) + return shimjson.Marshal(underlying, shimjson.WithSkipCompaction(true)) } } @@ -96,7 +96,7 @@ func MarshalUnion[T ParamStruct](metadata T, variants ...any) ([]byte, error) { Err: fmt.Errorf("expected union to have only one present variant, got %d", nPresent), } } - return shimjson.Marshal(variants[presentIdx]) + return shimjson.Marshal(variants[presentIdx], shimjson.WithSkipCompaction(true)) } // typeFor is shimmed from Go 1.23 "reflect" package diff --git a/packages/param/encoder_test.go b/packages/param/encoder_test.go index e311890..ef3dcc2 100644 --- a/packages/param/encoder_test.go +++ b/packages/param/encoder_test.go @@ -1,10 +1,13 @@ package param_test import ( + "bytes" "encoding/json" + "reflect" "testing" "time" + shimjson "github.com/stainless-api/stainless-api-go/internal/encoding/json" "github.com/stainless-api/stainless-api-go/packages/param" ) @@ -375,3 +378,176 @@ func TestNullStructUnion(t *testing.T) { t.Fatalf("expected null, received %s", string(b)) } } + +// +// Compaction optimization +// + +type NonCompactedDoubleParent struct { + Prop string `json:"prop"` + Parent NonCompactedParent `json:"parent"` + + param.APIObject +} + +type NonCompactedParent struct { + BadChild NonCompacted `json:"bad_child"` + + param.APIObject +} + +type NonCompacted struct { + Raw string + + param.APIObject +} + +func (a NonCompactedDoubleParent) MarshalJSON() ([]byte, error) { + type shadow NonCompactedDoubleParent + return param.MarshalObject(a, (*shadow)(&a)) +} + +func (a NonCompactedParent) MarshalJSON() ([]byte, error) { + type shadow NonCompactedParent + return param.MarshalObject(a, (*shadow)(&a)) +} + +func (a NonCompacted) MarshalJSON() ([]byte, error) { + if a.Raw == "" { + a.Raw = nonCompactedRaw + } + return []byte(a.Raw), nil +} + +var nonCompactedRaw string = ` { "foo": "bar" } ` + +func TestAppendCompactBroken(t *testing.T) { + tests := map[string]struct { + value json.Marshaler + }{ + "red/illegal-json": { + NonCompacted{Raw: `{ "broken": "json" `}, + }, + "red/nested-with-illegal-json": { + NonCompactedParent{BadChild: NonCompacted{ + Raw: `{ "broken": "json" `, + }}, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + v, err := json.Marshal(test.value) + if err == nil { + t.Fatal("expected error got", v) + } + }) + } +} + +// TestAppendCompact validates an optimization for internal SDK types to +// avoid O(keys^2) iteration over each JSON object. +// +// It's possible to intentionally trigger this behavior as both a user and +// SDK developer. However, the edge case is quite pathological and requires +// calling [json.Marshaler.MarshalJSON] rather than [json.Marshal]. +func TestAppendCompact(t *testing.T) { + + tests := map[string]struct { + value json.Marshaler + expected string + }{ + // + // Non-compacted cases + // + // Note this is how to exploit the compacter to fail, you must call [json.Marshaler.MarshalJSON] rather than [json.Marshal]. + // The type must also embed [param.APIObject] and return non-compacted JSON. + // + + "no-compact/fails-compaction": { + NonCompacted{Raw: nonCompactedRaw}, + nonCompactedRaw, + }, + "no-compact/nested-with-bad-child": { + NonCompactedParent{BadChild: NonCompacted{ + Raw: nonCompactedRaw, + }}, + `{"bad_child":` + nonCompactedRaw + `}`, + }, + "no-compact/double-nested-with-bad-child": { + NonCompactedDoubleParent{Prop: "1", Parent: NonCompactedParent{BadChild: NonCompacted{ + Raw: nonCompactedRaw, + }}}, + `{"prop":"1","parent":{"bad_child":` + nonCompactedRaw + `}}`, + }, + + // + // Compacted cases + // + + "override/spaces-within": { + param.Override[NonCompactedDoubleParent](json.RawMessage(`{"com": "pact"}`)), + `{"com":"pact"}`, + }, + "override/spaces-after": { + param.Override[NonCompactedDoubleParent](json.RawMessage(`{"com":"pact"} `)), + `{"com":"pact"}`, + }, + "override/spaces-before": { + param.Override[NonCompactedDoubleParent](json.RawMessage(` {"com":"pact"}`)), + `{"com":"pact"}`, + }, + "override/spaces-around": { + param.Override[NonCompactedDoubleParent](json.RawMessage(` { "com": "pact" }`)), + `{"com":"pact"}`, + }, + "override/override-with-nested": { + param.Override[NonCompactedDoubleParent](NonCompactedParent{}), + `{"bad_child":{"foo":"bar"}}`, + }, + "override/override-with-non-compacted": { + param.Override[NonCompactedDoubleParent](NonCompacted{}), + `{"foo":"bar"}`, + }, + } + + for name, test := range tests { + t.Run(name+"/marshal-json", func(t *testing.T) { + b, err := test.value.MarshalJSON() + if err != nil { + t.Fatalf("didn't expect error %v, expected %s", err, test.expected) + } + if string(b) != test.expected { + t.Fatalf("expected %s (%s), received %s", test.expected, reflect.TypeOf(test.value), string(b)) + } + }) + + t.Run(name+"/json-marshal", func(t *testing.T) { + b, err := json.Marshal(test.value) + if err != nil { + t.Fatalf("didn't expect error %v, expected %s", err, test.expected) + } + + // expected output of JSON Marshal should always be compacted + var compactedExpected bytes.Buffer + err = json.Compact(&compactedExpected, []byte(test.expected)) + if err != nil { + t.Fatalf("didn't expect error %v, expected %s", err, test.expected) + } + + if string(b) != compactedExpected.String() { + t.Fatalf("expected %s (%s), received %s", test.expected, reflect.TypeOf(test.value), string(b)) + } + }) + + t.Run(name+"/shimjson-marshal", func(t *testing.T) { + b, err := shimjson.Marshal(test.value) + if err != nil { + t.Fatalf("didn't expect error %v, expected %s", err, test.expected) + } + if string(b) != test.expected { + t.Logf("expected %s (%s), received %s", test.expected, reflect.TypeOf(test.value), string(b)) + } + }) + } +} From b9b9fa1d7d191af835ed7983808f331b130ecbee Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 29 May 2026 22:42:57 +0000 Subject: [PATCH 35/37] feat: allow direct upload of spec/config to v0 builds api when merging/rebasing --- .stats.yml | 4 +- build.go | 93 +++++++++++++++++++++++++++++++++++++------ build_test.go | 33 +++++++++++++-- projectbranch.go | 18 +++++++++ projectbranch_test.go | 13 +++++- 5 files changed, 142 insertions(+), 19 deletions(-) diff --git a/.stats.yml b/.stats.yml index 21cca53..f130a9d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 22 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/stainless/stainless-v0-4dbed62ce27b115068721f69e83b644ca0b7922e5be12b6469c0c054a371a170.yml -openapi_spec_hash: f4a68de1cb28c33de769c08f68f543ab +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/stainless/stainless-v0-21e9e0ca130a61ba2ba52d51980a6f4b726708fe8094b45045a8ac0ceb7e544c.yml +openapi_spec_hash: 752810fe6848884333c1bcacf28bc777 config_hash: 63178ec4b1d2ea5636c8619cffcf129b diff --git a/build.go b/build.go index d00ba1b..a0ab370 100644 --- a/build.go +++ b/build.go @@ -898,20 +898,23 @@ func (r *BuildNewParams) UnmarshalJSON(data []byte) error { // // Use [param.IsOmitted] to confirm if a field is set. type BuildNewParamsRevisionUnion struct { - OfString param.Opt[string] `json:",omitzero,inline"` - OfFileInputMap map[string]shared.FileInputUnionParam `json:",omitzero,inline"` + OfBuildNewsRevisionObject *BuildNewParamsRevisionObject `json:",omitzero,inline"` + OfString param.Opt[string] `json:",omitzero,inline"` + OfFileInputMap map[string]shared.FileInputUnionParam `json:",omitzero,inline"` paramUnion } func (u BuildNewParamsRevisionUnion) MarshalJSON() ([]byte, error) { - return param.MarshalUnion(u, u.OfString, u.OfFileInputMap) + return param.MarshalUnion(u, u.OfBuildNewsRevisionObject, u.OfString, u.OfFileInputMap) } func (u *BuildNewParamsRevisionUnion) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, u) } func (u *BuildNewParamsRevisionUnion) asAny() any { - if !param.IsOmitted(u.OfString) { + if !param.IsOmitted(u.OfBuildNewsRevisionObject) { + return u.OfBuildNewsRevisionObject + } else if !param.IsOmitted(u.OfString) { return &u.OfString.Value } else if !param.IsOmitted(u.OfFileInputMap) { return &u.OfFileInputMap @@ -919,6 +922,26 @@ func (u *BuildNewParamsRevisionUnion) asAny() any { return nil } +// A merge command combined with explicit file contents. The files are committed to +// the merge target (`base`) without performing an auto-merge. +// +// The properties Files, Merge are required. +type BuildNewParamsRevisionObject struct { + // File contents to commit directly + Files map[string]shared.FileInputUnionParam `json:"files,omitzero" api:"required"` + // A merge command in the format "base..head" + Merge string `json:"merge" api:"required"` + paramObj +} + +func (r BuildNewParamsRevisionObject) MarshalJSON() (data []byte, err error) { + type shadow BuildNewParamsRevisionObject + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *BuildNewParamsRevisionObject) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + // Optional commit messages to use for each SDK when making a new commit. SDKs not // represented in this object will fallback to the optional `commit_message` // parameter, or will fallback further to the default commit message. @@ -1052,20 +1075,23 @@ func (r *BuildCompareParamsBase) UnmarshalJSON(data []byte) error { // // Use [param.IsOmitted] to confirm if a field is set. type BuildCompareParamsBaseRevisionUnion struct { - OfString param.Opt[string] `json:",omitzero,inline"` - OfFileInputMap map[string]shared.FileInputUnionParam `json:",omitzero,inline"` + OfBuildComparesBaseRevisionObject *BuildCompareParamsBaseRevisionObject `json:",omitzero,inline"` + OfString param.Opt[string] `json:",omitzero,inline"` + OfFileInputMap map[string]shared.FileInputUnionParam `json:",omitzero,inline"` paramUnion } func (u BuildCompareParamsBaseRevisionUnion) MarshalJSON() ([]byte, error) { - return param.MarshalUnion(u, u.OfString, u.OfFileInputMap) + return param.MarshalUnion(u, u.OfBuildComparesBaseRevisionObject, u.OfString, u.OfFileInputMap) } func (u *BuildCompareParamsBaseRevisionUnion) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, u) } func (u *BuildCompareParamsBaseRevisionUnion) asAny() any { - if !param.IsOmitted(u.OfString) { + if !param.IsOmitted(u.OfBuildComparesBaseRevisionObject) { + return u.OfBuildComparesBaseRevisionObject + } else if !param.IsOmitted(u.OfString) { return &u.OfString.Value } else if !param.IsOmitted(u.OfFileInputMap) { return &u.OfFileInputMap @@ -1073,6 +1099,26 @@ func (u *BuildCompareParamsBaseRevisionUnion) asAny() any { return nil } +// A merge command combined with explicit file contents. The files are committed to +// the merge target (`base`) without performing an auto-merge. +// +// The properties Files, Merge are required. +type BuildCompareParamsBaseRevisionObject struct { + // File contents to commit directly + Files map[string]shared.FileInputUnionParam `json:"files,omitzero" api:"required"` + // A merge command in the format "base..head" + Merge string `json:"merge" api:"required"` + paramObj +} + +func (r BuildCompareParamsBaseRevisionObject) MarshalJSON() (data []byte, err error) { + type shadow BuildCompareParamsBaseRevisionObject + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *BuildCompareParamsBaseRevisionObject) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + // Parameters for the head build // // The properties Branch, Revision are required. @@ -1099,23 +1145,46 @@ func (r *BuildCompareParamsHead) UnmarshalJSON(data []byte) error { // // Use [param.IsOmitted] to confirm if a field is set. type BuildCompareParamsHeadRevisionUnion struct { - OfString param.Opt[string] `json:",omitzero,inline"` - OfFileInputMap map[string]shared.FileInputUnionParam `json:",omitzero,inline"` + OfBuildComparesHeadRevisionObject *BuildCompareParamsHeadRevisionObject `json:",omitzero,inline"` + OfString param.Opt[string] `json:",omitzero,inline"` + OfFileInputMap map[string]shared.FileInputUnionParam `json:",omitzero,inline"` paramUnion } func (u BuildCompareParamsHeadRevisionUnion) MarshalJSON() ([]byte, error) { - return param.MarshalUnion(u, u.OfString, u.OfFileInputMap) + return param.MarshalUnion(u, u.OfBuildComparesHeadRevisionObject, u.OfString, u.OfFileInputMap) } func (u *BuildCompareParamsHeadRevisionUnion) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, u) } func (u *BuildCompareParamsHeadRevisionUnion) asAny() any { - if !param.IsOmitted(u.OfString) { + if !param.IsOmitted(u.OfBuildComparesHeadRevisionObject) { + return u.OfBuildComparesHeadRevisionObject + } else if !param.IsOmitted(u.OfString) { return &u.OfString.Value } else if !param.IsOmitted(u.OfFileInputMap) { return &u.OfFileInputMap } return nil } + +// A merge command combined with explicit file contents. The files are committed to +// the merge target (`base`) without performing an auto-merge. +// +// The properties Files, Merge are required. +type BuildCompareParamsHeadRevisionObject struct { + // File contents to commit directly + Files map[string]shared.FileInputUnionParam `json:"files,omitzero" api:"required"` + // A merge command in the format "base..head" + Merge string `json:"merge" api:"required"` + paramObj +} + +func (r BuildCompareParamsHeadRevisionObject) MarshalJSON() (data []byte, err error) { + type shadow BuildCompareParamsHeadRevisionObject + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *BuildCompareParamsHeadRevisionObject) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} diff --git a/build_test.go b/build_test.go index 6b50fa1..ee2976e 100644 --- a/build_test.go +++ b/build_test.go @@ -29,7 +29,16 @@ func TestBuildNewWithOptionalParams(t *testing.T) { _, err := client.Builds.New(context.TODO(), stainless.BuildNewParams{ Project: stainless.String("project"), Revision: stainless.BuildNewParamsRevisionUnion{ - OfString: stainless.String("string"), + OfBuildNewsRevisionObject: &stainless.BuildNewParamsRevisionObject{ + Files: map[string]shared.FileInputUnionParam{ + "foo": { + OfFileInputContent: &shared.FileInputContentParam{ + Content: "content", + }, + }, + }, + Merge: "merge", + }, }, AllowEmpty: stainless.Bool(true), Branch: stainless.String("branch"), @@ -129,14 +138,32 @@ func TestBuildCompareWithOptionalParams(t *testing.T) { Base: stainless.BuildCompareParamsBase{ Branch: "branch", Revision: stainless.BuildCompareParamsBaseRevisionUnion{ - OfString: stainless.String("string"), + OfBuildComparesBaseRevisionObject: &stainless.BuildCompareParamsBaseRevisionObject{ + Files: map[string]shared.FileInputUnionParam{ + "foo": { + OfFileInputContent: &shared.FileInputContentParam{ + Content: "content", + }, + }, + }, + Merge: "merge", + }, }, CommitMessage: stainless.String("commit_message"), }, Head: stainless.BuildCompareParamsHead{ Branch: "branch", Revision: stainless.BuildCompareParamsHeadRevisionUnion{ - OfString: stainless.String("string"), + OfBuildComparesHeadRevisionObject: &stainless.BuildCompareParamsHeadRevisionObject{ + Files: map[string]shared.FileInputUnionParam{ + "foo": { + OfFileInputContent: &shared.FileInputContentParam{ + Content: "content", + }, + }, + }, + Merge: "merge", + }, }, CommitMessage: stainless.String("commit_message"), }, diff --git a/projectbranch.go b/projectbranch.go index 1ea1563..7e1c4f8 100644 --- a/projectbranch.go +++ b/projectbranch.go @@ -17,6 +17,7 @@ import ( "github.com/stainless-api/stainless-api-go/packages/pagination" "github.com/stainless-api/stainless-api-go/packages/param" "github.com/stainless-api/stainless-api-go/packages/respjson" + "github.com/stainless-api/stainless-api-go/shared" ) // ProjectBranchService contains methods and other services that help with @@ -137,6 +138,9 @@ func (r *ProjectBranchService) Delete(ctx context.Context, branch string, body P // // The branch is rebased onto the `base` branch or commit SHA, inheriting any // config and custom code changes. +// +// If `files` is provided, the auto-rebase is skipped: the branch is hard-reset to +// `base` and the provided files are committed on top. func (r *ProjectBranchService) Rebase(ctx context.Context, branch string, params ProjectBranchRebaseParams, opts ...option.RequestOption) (res *ProjectBranch, err error) { opts = slices.Concat(r.Options, opts) precfg, err := requestconfig.PreRequestOptions(opts...) @@ -449,9 +453,23 @@ type ProjectBranchRebaseParams struct { Project param.Opt[string] `path:"project,omitzero" api:"required" json:"-"` // The branch or commit SHA to rebase onto. Defaults to "main". Base param.Opt[string] `query:"base,omitzero" json:"-"` + // Optional commit message to use when `files` is provided. + CommitMessage param.Opt[string] `json:"commit_message,omitzero"` + // File contents to commit directly on top of `base`. When provided, the + // auto-rebase is skipped and the branch is hard-reset to `base` before the files + // are committed. + Files map[string]shared.FileInputUnionParam `json:"files,omitzero"` paramObj } +func (r ProjectBranchRebaseParams) MarshalJSON() (data []byte, err error) { + type shadow ProjectBranchRebaseParams + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *ProjectBranchRebaseParams) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + // URLQuery serializes [ProjectBranchRebaseParams]'s query parameters as // `url.Values`. func (r ProjectBranchRebaseParams) URLQuery() (v url.Values, err error) { diff --git a/projectbranch_test.go b/projectbranch_test.go index 0713965..2fe6ad7 100644 --- a/projectbranch_test.go +++ b/projectbranch_test.go @@ -11,6 +11,7 @@ import ( "github.com/stainless-api/stainless-api-go" "github.com/stainless-api/stainless-api-go/internal/testutil" "github.com/stainless-api/stainless-api-go/option" + "github.com/stainless-api/stainless-api-go/shared" ) func TestProjectBranchNewWithOptionalParams(t *testing.T) { @@ -138,8 +139,16 @@ func TestProjectBranchRebaseWithOptionalParams(t *testing.T) { context.TODO(), "branch", stainless.ProjectBranchRebaseParams{ - Project: stainless.String("project"), - Base: stainless.String("base"), + Project: stainless.String("project"), + Base: stainless.String("base"), + CommitMessage: stainless.String("commit_message"), + Files: map[string]shared.FileInputUnionParam{ + "foo": { + OfFileInputContent: &shared.FileInputContentParam{ + Content: "content", + }, + }, + }, }, ) if err != nil { From fae59c06fd45fdc298fa1c243878e6b79a8ca315 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 29 May 2026 23:49:50 +0000 Subject: [PATCH 36/37] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index f130a9d..9003a69 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 22 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/stainless/stainless-v0-21e9e0ca130a61ba2ba52d51980a6f4b726708fe8094b45045a8ac0ceb7e544c.yml -openapi_spec_hash: 752810fe6848884333c1bcacf28bc777 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/stainless/stainless-v0-4ec3b12ce6b2e1e2eb800fab7dea5fc272c8c7aaf364a5305af5f4eda95e07b3.yml +openapi_spec_hash: 8e661d2611da4f7d874cafe22cda765d config_hash: 63178ec4b1d2ea5636c8619cffcf129b From ae6aee2b51e9964d01706b94dddaa1d1e2ffbf00 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 29 May 2026 16:50:18 -0700 Subject: [PATCH 37/37] release: 0.31.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 49 +++++++++++++++++++++++++++++++++++ README.md | 2 +- internal/version.go | 2 +- 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 554e34b..f81bf99 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.30.0" + ".": "0.31.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a00339..0a49527 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,54 @@ # Changelog +## 0.31.0 (2026-05-29) + +Full Changelog: [v0.30.0...v0.31.0](https://github.com/stainless-api/stainless-api-go/compare/v0.30.0...v0.31.0) + +### Features + +* allow direct upload of spec/config to v0 builds api when merging/rebasing ([b9b9fa1](https://github.com/stainless-api/stainless-api-go/commit/b9b9fa1d7d191af835ed7983808f331b130ecbee)) +* **go:** add default http client with timeout ([b0fcaa2](https://github.com/stainless-api/stainless-api-go/commit/b0fcaa21da3ce9784ab5df50f2ad53c1624eee63)) +* **internal:** support comma format in multipart form encoding ([fe14b5a](https://github.com/stainless-api/stainless-api-go/commit/fe14b5a12cc6921f134e3cd58f1ddbadbef687d8)) +* support setting headers via env ([49cf293](https://github.com/stainless-api/stainless-api-go/commit/49cf293f1cba3628ff6690f853178c01e2560c85)) + + +### Bug Fixes + +* fix for union type names ([199c263](https://github.com/stainless-api/stainless-api-go/commit/199c263e6b0ad82369d0a957ea3b06856f9cd8b3)) +* **go:** avoid panic when http.DefaultTransport is wrapped ([7eaefa2](https://github.com/stainless-api/stainless-api-go/commit/7eaefa2b69e4f6a98a5baa1dd6a78ce334a3394a)) +* prevent duplicate ? in query params ([75d6c76](https://github.com/stainless-api/stainless-api-go/commit/75d6c762a9ff3a61bd85ca3c6849beba49071487)) + + +### Chores + +* avoid embedding reflect.Type for dead code elimination ([ed95de5](https://github.com/stainless-api/stainless-api-go/commit/ed95de5b4950f55c49a33c9f7aa0762e5b9a7eca)) +* **ci:** skip lint on metadata-only changes ([9ff15a2](https://github.com/stainless-api/stainless-api-go/commit/9ff15a28b3fa87a039afbc0530fd1596a2c3628b)) +* **ci:** support opting out of skipping builds on metadata-only commits ([fc8ba99](https://github.com/stainless-api/stainless-api-go/commit/fc8ba99ac8e0b5911fb116128c95f3b834b6e0eb)) +* **client:** fix multipart serialisation of Default() fields ([3440645](https://github.com/stainless-api/stainless-api-go/commit/3440645718d76c103cc8bbc0581f6b6f0701a5da)) +* configure new SDK language ([e3cdd77](https://github.com/stainless-api/stainless-api-go/commit/e3cdd774fec0ca4730b49a29b9449e6486689538)) +* **internal:** codegen related update ([0db8f71](https://github.com/stainless-api/stainless-api-go/commit/0db8f716cdd728dd59b8bac086c65aa050e4d0b8)) +* **internal:** codegen related update ([0060599](https://github.com/stainless-api/stainless-api-go/commit/0060599fcb6cc027331f9b2cd5308c203d22b220)) +* **internal:** more robust bootstrap script ([7a3c829](https://github.com/stainless-api/stainless-api-go/commit/7a3c829bc1b041174a381b0123cb75a9eee24125)) +* **internal:** support default value struct tag ([51c93c2](https://github.com/stainless-api/stainless-api-go/commit/51c93c2ae106ad64285dea87f94f6240f7f56e8a)) +* **internal:** tweak CI branches ([be313f6](https://github.com/stainless-api/stainless-api-go/commit/be313f62c0c1ac7bf5351180152ba28b97bead3a)) +* **internal:** update gitignore ([7b22047](https://github.com/stainless-api/stainless-api-go/commit/7b2204784376dd5ce909c461dbc7a56e35671415)) +* redact api-key headers in debug logs ([27ed10e](https://github.com/stainless-api/stainless-api-go/commit/27ed10ebca2c7b1c7ac6d97997d0f829d3b77bad)) +* remove unnecessary error check for url parsing ([459c159](https://github.com/stainless-api/stainless-api-go/commit/459c159f2481411fb1295c14570d02a44eeacc66)) +* **tests:** bump steady to v0.19.4 ([d07d7c1](https://github.com/stainless-api/stainless-api-go/commit/d07d7c193749d6405dbb22b4e8aea5993efd07d0)) +* **tests:** bump steady to v0.19.5 ([1abf6bb](https://github.com/stainless-api/stainless-api-go/commit/1abf6bb3dd5a72b18337c2d7ad557c9dbc5b6240)) +* **tests:** bump steady to v0.19.6 ([c3b044e](https://github.com/stainless-api/stainless-api-go/commit/c3b044efa0112dee1aaddb23bae87622eff99ec8)) +* **tests:** bump steady to v0.19.7 ([9fbed10](https://github.com/stainless-api/stainless-api-go/commit/9fbed10600a9bf9655c8289b6f0eb18de8a982b6)) +* **tests:** bump steady to v0.20.1 ([34fa796](https://github.com/stainless-api/stainless-api-go/commit/34fa7961079423a9653316c1d2e68023fdf2b76e)) +* **tests:** bump steady to v0.20.2 ([5b8d8a7](https://github.com/stainless-api/stainless-api-go/commit/5b8d8a7f2074c8cbba1e057cba98c78553a16b48)) +* **tests:** bump steady to v0.22.1 ([a5e7563](https://github.com/stainless-api/stainless-api-go/commit/a5e75636ac7bf44dfb46792921e22e0f161f96f0)) +* update docs for api:"required" ([dff036b](https://github.com/stainless-api/stainless-api-go/commit/dff036b0396ec75667e1880662a6d7d43cade57d)) +* update probot ([940e928](https://github.com/stainless-api/stainless-api-go/commit/940e9283bcbf16347fd8cb8f9c6d83cfb08fb227)) + + +### Refactors + +* **tests:** switch from prism to steady ([ac75b85](https://github.com/stainless-api/stainless-api-go/commit/ac75b85a15fdf1969bc92c55698cfb4793316b84)) + ## 0.30.0 (2026-03-11) Full Changelog: [v0.29.0...v0.30.0](https://github.com/stainless-api/stainless-api-go/compare/v0.29.0...v0.30.0) diff --git a/README.md b/README.md index 94b5ea4..a0905e2 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Or to pin the version: ```sh -go get -u 'github.com/stainless-api/stainless-api-go@v0.30.0' +go get -u 'github.com/stainless-api/stainless-api-go@v0.31.0' ``` diff --git a/internal/version.go b/internal/version.go index 7d5879c..d26ea0a 100644 --- a/internal/version.go +++ b/internal/version.go @@ -2,4 +2,4 @@ package internal -const PackageVersion = "0.30.0" // x-release-please-version +const PackageVersion = "0.31.0" // x-release-please-version