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/38] codegen metadata
---
.stats.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.stats.yml b/.stats.yml
index 80762ab8..d6227e89 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/38] 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 d6227e89..34d47222 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 2b480ee2..40339317 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.
-[](https://cursor.com/en-US/install-mcp?name=%40stainless-api%2Fmcp&config=eyJuYW1lIjoiQHN0YWlubGVzcy1hcGkvbWNwIiwidHJhbnNwb3J0IjoiaHR0cCIsInVybCI6Imh0dHBzOi8vc3RhaW5sZXNzLXYwLnN0bG1jcC5jb20iLCJoZWFkZXJzIjp7Ingtc3RhaW5sZXNzLWFwaS1rZXkiOiJNeSBBUEkgS2V5In19)
-[](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)
+[](https://cursor.com/en-US/install-mcp?name=%40stainless-api%2Fsdk-mcp&config=eyJuYW1lIjoiQHN0YWlubGVzcy1hcGkvc2RrLW1jcCIsInRyYW5zcG9ydCI6Imh0dHAiLCJ1cmwiOiJodHRwczovL3N0YWlubGVzcy12MC5zdGxtY3AuY29tIiwiaGVhZGVycyI6eyJ4LXN0YWlubGVzcy1hcGkta2V5IjoiTXkgQVBJIEtleSJ9fQ)
+[](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/38] codegen metadata
---
.stats.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.stats.yml b/.stats.yml
index 34d47222..0b3b470e 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/38] 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 2803ecdc..72dc7f7c 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/38] 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 07e20026..0d887231 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 bcf3b392..38201de8 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 c26b1222..868e2a94 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/38] 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 38201de8..e1c19e88 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 868e2a94..06ee1a04 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/38] 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 e1c19e88..ab814d38 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 06ee1a04..48d99a6c 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/38] chore(internal): update gitignore
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index c6d05015..8554affe 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/38] 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 ab814d38..b319bdfb 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 48d99a6c..2e421bc1 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/38] 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 72dc7f7c..19a0cb12 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/38] 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 b319bdfb..09eb49f6 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 2e421bc1..c875306a 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/38] 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 33e60ae5..a74a77d6 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 c8d2bec4..82da63f5 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 a735ca54..ea65f1c1 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 d9915d4a..f0c9d14c 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 0decb733..f7eccb97 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 19b36146..2853bf94 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 17b21302..efcaf8ca 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/38] 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 40916adb..98c2f838 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 a2290b09..3f1e942a 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/38] 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 962432c6..a8b4e32c 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/38] 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 a8b4e32c..b2f9f0f6 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/38] 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 98c2f838..2ebe896c 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 09eb49f6..290e21b9 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 c875306a..952b7439 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/38] 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 19a0cb12..aa38ccad 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/38] 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 40339317..94b5ea45 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/38] 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 290e21b9..15c29941 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 952b7439..31bb2e8c 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/38] 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 15c29941..5cd7c157 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 31bb2e8c..c3a0d2cc 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/38] 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 a74a77d6..87c97c43 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/38] 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 0b3b470e..a706af67 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 87c97c43..d00ba1b9 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 7aa9ba13..416006b4 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/38] codegen metadata
---
.stats.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.stats.yml b/.stats.yml
index a706af67..b25db43f 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/38] 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 5cd7c157..feebe5ed 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 c3a0d2cc..14cb1afc 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/38] 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 5ab30665..46547f18 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/38] 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 98ecebda..2b2c1cf0 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 00000000..c0b9fe43
--- /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/38] feat: support setting headers via env
---
client.go | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/client.go b/client.go
index 2b2c1cf0..766c7b23 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/38] codegen metadata
---
.stats.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.stats.yml b/.stats.yml
index b25db43f..3cfe0e7b 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/38] codegen metadata
---
.stats.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.stats.yml b/.stats.yml
index 3cfe0e7b..21cca53a 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/38] 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 2ebe896c..f8bbc166 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 a96464a2..465a3ccf 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 f7eccb97..ed0b4f32 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 a843d580..ebd476cc 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/38] 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 8ec9dd60..4be09875 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/38] 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 c0b9fe43..436bf5a6 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/38] 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 aa38ccad..e2a5ee32 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/38] 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 b9a9d4bb..2da9f4ea 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 01bfdf65..c9d6ca5b 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 00000000..fd6f8d2f
--- /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 e2d9470b..652522c3 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 601f941d..8e8e3b6a 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 7a404515..47b98e4a 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 e311890a..ef3dcc23 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/38] 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 21cca53a..f130a9d8 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 d00ba1b9..a0ab3701 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 6b50fa10..ee2976e2 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 1ea1563a..7e1c4f81 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 07139654..2fe6ad77 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/38] codegen metadata
---
.stats.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index f130a9d8..9003a691 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 8c6c9fce6fa563aee07b6c8352830c777a745edc Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 2 Jul 2026 15:57:39 +0000
Subject: [PATCH 37/38] feat: add option to get raw openapi spec content,
bypassing gcs
---
.stats.yml | 4 +-
buildtargetoutput.go | 108 +++++++++++++++++++++++++++--------
shared/constant/constants.go | 3 +
3 files changed, 90 insertions(+), 25 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 9003a691..6ff1e844 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-4ec3b12ce6b2e1e2eb800fab7dea5fc272c8c7aaf364a5305af5f4eda95e07b3.yml
-openapi_spec_hash: 8e661d2611da4f7d874cafe22cda765d
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/stainless/stainless-v0-04d48dee1177701e600037688edbef35aaf430557c83831a72ae7fce67f5459c.yml
+openapi_spec_hash: 4c8fdfec19cee6744bf22b7ccd3e0cb0
config_hash: 63178ec4b1d2ea5636c8619cffcf129b
diff --git a/buildtargetoutput.go b/buildtargetoutput.go
index ea65f1c1..13feafc7 100644
--- a/buildtargetoutput.go
+++ b/buildtargetoutput.go
@@ -46,8 +46,12 @@ func NewBuildTargetOutputService(opts ...option.RequestOption) (r BuildTargetOut
// necessary) is returned.
//
// Otherwise, the possible types of outputs are specific to the requested target,
-// and the output method _must_ be `url`. See the documentation for `type` for more
-// information.
+// and the output method _must_ be `url` or `content`. See the documentation for
+// `type` for more information.
+//
+// The `content` output method returns the raw content of the output inline,
+// instead of a download URL, and is only supported for the `openapi-*` and `file`
+// types.
func (r *BuildTargetOutputService) Get(ctx context.Context, query BuildTargetOutputGetParams, opts ...option.RequestOption) (res *BuildTargetOutputGetResponseUnion, err error) {
opts = slices.Concat(r.Options, opts)
path := "v0/build_target_outputs"
@@ -56,34 +60,37 @@ func (r *BuildTargetOutputService) Get(ctx context.Context, query BuildTargetOut
}
// BuildTargetOutputGetResponseUnion contains all possible properties and values
-// from [BuildTargetOutputGetResponseURL], [BuildTargetOutputGetResponseGit].
+// from [BuildTargetOutputGetResponseURL], [BuildTargetOutputGetResponseGit],
+// [BuildTargetOutputGetResponseContent].
//
// Use the [BuildTargetOutputGetResponseUnion.AsAny] method to switch on the
// variant.
//
// Use the methods beginning with 'As' to cast the union to one of its variants.
type BuildTargetOutputGetResponseUnion struct {
- // Any of "url", "git".
+ // Any of "url", "git", "content".
Output string `json:"output"`
// This field is from variant [BuildTargetOutputGetResponseURL].
Target shared.Target `json:"target"`
Type string `json:"type"`
URL string `json:"url"`
- // This field is from variant [BuildTargetOutputGetResponseURL].
- Path string `json:"path"`
+ Path string `json:"path"`
// This field is from variant [BuildTargetOutputGetResponseGit].
Token string `json:"token"`
// This field is from variant [BuildTargetOutputGetResponseGit].
- Ref string `json:"ref"`
- JSON struct {
- Output respjson.Field
- Target respjson.Field
- Type respjson.Field
- URL respjson.Field
- Path respjson.Field
- Token respjson.Field
- Ref respjson.Field
- raw string
+ Ref string `json:"ref"`
+ // This field is from variant [BuildTargetOutputGetResponseContent].
+ Content string `json:"content"`
+ JSON struct {
+ Output respjson.Field
+ Target respjson.Field
+ Type respjson.Field
+ URL respjson.Field
+ Path respjson.Field
+ Token respjson.Field
+ Ref respjson.Field
+ Content respjson.Field
+ raw string
} `json:"-"`
}
@@ -94,14 +101,16 @@ type anyBuildTargetOutputGetResponse interface {
implBuildTargetOutputGetResponseUnion()
}
-func (BuildTargetOutputGetResponseURL) implBuildTargetOutputGetResponseUnion() {}
-func (BuildTargetOutputGetResponseGit) implBuildTargetOutputGetResponseUnion() {}
+func (BuildTargetOutputGetResponseURL) implBuildTargetOutputGetResponseUnion() {}
+func (BuildTargetOutputGetResponseGit) implBuildTargetOutputGetResponseUnion() {}
+func (BuildTargetOutputGetResponseContent) implBuildTargetOutputGetResponseUnion() {}
// Use the following switch statement to find the correct variant
//
// switch variant := BuildTargetOutputGetResponseUnion.AsAny().(type) {
// case stainless.BuildTargetOutputGetResponseURL:
// case stainless.BuildTargetOutputGetResponseGit:
+// case stainless.BuildTargetOutputGetResponseContent:
// default:
// fmt.Errorf("no variant present")
// }
@@ -111,6 +120,8 @@ func (u BuildTargetOutputGetResponseUnion) AsAny() anyBuildTargetOutputGetRespon
return u.AsURL()
case "git":
return u.AsGit()
+ case "content":
+ return u.AsContent()
}
return nil
}
@@ -125,6 +136,11 @@ func (u BuildTargetOutputGetResponseUnion) AsGit() (v BuildTargetOutputGetRespon
return
}
+func (u BuildTargetOutputGetResponseUnion) AsContent() (v BuildTargetOutputGetResponseContent) {
+ apijson.UnmarshalRoot(json.RawMessage(u.JSON.raw), &v)
+ return
+}
+
// Returns the unmodified JSON received from the API
func (u BuildTargetOutputGetResponseUnion) RawJSON() string { return u.JSON.raw }
@@ -220,6 +236,49 @@ const (
BuildTargetOutputGetResponseGitTypeFile BuildTargetOutputGetResponseGitType = "file"
)
+type BuildTargetOutputGetResponseContent struct {
+ // The raw content of the output
+ Content string `json:"content" api:"required"`
+ Output constant.Content `json:"output" default:"content"`
+ // Any of "node", "typescript", "python", "go", "java", "kotlin", "ruby",
+ // "terraform", "cli", "php", "csharp", "sql", "openapi".
+ Target shared.Target `json:"target" api:"required"`
+ // Any of "source", "dist", "wheel", "openapi-with-transforms",
+ // "openapi-with-code-samples", "openapi-sdk-spec", "file".
+ Type BuildTargetOutputGetResponseContentType `json:"type" api:"required"`
+ // The path of the file, which is only present when using with the type "file"
+ // option.
+ Path string `json:"path"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Content respjson.Field
+ Output respjson.Field
+ Target respjson.Field
+ Type respjson.Field
+ Path respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r BuildTargetOutputGetResponseContent) RawJSON() string { return r.JSON.raw }
+func (r *BuildTargetOutputGetResponseContent) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+type BuildTargetOutputGetResponseContentType string
+
+const (
+ BuildTargetOutputGetResponseContentTypeSource BuildTargetOutputGetResponseContentType = "source"
+ BuildTargetOutputGetResponseContentTypeDist BuildTargetOutputGetResponseContentType = "dist"
+ BuildTargetOutputGetResponseContentTypeWheel BuildTargetOutputGetResponseContentType = "wheel"
+ BuildTargetOutputGetResponseContentTypeOpenAPIWithTransforms BuildTargetOutputGetResponseContentType = "openapi-with-transforms"
+ BuildTargetOutputGetResponseContentTypeOpenAPIWithCodeSamples BuildTargetOutputGetResponseContentType = "openapi-with-code-samples"
+ BuildTargetOutputGetResponseContentTypeOpenAPISDKSpec BuildTargetOutputGetResponseContentType = "openapi-sdk-spec"
+ BuildTargetOutputGetResponseContentTypeFile BuildTargetOutputGetResponseContentType = "file"
+)
+
type BuildTargetOutputGetResponseType string
const (
@@ -245,9 +304,10 @@ type BuildTargetOutputGetParams struct {
Type BuildTargetOutputGetParamsType `query:"type,omitzero" api:"required" json:"-"`
// The path of the file to get when used with "type": "file".
Path param.Opt[string] `query:"path,omitzero" json:"-"`
- // Output format: url (download URL) or git (temporary access token).
+ // Output format: url (download URL), git (temporary access token), or content (raw
+ // content returned inline, only supported for the "openapi-\*" and "file" types).
//
- // Any of "url", "git".
+ // Any of "url", "git", "content".
Output BuildTargetOutputGetParamsOutput `query:"output,omitzero" json:"-"`
paramObj
}
@@ -292,10 +352,12 @@ const (
BuildTargetOutputGetParamsTypeFile BuildTargetOutputGetParamsType = "file"
)
-// Output format: url (download URL) or git (temporary access token).
+// Output format: url (download URL), git (temporary access token), or content (raw
+// content returned inline, only supported for the "openapi-\*" and "file" types).
type BuildTargetOutputGetParamsOutput string
const (
- BuildTargetOutputGetParamsOutputURL BuildTargetOutputGetParamsOutput = "url"
- BuildTargetOutputGetParamsOutputGit BuildTargetOutputGetParamsOutput = "git"
+ BuildTargetOutputGetParamsOutputURL BuildTargetOutputGetParamsOutput = "url"
+ BuildTargetOutputGetParamsOutputGit BuildTargetOutputGetParamsOutput = "git"
+ BuildTargetOutputGetParamsOutputContent BuildTargetOutputGetParamsOutput = "content"
)
diff --git a/shared/constant/constants.go b/shared/constant/constants.go
index 416006b4..36984aa2 100644
--- a/shared/constant/constants.go
+++ b/shared/constant/constants.go
@@ -19,6 +19,7 @@ func ValueOf[T Constant[T]]() T {
}
type Completed string // Always "completed"
+type Content string // Always "content"
type Git string // Always "git"
type InProgress string // Always "in_progress"
type Markdown string // Always "markdown"
@@ -29,6 +30,7 @@ type URL string // Always "url"
type Waiting string // Always "waiting"
func (c Completed) Default() Completed { return "completed" }
+func (c Content) Default() Content { return "content" }
func (c Git) Default() Git { return "git" }
func (c InProgress) Default() InProgress { return "in_progress" }
func (c Markdown) Default() Markdown { return "markdown" }
@@ -39,6 +41,7 @@ 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 Content) MarshalJSON() ([]byte, error) { return marshalString(c) }
func (c Git) MarshalJSON() ([]byte, error) { return marshalString(c) }
func (c InProgress) MarshalJSON() ([]byte, error) { return marshalString(c) }
func (c Markdown) MarshalJSON() ([]byte, error) { return marshalString(c) }
From 47a4fc528d21539177d196301324dc1d81de6e2e Mon Sep 17 00:00:00 2001
From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com>
Date: Thu, 2 Jul 2026 08:58:12 -0700
Subject: [PATCH 38/38] release: 0.31.0
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 50 +++++++++++++++++++++++++++++++++++
README.md | 2 +-
internal/version.go | 2 +-
4 files changed, 53 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 554e34bb..f81bf992 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 4a003391..d50977b3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,55 @@
# Changelog
+## 0.31.0 (2026-07-02)
+
+Full Changelog: [v0.30.0...v0.31.0](https://github.com/stainless-api/stainless-api-go/compare/v0.30.0...v0.31.0)
+
+### Features
+
+* add option to get raw openapi spec content, bypassing gcs ([8c6c9fc](https://github.com/stainless-api/stainless-api-go/commit/8c6c9fce6fa563aee07b6c8352830c777a745edc))
+* 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 94b5ea45..a0905e2b 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 7d5879c5..d26ea0ab 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