From 142ce79822363e4177eb82fd912494f5561bf1c6 Mon Sep 17 00:00:00 2001 From: David Konigsberg <72822263+davidkonigsberg@users.noreply.github.com> Date: Tue, 7 Apr 2026 08:21:23 -0400 Subject: [PATCH 01/21] chore(ci): remove dangling typescript-express-seed-update reference from update-seed workflow (#14694) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .github/workflows/update-seed.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/update-seed.yml b/.github/workflows/update-seed.yml index 85a26d010bbb..4feb6bc5737f 100644 --- a/.github/workflows/update-seed.yml +++ b/.github/workflows/update-seed.yml @@ -895,7 +895,6 @@ jobs: java-sdk-seed-update, java-model-seed-update, typescript-sdk-seed-update, - typescript-express-seed-update, go-model-seed-update, go-sdk-seed-update, csharp-model-seed-update, @@ -979,7 +978,6 @@ jobs: java-sdk-seed-update, java-model-seed-update, typescript-sdk-seed-update, - typescript-express-seed-update, go-model-seed-update, go-sdk-seed-update, csharp-model-seed-update, From 4e918bab1dd67444628cd3a1733f515196f9f2a3 Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 7 Apr 2026 08:53:01 -0400 Subject: [PATCH 02/21] chore(php): update php-sdk seed (#14695) Co-authored-by: davidkonigsberg <72822263+davidkonigsberg@users.noreply.github.com> --- seed/php-sdk/pagination-custom/README.md | 15 +++-- seed/php-sdk/pagination-custom/reference.md | 18 +++-- .../pagination-custom/src/Types/Link.php | 50 ++++++++++++++ .../src/Types/UsernameCursor.php | 34 ---------- .../src/Types/UsernamePage.php | 43 ------------ .../src/Types/UsersListResponse.php | 67 +++++++++++++++++++ ...tom.php => ListWithCustomPagerRequest.php} | 14 ++-- .../src/Users/UsersClient.php | 21 +++--- .../src/dynamic-snippets/example0/snippet.php | 7 +- 9 files changed, 162 insertions(+), 107 deletions(-) create mode 100644 seed/php-sdk/pagination-custom/src/Types/Link.php delete mode 100644 seed/php-sdk/pagination-custom/src/Types/UsernameCursor.php delete mode 100644 seed/php-sdk/pagination-custom/src/Types/UsernamePage.php create mode 100644 seed/php-sdk/pagination-custom/src/Types/UsersListResponse.php rename seed/php-sdk/pagination-custom/src/Users/Requests/{ListUsernamesRequestCustom.php => ListWithCustomPagerRequest.php} (54%) diff --git a/seed/php-sdk/pagination-custom/README.md b/seed/php-sdk/pagination-custom/README.md index 9fc7965d23e0..4d83f66a42a0 100644 --- a/seed/php-sdk/pagination-custom/README.md +++ b/seed/php-sdk/pagination-custom/README.md @@ -38,13 +38,14 @@ Instantiate and use the client with the following: namespace Example; use Seed\SeedClient; -use Seed\Users\Requests\ListUsernamesRequestCustom; +use Seed\Users\Requests\ListWithCustomPagerRequest; $client = new SeedClient( token: '', ); -$client->users->listUsernamesCustom( - new ListUsernamesRequestCustom([ +$client->users->listWithCustomPager( + new ListWithCustomPagerRequest([ + 'limit' => 1, 'startingAfter' => 'starting_after', ]), ); @@ -60,7 +61,7 @@ use Seed\Exceptions\SeedApiException; use Seed\Exceptions\SeedException; try { - $response = $client->users->listUsernamesCustom(...); + $response = $client->users->listWithCustomPager(...); } catch (SeedApiException $e) { echo 'API Exception occurred: ' . $e->getMessage() . "\n"; echo 'Status Code: ' . $e->getCode() . "\n"; @@ -81,7 +82,7 @@ $client = new SeedClient( ['baseUrl' => 'https://api.example.com'], ); -$items = $client->users->listUsernamesCustom(['limit' => 10]); +$items = $client->users->listWithCustomPager(['limit' => 10]); foreach ($items as $item) { var_dump($item); @@ -143,7 +144,7 @@ A request is deemed retryable when any of the following HTTP status codes is ret Use the `maxRetries` request option to configure this behavior. ```php -$response = $client->users->listUsernamesCustom( +$response = $client->users->listWithCustomPager( ..., options: [ 'maxRetries' => 0 // Override maxRetries at the request level @@ -156,7 +157,7 @@ $response = $client->users->listUsernamesCustom( The SDK defaults to a 30 second timeout. Use the `timeout` option to configure this behavior. ```php -$response = $client->users->listUsernamesCustom( +$response = $client->users->listWithCustomPager( ..., options: [ 'timeout' => 3.0 // Override timeout at the request level diff --git a/seed/php-sdk/pagination-custom/reference.md b/seed/php-sdk/pagination-custom/reference.md index 152b86b04ff1..524d193ebaa4 100644 --- a/seed/php-sdk/pagination-custom/reference.md +++ b/seed/php-sdk/pagination-custom/reference.md @@ -1,6 +1,6 @@ # Reference ## Users -
$client->users->listUsernamesCustom($request) -> ?UsernameCursor +
$client->users->listWithCustomPager($request) -> ?UsersListResponse
@@ -13,8 +13,9 @@
```php -$client->users->listUsernamesCustom( - new ListUsernamesRequestCustom([ +$client->users->listWithCustomPager( + new ListWithCustomPagerRequest([ + 'limit' => 1, 'startingAfter' => 'starting_after', ]), ); @@ -32,10 +33,15 @@ $client->users->listUsernamesCustom(
-**$startingAfter:** `?string` +**$limit:** `?int` — The maximum number of results to return. + +
+
+ +
+
-The cursor used for pagination in order to fetch -the next page of results. +**$startingAfter:** `?string` — The cursor used for pagination.
diff --git a/seed/php-sdk/pagination-custom/src/Types/Link.php b/seed/php-sdk/pagination-custom/src/Types/Link.php new file mode 100644 index 000000000000..09ebe3d93fc9 --- /dev/null +++ b/seed/php-sdk/pagination-custom/src/Types/Link.php @@ -0,0 +1,50 @@ +rel = $values['rel']; + $this->method = $values['method']; + $this->href = $values['href']; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->toJson(); + } +} diff --git a/seed/php-sdk/pagination-custom/src/Types/UsernameCursor.php b/seed/php-sdk/pagination-custom/src/Types/UsernameCursor.php deleted file mode 100644 index e64d44fa92c9..000000000000 --- a/seed/php-sdk/pagination-custom/src/Types/UsernameCursor.php +++ /dev/null @@ -1,34 +0,0 @@ -cursor = $values['cursor']; - } - - /** - * @return string - */ - public function __toString(): string - { - return $this->toJson(); - } -} diff --git a/seed/php-sdk/pagination-custom/src/Types/UsernamePage.php b/seed/php-sdk/pagination-custom/src/Types/UsernamePage.php deleted file mode 100644 index c21651929c07..000000000000 --- a/seed/php-sdk/pagination-custom/src/Types/UsernamePage.php +++ /dev/null @@ -1,43 +0,0 @@ - $data - */ - #[JsonProperty('data'), ArrayType(['string'])] - public array $data; - - /** - * @param array{ - * data: array, - * after?: ?string, - * } $values - */ - public function __construct( - array $values, - ) { - $this->after = $values['after'] ?? null; - $this->data = $values['data']; - } - - /** - * @return string - */ - public function __toString(): string - { - return $this->toJson(); - } -} diff --git a/seed/php-sdk/pagination-custom/src/Types/UsersListResponse.php b/seed/php-sdk/pagination-custom/src/Types/UsersListResponse.php new file mode 100644 index 000000000000..2de7cea07f8d --- /dev/null +++ b/seed/php-sdk/pagination-custom/src/Types/UsersListResponse.php @@ -0,0 +1,67 @@ + $links + */ + #[JsonProperty('links'), ArrayType([Link::class])] + public array $links; + + /** + * @var array $data + */ + #[JsonProperty('data'), ArrayType(['string'])] + public array $data; + + /** + * @param array{ + * links: array, + * data: array, + * limit?: ?int, + * count?: ?int, + * hasMore?: ?bool, + * } $values + */ + public function __construct( + array $values, + ) { + $this->limit = $values['limit'] ?? null; + $this->count = $values['count'] ?? null; + $this->hasMore = $values['hasMore'] ?? null; + $this->links = $values['links']; + $this->data = $values['data']; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->toJson(); + } +} diff --git a/seed/php-sdk/pagination-custom/src/Users/Requests/ListUsernamesRequestCustom.php b/seed/php-sdk/pagination-custom/src/Users/Requests/ListWithCustomPagerRequest.php similarity index 54% rename from seed/php-sdk/pagination-custom/src/Users/Requests/ListUsernamesRequestCustom.php rename to seed/php-sdk/pagination-custom/src/Users/Requests/ListWithCustomPagerRequest.php index eb47ea68e808..eed31a86601f 100644 --- a/seed/php-sdk/pagination-custom/src/Users/Requests/ListUsernamesRequestCustom.php +++ b/seed/php-sdk/pagination-custom/src/Users/Requests/ListWithCustomPagerRequest.php @@ -4,24 +4,28 @@ use Seed\Core\Json\JsonSerializableType; -class ListUsernamesRequestCustom extends JsonSerializableType +class ListWithCustomPagerRequest extends JsonSerializableType { /** - * The cursor used for pagination in order to fetch - * the next page of results. - * - * @var ?string $startingAfter + * @var ?int $limit The maximum number of results to return. + */ + public ?int $limit; + + /** + * @var ?string $startingAfter The cursor used for pagination. */ public ?string $startingAfter; /** * @param array{ + * limit?: ?int, * startingAfter?: ?string, * } $values */ public function __construct( array $values = [], ) { + $this->limit = $values['limit'] ?? null; $this->startingAfter = $values['startingAfter'] ?? null; } } diff --git a/seed/php-sdk/pagination-custom/src/Users/UsersClient.php b/seed/php-sdk/pagination-custom/src/Users/UsersClient.php index ebc9301671da..fd15278a11b0 100644 --- a/seed/php-sdk/pagination-custom/src/Users/UsersClient.php +++ b/seed/php-sdk/pagination-custom/src/Users/UsersClient.php @@ -4,10 +4,10 @@ use Psr\Http\Client\ClientInterface; use Seed\Core\Client\RawClient; -use Seed\Users\Requests\ListUsernamesRequestCustom; +use Seed\Users\Requests\ListWithCustomPagerRequest; use Seed\Core\Pagination\Pager; use Seed\Core\Pagination\CustomPager; -use Seed\Types\UsernameCursor; +use Seed\Types\UsersListResponse; use Seed\Exceptions\SeedException; use Seed\Exceptions\SeedApiException; use Seed\Core\Json\JsonApiRequest; @@ -52,7 +52,7 @@ public function __construct( } /** - * @param ListUsernamesRequestCustom $request + * @param ListWithCustomPagerRequest $request * @param ?array{ * baseUrl?: string, * maxRetries?: int, @@ -63,14 +63,14 @@ public function __construct( * } $options * @return Pager */ - public function listUsernamesCustom(ListUsernamesRequestCustom $request = new ListUsernamesRequestCustom(), ?array $options = null): Pager + public function listWithCustomPager(ListWithCustomPagerRequest $request = new ListWithCustomPagerRequest(), ?array $options = null): Pager { - $response = $this->_listUsernamesCustom($request, $options); + $response = $this->_listWithCustomPager($request, $options); return new CustomPager(response: $response, client: $this); } /** - * @param ListUsernamesRequestCustom $request + * @param ListWithCustomPagerRequest $request * @param ?array{ * baseUrl?: string, * maxRetries?: int, @@ -79,14 +79,17 @@ public function listUsernamesCustom(ListUsernamesRequestCustom $request = new Li * queryParameters?: array, * bodyProperties?: array, * } $options - * @return ?UsernameCursor + * @return ?UsersListResponse * @throws SeedException * @throws SeedApiException */ - private function _listUsernamesCustom(ListUsernamesRequestCustom $request = new ListUsernamesRequestCustom(), ?array $options = null): ?UsernameCursor + private function _listWithCustomPager(ListWithCustomPagerRequest $request = new ListWithCustomPagerRequest(), ?array $options = null): ?UsersListResponse { $options = array_merge($this->options, $options ?? []); $query = []; + if ($request->limit != null) { + $query['limit'] = $request->limit; + } if ($request->startingAfter != null) { $query['starting_after'] = $request->startingAfter; } @@ -106,7 +109,7 @@ private function _listUsernamesCustom(ListUsernamesRequestCustom $request = new if (empty($json)) { return null; } - return UsernameCursor::fromJson($json); + return UsersListResponse::fromJson($json); } } catch (JsonException $e) { throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); diff --git a/seed/php-sdk/pagination-custom/src/dynamic-snippets/example0/snippet.php b/seed/php-sdk/pagination-custom/src/dynamic-snippets/example0/snippet.php index 000cb549d876..720084a41713 100644 --- a/seed/php-sdk/pagination-custom/src/dynamic-snippets/example0/snippet.php +++ b/seed/php-sdk/pagination-custom/src/dynamic-snippets/example0/snippet.php @@ -3,7 +3,7 @@ namespace Example; use Seed\SeedClient; -use Seed\Users\Requests\ListUsernamesRequestCustom; +use Seed\Users\Requests\ListWithCustomPagerRequest; $client = new SeedClient( token: '', @@ -11,8 +11,9 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->users->listUsernamesCustom( - new ListUsernamesRequestCustom([ +$client->users->listWithCustomPager( + new ListWithCustomPagerRequest([ + 'limit' => 1, 'startingAfter' => 'starting_after', ]), ); From d415016d912089c61fa15688290d53a76d788156 Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 7 Apr 2026 08:58:48 -0400 Subject: [PATCH 03/21] chore(typescript): update ts-sdk seed (#14705) Co-authored-by: davidkonigsberg <72822263+davidkonigsberg@users.noreply.github.com> --- seed/ts-sdk/pagination-custom/README.md | 28 +- seed/ts-sdk/pagination-custom/reference.md | 10 +- seed/ts-sdk/pagination-custom/snippet.json | 4 +- .../src/api/resources/users/client/Client.ts | 18 +- .../requests/ListUsernamesRequestCustom.ts | 15 - .../requests/ListWithCustomPagerRequest.ts | 15 + .../resources/users/client/requests/index.ts | 2 +- .../pagination-custom/src/api/types/Link.ts | 7 + .../src/api/types/UsernameCursor.ts | 7 - .../src/api/types/UsernamePage.ts | 6 - .../src/api/types/UsersListResponse.ts | 11 + .../pagination-custom/src/api/types/index.ts | 4 +- .../.fern/metadata.json | 7 + .../.github/workflows/ci.yml | 46 + .../.gitignore | 3 + .../CONTRIBUTING.md | 133 ++ .../README.md | 288 +++++ .../biome.json | 74 ++ .../package.json | 69 + .../pnpm-workspace.yaml | 1 + .../reference.md | 213 ++++ .../scripts/rename-to-esm-files.js | 123 ++ .../snippet.json | 38 + .../src/BaseClient.ts | 59 + .../src/Client.ts | 242 ++++ .../src/api/client/index.ts | 1 + .../requests/CreatePlantWithSchemaRequest.ts | 13 + .../api/client/requests/UpdatePlantRequest.ts | 18 + .../src/api/client/requests/index.ts | 2 + .../src/api/index.ts | 2 + .../src/api/types/CreatePlantResponse.ts | 6 + .../types/CreatePlantWithSchemaResponse.ts | 6 + .../src/api/types/UpdatePlantResponse.ts | 6 + .../src/api/types/index.ts | 3 + .../src/core/exports.ts | 1 + .../src/core/fetcher/APIResponse.ts | 23 + .../src/core/fetcher/BinaryResponse.ts | 34 + .../src/core/fetcher/EndpointMetadata.ts | 13 + .../src/core/fetcher/EndpointSupplier.ts | 14 + .../src/core/fetcher/Fetcher.ts | 404 ++++++ .../src/core/fetcher/Headers.ts | 93 ++ .../src/core/fetcher/HttpResponsePromise.ts | 116 ++ .../src/core/fetcher/RawResponse.ts | 61 + .../src/core/fetcher/Supplier.ts | 11 + .../src/core/fetcher/createRequestUrl.ts | 6 + .../src/core/fetcher/getErrorResponseBody.ts | 33 + .../src/core/fetcher/getFetchFn.ts | 3 + .../src/core/fetcher/getHeader.ts | 8 + .../src/core/fetcher/getRequestBody.ts | 20 + .../src/core/fetcher/getResponseBody.ts | 58 + .../src/core/fetcher/index.ts | 13 + .../core/fetcher/makePassthroughRequest.ts | 189 +++ .../src/core/fetcher/makeRequest.ts | 70 ++ .../src/core/fetcher/requestWithRetries.ts | 64 + .../src/core/fetcher/signals.ts | 26 + .../src/core/headers.ts | 33 + .../src/core/index.ts | 4 + .../src/core/json.ts | 27 + .../src/core/logging/exports.ts | 19 + .../src/core/logging/index.ts | 1 + .../src/core/logging/logger.ts | 203 +++ .../src/core/runtime/index.ts | 1 + .../src/core/runtime/runtime.ts | 134 ++ .../src/core/url/encodePathParam.ts | 18 + .../src/core/url/index.ts | 3 + .../src/core/url/join.ts | 79 ++ .../src/core/url/qs.ts | 74 ++ .../src/errors/SeedApiError.ts | 64 + .../src/errors/SeedApiTimeoutError.ts | 18 + .../src/errors/handleNonStatusCodeError.ts | 40 + .../src/errors/index.ts | 2 + .../src/exports.ts | 1 + .../src/index.ts | 5 + .../src/version.ts | 1 + .../tests/custom.test.ts | 13 + .../tests/mock-server/MockServer.ts | 29 + .../tests/mock-server/MockServerPool.ts | 106 ++ .../tests/mock-server/mockEndpointBuilder.ts | 234 ++++ .../tests/mock-server/randomBaseUrl.ts | 4 + .../tests/mock-server/setup.ts | 10 + .../tests/mock-server/withFormUrlEncoded.ts | 104 ++ .../tests/mock-server/withHeaders.ts | 70 ++ .../tests/mock-server/withJson.ts | 173 +++ .../tests/setup.ts | 80 ++ .../tests/tsconfig.json | 10 + .../tests/unit/fetcher/Fetcher.test.ts | 262 ++++ .../unit/fetcher/HttpResponsePromise.test.ts | 143 +++ .../tests/unit/fetcher/RawResponse.test.ts | 34 + .../unit/fetcher/createRequestUrl.test.ts | 163 +++ .../tests/unit/fetcher/getRequestBody.test.ts | 129 ++ .../unit/fetcher/getResponseBody.test.ts | 97 ++ .../tests/unit/fetcher/logging.test.ts | 517 ++++++++ .../fetcher/makePassthroughRequest.test.ts | 398 ++++++ .../tests/unit/fetcher/makeRequest.test.ts | 158 +++ .../tests/unit/fetcher/redacting.test.ts | 1115 +++++++++++++++++ .../unit/fetcher/requestWithRetries.test.ts | 230 ++++ .../tests/unit/fetcher/signals.test.ts | 69 + .../tests/unit/fetcher/test-file.txt | 1 + .../tests/unit/logging/logger.test.ts | 454 +++++++ .../tests/unit/url/join.test.ts | 284 +++++ .../tests/unit/url/qs.test.ts | 278 ++++ .../tests/wire/.gitkeep | 0 .../tests/wire/main.test.ts | 97 ++ .../tsconfig.base.json | 17 + .../tsconfig.cjs.json | 9 + .../tsconfig.esm.json | 10 + .../tsconfig.json | 3 + .../vitest.config.mts | 32 + 108 files changed, 8743 insertions(+), 57 deletions(-) delete mode 100644 seed/ts-sdk/pagination-custom/src/api/resources/users/client/requests/ListUsernamesRequestCustom.ts create mode 100644 seed/ts-sdk/pagination-custom/src/api/resources/users/client/requests/ListWithCustomPagerRequest.ts create mode 100644 seed/ts-sdk/pagination-custom/src/api/types/Link.ts delete mode 100644 seed/ts-sdk/pagination-custom/src/api/types/UsernameCursor.ts delete mode 100644 seed/ts-sdk/pagination-custom/src/api/types/UsernamePage.ts create mode 100644 seed/ts-sdk/pagination-custom/src/api/types/UsersListResponse.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/.fern/metadata.json create mode 100644 seed/ts-sdk/schemaless-request-body-examples/.github/workflows/ci.yml create mode 100644 seed/ts-sdk/schemaless-request-body-examples/.gitignore create mode 100644 seed/ts-sdk/schemaless-request-body-examples/CONTRIBUTING.md create mode 100644 seed/ts-sdk/schemaless-request-body-examples/README.md create mode 100644 seed/ts-sdk/schemaless-request-body-examples/biome.json create mode 100644 seed/ts-sdk/schemaless-request-body-examples/package.json create mode 100644 seed/ts-sdk/schemaless-request-body-examples/pnpm-workspace.yaml create mode 100644 seed/ts-sdk/schemaless-request-body-examples/reference.md create mode 100644 seed/ts-sdk/schemaless-request-body-examples/scripts/rename-to-esm-files.js create mode 100644 seed/ts-sdk/schemaless-request-body-examples/snippet.json create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/BaseClient.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/Client.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/api/client/index.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/api/client/requests/CreatePlantWithSchemaRequest.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/api/client/requests/UpdatePlantRequest.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/api/client/requests/index.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/api/index.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/api/types/CreatePlantResponse.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/api/types/CreatePlantWithSchemaResponse.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/api/types/UpdatePlantResponse.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/api/types/index.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/exports.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/APIResponse.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/BinaryResponse.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/EndpointMetadata.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/EndpointSupplier.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/Fetcher.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/Headers.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/HttpResponsePromise.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/RawResponse.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/Supplier.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/createRequestUrl.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getErrorResponseBody.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getFetchFn.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getHeader.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getRequestBody.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getResponseBody.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/index.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/makePassthroughRequest.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/makeRequest.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/requestWithRetries.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/signals.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/headers.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/index.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/json.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/logging/exports.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/logging/index.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/logging/logger.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/runtime/index.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/runtime/runtime.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/url/encodePathParam.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/url/index.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/url/join.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/core/url/qs.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/errors/SeedApiError.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/errors/SeedApiTimeoutError.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/errors/handleNonStatusCodeError.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/errors/index.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/exports.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/index.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/src/version.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/custom.test.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/MockServer.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/MockServerPool.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/mockEndpointBuilder.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/randomBaseUrl.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/setup.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/withFormUrlEncoded.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/withHeaders.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/withJson.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/setup.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/tsconfig.json create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/Fetcher.test.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/HttpResponsePromise.test.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/RawResponse.test.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/createRequestUrl.test.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/getRequestBody.test.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/getResponseBody.test.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/logging.test.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/makePassthroughRequest.test.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/makeRequest.test.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/redacting.test.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/requestWithRetries.test.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/signals.test.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/test-file.txt create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/unit/logging/logger.test.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/unit/url/join.test.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/unit/url/qs.test.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/wire/.gitkeep create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tests/wire/main.test.ts create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tsconfig.base.json create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tsconfig.cjs.json create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tsconfig.esm.json create mode 100644 seed/ts-sdk/schemaless-request-body-examples/tsconfig.json create mode 100644 seed/ts-sdk/schemaless-request-body-examples/vitest.config.mts diff --git a/seed/ts-sdk/pagination-custom/README.md b/seed/ts-sdk/pagination-custom/README.md index 757ef4672cbd..b68cb6763b40 100644 --- a/seed/ts-sdk/pagination-custom/README.md +++ b/seed/ts-sdk/pagination-custom/README.md @@ -44,7 +44,8 @@ Instantiate and use the client with the following: import { SeedPaginationClient } from "@fern/pagination-custom"; const client = new SeedPaginationClient({ environment: "YOUR_BASE_URL", token: "YOUR_TOKEN" }); -const pageableResponse = await client.users.listUsernamesCustom({ +const pageableResponse = await client.users.listWithCustomPager({ + limit: 1, starting_after: "starting_after" }); for await (const item of pageableResponse) { @@ -52,7 +53,8 @@ for await (const item of pageableResponse) { } // Or you can manually iterate page-by-page -let page = await client.users.listUsernamesCustom({ +let page = await client.users.listWithCustomPager({ + limit: 1, starting_after: "starting_after" }); while (page.hasNextPage()) { @@ -71,7 +73,7 @@ following namespace: ```typescript import { SeedPagination } from "@fern/pagination-custom"; -const request: SeedPagination.ListUsernamesRequestCustom = { +const request: SeedPagination.ListWithCustomPagerRequest = { ... }; ``` @@ -85,7 +87,7 @@ will be thrown. import { SeedPaginationError } from "@fern/pagination-custom"; try { - await client.users.listUsernamesCustom(...); + await client.users.listWithCustomPager(...); } catch (err) { if (err instanceof SeedPaginationError) { console.log(err.statusCode); @@ -104,7 +106,8 @@ List endpoints are paginated. The SDK provides an iterator so that you can simpl import { SeedPaginationClient } from "@fern/pagination-custom"; const client = new SeedPaginationClient({ environment: "YOUR_BASE_URL", token: "YOUR_TOKEN" }); -const pageableResponse = await client.users.listUsernamesCustom({ +const pageableResponse = await client.users.listWithCustomPager({ + limit: 1, starting_after: "starting_after" }); for await (const item of pageableResponse) { @@ -112,7 +115,8 @@ for await (const item of pageableResponse) { } // Or you can manually iterate page-by-page -let page = await client.users.listUsernamesCustom({ +let page = await client.users.listWithCustomPager({ + limit: 1, starting_after: "starting_after" }); while (page.hasNextPage()) { @@ -149,7 +153,7 @@ const client = new SeedPaginationClient({ } }); -const response = await client.users.listUsernamesCustom(..., { +const response = await client.users.listWithCustomPager(..., { headers: { 'X-Custom-Header': 'custom value' } @@ -161,7 +165,7 @@ const response = await client.users.listUsernamesCustom(..., { If you would like to send additional query string parameters as part of the request, use the `queryParams` request option. ```typescript -const response = await client.users.listUsernamesCustom(..., { +const response = await client.users.listWithCustomPager(..., { queryParams: { 'customQueryParamKey': 'custom query param value' } @@ -183,7 +187,7 @@ A request is deemed retryable when any of the following HTTP status codes is ret Use the `maxRetries` request option to configure this behavior. ```typescript -const response = await client.users.listUsernamesCustom(..., { +const response = await client.users.listWithCustomPager(..., { maxRetries: 0 // override maxRetries at the request level }); ``` @@ -193,7 +197,7 @@ const response = await client.users.listUsernamesCustom(..., { The SDK defaults to a 60 second timeout. Use the `timeoutInSeconds` option to configure this behavior. ```typescript -const response = await client.users.listUsernamesCustom(..., { +const response = await client.users.listWithCustomPager(..., { timeoutInSeconds: 30 // override timeout to 30s }); ``` @@ -204,7 +208,7 @@ The SDK allows users to abort requests at any point by passing in an abort signa ```typescript const controller = new AbortController(); -const response = await client.users.listUsernamesCustom(..., { +const response = await client.users.listWithCustomPager(..., { abortSignal: controller.signal }); controller.abort(); // aborts the request @@ -216,7 +220,7 @@ The SDK provides access to raw response data, including headers, through the `.w The `.withRawResponse()` method returns a promise that results to an object with a `data` and a `rawResponse` property. ```typescript -const { data, rawResponse } = await client.users.listUsernamesCustom(...).withRawResponse(); +const { data, rawResponse } = await client.users.listWithCustomPager(...).withRawResponse(); console.log(data); console.log(rawResponse.headers['X-My-Header']); diff --git a/seed/ts-sdk/pagination-custom/reference.md b/seed/ts-sdk/pagination-custom/reference.md index 174a2ea71dbd..63175e586710 100644 --- a/seed/ts-sdk/pagination-custom/reference.md +++ b/seed/ts-sdk/pagination-custom/reference.md @@ -1,6 +1,6 @@ # Reference ## Users -
client.users.listUsernamesCustom({ ...params }) -> core.MyPager<string, SeedPagination.UsernameCursor> +
client.users.listWithCustomPager({ ...params }) -> core.MyPager<string, SeedPagination.UsersListResponse>
@@ -13,7 +13,8 @@
```typescript -const pageableResponse = await client.users.listUsernamesCustom({ +const pageableResponse = await client.users.listWithCustomPager({ + limit: 1, starting_after: "starting_after" }); for await (const item of pageableResponse) { @@ -21,7 +22,8 @@ for await (const item of pageableResponse) { } // Or you can manually iterate page-by-page -let page = await client.users.listUsernamesCustom({ +let page = await client.users.listWithCustomPager({ + limit: 1, starting_after: "starting_after" }); while (page.hasNextPage()) { @@ -45,7 +47,7 @@ const response = page.response;
-**request:** `SeedPagination.ListUsernamesRequestCustom` +**request:** `SeedPagination.ListWithCustomPagerRequest`
diff --git a/seed/ts-sdk/pagination-custom/snippet.json b/seed/ts-sdk/pagination-custom/snippet.json index 86deaeed4161..21609ce86e3d 100644 --- a/seed/ts-sdk/pagination-custom/snippet.json +++ b/seed/ts-sdk/pagination-custom/snippet.json @@ -4,11 +4,11 @@ "id": { "path": "/users", "method": "GET", - "identifier_override": "endpoint_users.listUsernamesCustom" + "identifier_override": "endpoint_users.listWithCustomPager" }, "snippet": { "type": "typescript", - "client": "import { SeedPaginationClient } from \"@fern/pagination-custom\";\n\nconst client = new SeedPaginationClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nconst pageableResponse = await client.users.listUsernamesCustom({\n starting_after: \"starting_after\"\n});\nfor await (const item of pageableResponse) {\n console.log(item);\n}\n\n// Or you can manually iterate page-by-page\nlet page = await client.users.listUsernamesCustom({\n starting_after: \"starting_after\"\n});\nwhile (page.hasNextPage()) {\n page = page.getNextPage();\n}\n\n// You can also access the underlying response\nconst response = page.response;\n" + "client": "import { SeedPaginationClient } from \"@fern/pagination-custom\";\n\nconst client = new SeedPaginationClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nconst pageableResponse = await client.users.listWithCustomPager({\n limit: 1,\n starting_after: \"starting_after\"\n});\nfor await (const item of pageableResponse) {\n console.log(item);\n}\n\n// Or you can manually iterate page-by-page\nlet page = await client.users.listWithCustomPager({\n limit: 1,\n starting_after: \"starting_after\"\n});\nwhile (page.hasNextPage()) {\n page = page.getNextPage();\n}\n\n// You can also access the underlying response\nconst response = page.response;\n" } } ], diff --git a/seed/ts-sdk/pagination-custom/src/api/resources/users/client/Client.ts b/seed/ts-sdk/pagination-custom/src/api/resources/users/client/Client.ts index bedab4ae1774..bfb894c0cfa0 100644 --- a/seed/ts-sdk/pagination-custom/src/api/resources/users/client/Client.ts +++ b/seed/ts-sdk/pagination-custom/src/api/resources/users/client/Client.ts @@ -22,20 +22,22 @@ export class UsersClient { } /** - * @param {SeedPagination.ListUsernamesRequestCustom} request + * @param {SeedPagination.ListWithCustomPagerRequest} request * @param {UsersClient.RequestOptions} requestOptions - Request-specific configuration. * * @example - * await client.users.listUsernamesCustom({ + * await client.users.listWithCustomPager({ + * limit: 1, * starting_after: "starting_after" * }) */ - public async listUsernamesCustom( - request: SeedPagination.ListUsernamesRequestCustom = {}, + public async listWithCustomPager( + request: SeedPagination.ListWithCustomPagerRequest = {}, requestOptions?: UsersClient.RequestOptions, - ): Promise> { - const { starting_after: startingAfter } = request; + ): Promise> { + const { limit, starting_after: startingAfter } = request; const _queryParams: Record = { + limit, starting_after: startingAfter, }; const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); @@ -55,7 +57,7 @@ export class UsersClient { logging: this._options.logging, }; const _sendRequest = async (request: core.Fetcher.Args) => { - const _response = await core.fetcher(request); + const _response = await core.fetcher(request); if (_response.ok) { return _response; } @@ -68,7 +70,7 @@ export class UsersClient { } return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/users"); }; - return core.createMyPager({ + return core.createMyPager({ sendRequest: _sendRequest, initialHttpRequest: _request, clientOptions: this._options, diff --git a/seed/ts-sdk/pagination-custom/src/api/resources/users/client/requests/ListUsernamesRequestCustom.ts b/seed/ts-sdk/pagination-custom/src/api/resources/users/client/requests/ListUsernamesRequestCustom.ts deleted file mode 100644 index 04dec210dd5a..000000000000 --- a/seed/ts-sdk/pagination-custom/src/api/resources/users/client/requests/ListUsernamesRequestCustom.ts +++ /dev/null @@ -1,15 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -/** - * @example - * { - * starting_after: "starting_after" - * } - */ -export interface ListUsernamesRequestCustom { - /** - * The cursor used for pagination in order to fetch - * the next page of results. - */ - starting_after?: string; -} diff --git a/seed/ts-sdk/pagination-custom/src/api/resources/users/client/requests/ListWithCustomPagerRequest.ts b/seed/ts-sdk/pagination-custom/src/api/resources/users/client/requests/ListWithCustomPagerRequest.ts new file mode 100644 index 000000000000..7c088f57c342 --- /dev/null +++ b/seed/ts-sdk/pagination-custom/src/api/resources/users/client/requests/ListWithCustomPagerRequest.ts @@ -0,0 +1,15 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * limit: 1, + * starting_after: "starting_after" + * } + */ +export interface ListWithCustomPagerRequest { + /** The maximum number of results to return. */ + limit?: number; + /** The cursor used for pagination. */ + starting_after?: string; +} diff --git a/seed/ts-sdk/pagination-custom/src/api/resources/users/client/requests/index.ts b/seed/ts-sdk/pagination-custom/src/api/resources/users/client/requests/index.ts index fa63b0d4bc04..e21c2aa9558c 100644 --- a/seed/ts-sdk/pagination-custom/src/api/resources/users/client/requests/index.ts +++ b/seed/ts-sdk/pagination-custom/src/api/resources/users/client/requests/index.ts @@ -1 +1 @@ -export type { ListUsernamesRequestCustom } from "./ListUsernamesRequestCustom.js"; +export type { ListWithCustomPagerRequest } from "./ListWithCustomPagerRequest.js"; diff --git a/seed/ts-sdk/pagination-custom/src/api/types/Link.ts b/seed/ts-sdk/pagination-custom/src/api/types/Link.ts new file mode 100644 index 000000000000..37b906825a6d --- /dev/null +++ b/seed/ts-sdk/pagination-custom/src/api/types/Link.ts @@ -0,0 +1,7 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface Link { + rel: string; + method: string; + href: string; +} diff --git a/seed/ts-sdk/pagination-custom/src/api/types/UsernameCursor.ts b/seed/ts-sdk/pagination-custom/src/api/types/UsernameCursor.ts deleted file mode 100644 index 9ae45c58579a..000000000000 --- a/seed/ts-sdk/pagination-custom/src/api/types/UsernameCursor.ts +++ /dev/null @@ -1,7 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type * as SeedPagination from "../index.js"; - -export interface UsernameCursor { - cursor: SeedPagination.UsernamePage; -} diff --git a/seed/ts-sdk/pagination-custom/src/api/types/UsernamePage.ts b/seed/ts-sdk/pagination-custom/src/api/types/UsernamePage.ts deleted file mode 100644 index b72da0397360..000000000000 --- a/seed/ts-sdk/pagination-custom/src/api/types/UsernamePage.ts +++ /dev/null @@ -1,6 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -export interface UsernamePage { - after?: string | undefined; - data: string[]; -} diff --git a/seed/ts-sdk/pagination-custom/src/api/types/UsersListResponse.ts b/seed/ts-sdk/pagination-custom/src/api/types/UsersListResponse.ts new file mode 100644 index 000000000000..723e42f80bb1 --- /dev/null +++ b/seed/ts-sdk/pagination-custom/src/api/types/UsersListResponse.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedPagination from "../index.js"; + +export interface UsersListResponse { + limit?: number | undefined; + count?: number | undefined; + has_more?: boolean | undefined; + links: SeedPagination.Link[]; + data: string[]; +} diff --git a/seed/ts-sdk/pagination-custom/src/api/types/index.ts b/seed/ts-sdk/pagination-custom/src/api/types/index.ts index 8480ddf13b75..6441a4059e1b 100644 --- a/seed/ts-sdk/pagination-custom/src/api/types/index.ts +++ b/seed/ts-sdk/pagination-custom/src/api/types/index.ts @@ -1,2 +1,2 @@ -export * from "./UsernameCursor.js"; -export * from "./UsernamePage.js"; +export * from "./Link.js"; +export * from "./UsersListResponse.js"; diff --git a/seed/ts-sdk/schemaless-request-body-examples/.fern/metadata.json b/seed/ts-sdk/schemaless-request-body-examples/.fern/metadata.json new file mode 100644 index 000000000000..7b69b32c1f15 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/.fern/metadata.json @@ -0,0 +1,7 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-typescript-sdk", + "generatorVersion": "local", + "originGitCommit": "DUMMY", + "sdkVersion": "0.0.1" +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/.github/workflows/ci.yml b/seed/ts-sdk/schemaless-request-body-examples/.github/workflows/ci.yml new file mode 100644 index 000000000000..93fba226cb67 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/.github/workflows/ci.yml @@ -0,0 +1,46 @@ +name: ci + +on: [push] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v6 + + - name: Set up node + uses: actions/setup-node@v6 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Compile + run: pnpm build + + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v6 + + - name: Set up node + uses: actions/setup-node@v6 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Test + run: pnpm test diff --git a/seed/ts-sdk/schemaless-request-body-examples/.gitignore b/seed/ts-sdk/schemaless-request-body-examples/.gitignore new file mode 100644 index 000000000000..72271e049c02 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/.gitignore @@ -0,0 +1,3 @@ +node_modules +.DS_Store +/dist \ No newline at end of file diff --git a/seed/ts-sdk/schemaless-request-body-examples/CONTRIBUTING.md b/seed/ts-sdk/schemaless-request-body-examples/CONTRIBUTING.md new file mode 100644 index 000000000000..fe5bc2f77e0b --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/CONTRIBUTING.md @@ -0,0 +1,133 @@ +# Contributing + +Thanks for your interest in contributing to this SDK! This document provides guidelines for contributing to the project. + +## Getting Started + +### Prerequisites + +- Node.js 20 or higher +- pnpm package manager + +### Installation + +Install the project dependencies: + +```bash +pnpm install +``` + +### Building + +Build the project: + +```bash +pnpm build +``` + +### Testing + +Run the test suite: + +```bash +pnpm test +``` + +Run specific test types: +- `pnpm test:unit` - Run unit tests +- `pnpm test:wire` - Run wire/integration tests + +### Linting and Formatting + +Check code style: + +```bash +pnpm run lint +pnpm run format:check +``` + +Fix code style issues: + +```bash +pnpm run lint:fix +pnpm run format:fix +``` + +Or use the combined check command: + +```bash +pnpm run check:fix +``` + +## About Generated Code + +**Important**: Most files in this SDK are automatically generated by [Fern](https://buildwithfern.com) from the API definition. Direct modifications to generated files will be overwritten the next time the SDK is generated. + +### Generated Files + +The following directories contain generated code: +- `src/api/` - API client classes and types +- `src/serialization/` - Serialization/deserialization logic +- Most TypeScript files in `src/` + +### How to Customize + +If you need to customize the SDK, you have two options: + +#### Option 1: Use `.fernignore` + +For custom code that should persist across SDK regenerations: + +1. Create a `.fernignore` file in the project root +2. Add file patterns for files you want to preserve (similar to `.gitignore` syntax) +3. Add your custom code to those files + +Files listed in `.fernignore` will not be overwritten when the SDK is regenerated. + +For more information, see the [Fern documentation on custom code](https://buildwithfern.com/learn/sdks/overview/custom-code). + +#### Option 2: Contribute to the Generator + +If you want to change how code is generated for all users of this SDK: + +1. The TypeScript SDK generator lives in the [Fern repository](https://github.com/fern-api/fern) +2. Generator code is located at `generators/typescript/sdk/` +3. Follow the [Fern contributing guidelines](https://github.com/fern-api/fern/blob/main/CONTRIBUTING.md) +4. Submit a pull request with your changes to the generator + +This approach is best for: +- Bug fixes in generated code +- New features that would benefit all users +- Improvements to code generation patterns + +## Making Changes + +### Workflow + +1. Create a new branch for your changes +2. Make your modifications +3. Run tests to ensure nothing breaks: `pnpm test` +4. Run linting and formatting: `pnpm run check:fix` +5. Build the project: `pnpm build` +6. Commit your changes with a clear commit message +7. Push your branch and create a pull request + +### Commit Messages + +Write clear, descriptive commit messages that explain what changed and why. + +### Code Style + +This project uses automated code formatting and linting. Run `pnpm run check:fix` before committing to ensure your code meets the project's style guidelines. + +## Questions or Issues? + +If you have questions or run into issues: + +1. Check the [Fern documentation](https://buildwithfern.com) +2. Search existing [GitHub issues](https://github.com/fern-api/fern/issues) +3. Open a new issue if your question hasn't been addressed + +## License + +By contributing to this project, you agree that your contributions will be licensed under the same license as the project. diff --git a/seed/ts-sdk/schemaless-request-body-examples/README.md b/seed/ts-sdk/schemaless-request-body-examples/README.md new file mode 100644 index 000000000000..735789c034bf --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/README.md @@ -0,0 +1,288 @@ +# Seed TypeScript Library + +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) +[![npm shield](https://img.shields.io/npm/v/@fern/schemaless-request-body-examples)](https://www.npmjs.com/package/@fern/schemaless-request-body-examples) + +The Seed TypeScript library provides convenient access to the Seed APIs from TypeScript. + +## Table of Contents + +- [Installation](#installation) +- [Reference](#reference) +- [Usage](#usage) +- [Request and Response Types](#request-and-response-types) +- [Exception Handling](#exception-handling) +- [Advanced](#advanced) + - [Additional Headers](#additional-headers) + - [Additional Query String Parameters](#additional-query-string-parameters) + - [Retries](#retries) + - [Timeouts](#timeouts) + - [Aborting Requests](#aborting-requests) + - [Access Raw Response Data](#access-raw-response-data) + - [Logging](#logging) + - [Custom Fetch](#custom-fetch) + - [Runtime Compatibility](#runtime-compatibility) +- [Contributing](#contributing) + +## Installation + +```sh +npm i -s @fern/schemaless-request-body-examples +``` + +## Reference + +A full reference for this library is available [here](./reference.md). + +## Usage + +Instantiate and use the client with the following: + +```typescript +import { SeedApiClient } from "@fern/schemaless-request-body-examples"; + +const client = new SeedApiClient({ environment: "YOUR_BASE_URL" }); +await client.createPlant({ + "name": "Venus Flytrap", + "species": "Dionaea muscipula", + "care": { + "light": "full sun", + "water": "distilled only", + "humidity": "high" + }, + "tags": [ + "carnivorous", + "tropical" + ] +}); +``` + +## Request and Response Types + +The SDK exports all request and response types as TypeScript interfaces. Simply import them with the +following namespace: + +```typescript +import { SeedApi } from "@fern/schemaless-request-body-examples"; + +const request: SeedApi.UpdatePlantRequest = { + ... +}; +``` + +## Exception Handling + +When the API returns a non-success status code (4xx or 5xx response), a subclass of the following error +will be thrown. + +```typescript +import { SeedApiError } from "@fern/schemaless-request-body-examples"; + +try { + await client.createPlant(...); +} catch (err) { + if (err instanceof SeedApiError) { + console.log(err.statusCode); + console.log(err.message); + console.log(err.body); + console.log(err.rawResponse); + } +} +``` + +## Advanced + +### Additional Headers + +If you would like to send additional headers as part of the request, use the `headers` request option. + +```typescript +import { SeedApiClient } from "@fern/schemaless-request-body-examples"; + +const client = new SeedApiClient({ + ... + headers: { + 'X-Custom-Header': 'custom value' + } +}); + +const response = await client.createPlant(..., { + headers: { + 'X-Custom-Header': 'custom value' + } +}); +``` + +### Additional Query String Parameters + +If you would like to send additional query string parameters as part of the request, use the `queryParams` request option. + +```typescript +const response = await client.createPlant(..., { + queryParams: { + 'customQueryParamKey': 'custom query param value' + } +}); +``` + +### Retries + +The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long +as the request is deemed retryable and the number of retry attempts has not grown larger than the configured +retry limit (default: 2). + +A request is deemed retryable when any of the following HTTP status codes is returned: + +- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) +- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) +- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) + +Use the `maxRetries` request option to configure this behavior. + +```typescript +const response = await client.createPlant(..., { + maxRetries: 0 // override maxRetries at the request level +}); +``` + +### Timeouts + +The SDK defaults to a 60 second timeout. Use the `timeoutInSeconds` option to configure this behavior. + +```typescript +const response = await client.createPlant(..., { + timeoutInSeconds: 30 // override timeout to 30s +}); +``` + +### Aborting Requests + +The SDK allows users to abort requests at any point by passing in an abort signal. + +```typescript +const controller = new AbortController(); +const response = await client.createPlant(..., { + abortSignal: controller.signal +}); +controller.abort(); // aborts the request +``` + +### Access Raw Response Data + +The SDK provides access to raw response data, including headers, through the `.withRawResponse()` method. +The `.withRawResponse()` method returns a promise that results to an object with a `data` and a `rawResponse` property. + +```typescript +const { data, rawResponse } = await client.createPlant(...).withRawResponse(); + +console.log(data); +console.log(rawResponse.headers['X-My-Header']); +``` + +### Logging + +The SDK supports logging. You can configure the logger by passing in a `logging` object to the client options. + +```typescript +import { SeedApiClient, logging } from "@fern/schemaless-request-body-examples"; + +const client = new SeedApiClient({ + ... + logging: { + level: logging.LogLevel.Debug, // defaults to logging.LogLevel.Info + logger: new logging.ConsoleLogger(), // defaults to ConsoleLogger + silent: false, // defaults to true, set to false to enable logging + } +}); +``` +The `logging` object can have the following properties: +- `level`: The log level to use. Defaults to `logging.LogLevel.Info`. +- `logger`: The logger to use. Defaults to a `logging.ConsoleLogger`. +- `silent`: Whether to silence the logger. Defaults to `true`. + +The `level` property can be one of the following values: +- `logging.LogLevel.Debug` +- `logging.LogLevel.Info` +- `logging.LogLevel.Warn` +- `logging.LogLevel.Error` + +To provide a custom logger, you can pass in an object that implements the `logging.ILogger` interface. + +
+Custom logger examples + +Here's an example using the popular `winston` logging library. +```ts +import winston from 'winston'; + +const winstonLogger = winston.createLogger({...}); + +const logger: logging.ILogger = { + debug: (msg, ...args) => winstonLogger.debug(msg, ...args), + info: (msg, ...args) => winstonLogger.info(msg, ...args), + warn: (msg, ...args) => winstonLogger.warn(msg, ...args), + error: (msg, ...args) => winstonLogger.error(msg, ...args), +}; +``` + +Here's an example using the popular `pino` logging library. + +```ts +import pino from 'pino'; + +const pinoLogger = pino({...}); + +const logger: logging.ILogger = { + debug: (msg, ...args) => pinoLogger.debug(args, msg), + info: (msg, ...args) => pinoLogger.info(args, msg), + warn: (msg, ...args) => pinoLogger.warn(args, msg), + error: (msg, ...args) => pinoLogger.error(args, msg), +}; +``` +
+ + +### Custom Fetch + +The SDK provides a low-level `fetch` method for making custom HTTP requests while still +benefiting from SDK-level configuration like authentication, retries, timeouts, and logging. +This is useful for calling API endpoints not yet supported in the SDK. + +```typescript +const response = await client.fetch("/v1/custom/endpoint", { + method: "GET", +}, { + timeoutInSeconds: 30, + maxRetries: 3, + headers: { + "X-Custom-Header": "custom-value", + }, +}); + +const data = await response.json(); +``` + +### Runtime Compatibility + + +The SDK works in the following runtimes: + + + +- Node.js 18+ +- Vercel +- Cloudflare Workers +- Deno v1.25+ +- Bun 1.0+ +- React Native + + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! diff --git a/seed/ts-sdk/schemaless-request-body-examples/biome.json b/seed/ts-sdk/schemaless-request-body-examples/biome.json new file mode 100644 index 000000000000..6b89164f9f99 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/biome.json @@ -0,0 +1,74 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.4.10/schema.json", + "root": true, + "vcs": { + "enabled": false + }, + "files": { + "ignoreUnknown": true, + "includes": [ + "**", + "!!dist", + "!!**/dist", + "!!lib", + "!!**/lib", + "!!_tmp_*", + "!!**/_tmp_*", + "!!*.tmp", + "!!**/*.tmp", + "!!.tmp/", + "!!**/.tmp/", + "!!*.log", + "!!**/*.log", + "!!**/.DS_Store", + "!!**/Thumbs.db" + ] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 120 + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + }, + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } + } + }, + "linter": { + "rules": { + "style": { + "useNodejsImportProtocol": "off" + }, + "suspicious": { + "noAssignInExpressions": "warn", + "noUselessEscapeInString": { + "level": "warn", + "fix": "none", + "options": {} + }, + "noThenProperty": "warn", + "useIterableCallbackReturn": "warn", + "noShadowRestrictedNames": "warn", + "noTsIgnore": { + "level": "warn", + "fix": "none", + "options": {} + }, + "noConfusingVoidType": { + "level": "warn", + "fix": "none", + "options": {} + } + } + } + } +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/package.json b/seed/ts-sdk/schemaless-request-body-examples/package.json new file mode 100644 index 000000000000..b90481e7edea --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/package.json @@ -0,0 +1,69 @@ +{ + "name": "@fern/schemaless-request-body-examples", + "version": "0.0.1", + "private": false, + "repository": { + "type": "git", + "url": "git+https://github.com/schemaless-request-body-examples/fern.git" + }, + "type": "commonjs", + "main": "./dist/cjs/index.js", + "module": "./dist/esm/index.mjs", + "types": "./dist/cjs/index.d.ts", + "exports": { + ".": { + "import": { + "types": "./dist/esm/index.d.mts", + "default": "./dist/esm/index.mjs" + }, + "require": { + "types": "./dist/cjs/index.d.ts", + "default": "./dist/cjs/index.js" + }, + "default": "./dist/cjs/index.js" + }, + "./package.json": "./package.json" + }, + "files": [ + "dist", + "reference.md", + "README.md", + "LICENSE" + ], + "scripts": { + "format": "biome format --write --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", + "format:check": "biome format --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", + "lint": "biome lint --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", + "lint:fix": "biome lint --fix --unsafe --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", + "check": "biome check --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", + "check:fix": "biome check --fix --unsafe --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", + "build": "pnpm build:cjs && pnpm build:esm", + "build:cjs": "tsc --project ./tsconfig.cjs.json", + "build:esm": "tsc --project ./tsconfig.esm.json && node scripts/rename-to-esm-files.js dist/esm", + "test": "vitest", + "test:unit": "vitest --project unit", + "test:wire": "vitest --project wire" + }, + "dependencies": {}, + "devDependencies": { + "webpack": "^5.105.4", + "ts-loader": "^9.5.4", + "vitest": "^4.1.1", + "msw": "2.11.2", + "@types/node": "^18.19.70", + "typescript": "~5.9.3", + "@biomejs/biome": "2.4.10" + }, + "browser": { + "fs": false, + "os": false, + "path": false, + "stream": false, + "crypto": false + }, + "packageManager": "pnpm@10.33.0", + "engines": { + "node": ">=18.0.0" + }, + "sideEffects": false +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/pnpm-workspace.yaml b/seed/ts-sdk/schemaless-request-body-examples/pnpm-workspace.yaml new file mode 100644 index 000000000000..6e4c395107df --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/pnpm-workspace.yaml @@ -0,0 +1 @@ +packages: ['.'] \ No newline at end of file diff --git a/seed/ts-sdk/schemaless-request-body-examples/reference.md b/seed/ts-sdk/schemaless-request-body-examples/reference.md new file mode 100644 index 000000000000..48461295287d --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/reference.md @@ -0,0 +1,213 @@ +# Reference +
client.createPlant({ ...params }) -> SeedApi.CreatePlantResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Creates a plant with example JSON but no request body schema. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.createPlant({ + "name": "Venus Flytrap", + "species": "Dionaea muscipula", + "care": { + "light": "full sun", + "water": "distilled only", + "humidity": "high" + }, + "tags": [ + "carnivorous", + "tropical" + ] +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `unknown` + +
+
+ +
+
+ +**requestOptions:** `SeedApiClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.updatePlant({ ...params }) -> SeedApi.UpdatePlantResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Updates a plant with example JSON but no request body schema. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.updatePlant({ + plantId: "plantId", + body: { + "name": "Updated Venus Flytrap", + "care": { + "light": "partial shade" + } + } +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedApi.UpdatePlantRequest` + +
+
+ +
+
+ +**requestOptions:** `SeedApiClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.createPlantWithSchema({ ...params }) -> SeedApi.CreatePlantWithSchemaResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +A control endpoint that has both schema and example defined. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.createPlantWithSchema({ + name: "Sundew", + species: "Drosera capensis" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedApi.CreatePlantWithSchemaRequest` + +
+
+ +
+
+ +**requestOptions:** `SeedApiClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ diff --git a/seed/ts-sdk/schemaless-request-body-examples/scripts/rename-to-esm-files.js b/seed/ts-sdk/schemaless-request-body-examples/scripts/rename-to-esm-files.js new file mode 100644 index 000000000000..dc1df1cbbacb --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/scripts/rename-to-esm-files.js @@ -0,0 +1,123 @@ +#!/usr/bin/env node + +const fs = require("fs").promises; +const path = require("path"); + +const extensionMap = { + ".js": ".mjs", + ".d.ts": ".d.mts", +}; +const oldExtensions = Object.keys(extensionMap); + +async function findFiles(rootPath) { + const files = []; + + async function scan(directory) { + const entries = await fs.readdir(directory, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(directory, entry.name); + + if (entry.isDirectory()) { + if (entry.name !== "node_modules" && !entry.name.startsWith(".")) { + await scan(fullPath); + } + } else if (entry.isFile()) { + if (oldExtensions.some((ext) => entry.name.endsWith(ext))) { + files.push(fullPath); + } + } + } + } + + await scan(rootPath); + return files; +} + +async function updateFiles(files) { + const updatedFiles = []; + for (const file of files) { + const updated = await updateFileContents(file); + updatedFiles.push(updated); + } + + console.log(`Updated imports in ${updatedFiles.length} files.`); +} + +async function updateFileContents(file) { + const content = await fs.readFile(file, "utf8"); + + let newContent = content; + // Update each extension type defined in the map + for (const [oldExt, newExt] of Object.entries(extensionMap)) { + // Handle static imports/exports + const staticRegex = new RegExp(`(import|export)(.+from\\s+['"])(\\.\\.?\\/[^'"]+)(\\${oldExt})(['"])`, "g"); + newContent = newContent.replace(staticRegex, `$1$2$3${newExt}$5`); + + // Handle dynamic imports (yield import, await import, regular import()) + const dynamicRegex = new RegExp( + `(yield\\s+import|await\\s+import|import)\\s*\\(\\s*['"](\\.\\.\?\\/[^'"]+)(\\${oldExt})['"]\\s*\\)`, + "g", + ); + newContent = newContent.replace(dynamicRegex, `$1("$2${newExt}")`); + } + + if (content !== newContent) { + await fs.writeFile(file, newContent, "utf8"); + return true; + } + return false; +} + +async function renameFiles(files) { + let counter = 0; + for (const file of files) { + const ext = oldExtensions.find((ext) => file.endsWith(ext)); + const newExt = extensionMap[ext]; + + if (newExt) { + const newPath = file.slice(0, -ext.length) + newExt; + await fs.rename(file, newPath); + counter++; + } + } + + console.log(`Renamed ${counter} files.`); +} + +async function main() { + try { + const targetDir = process.argv[2]; + if (!targetDir) { + console.error("Please provide a target directory"); + process.exit(1); + } + + const targetPath = path.resolve(targetDir); + const targetStats = await fs.stat(targetPath); + + if (!targetStats.isDirectory()) { + console.error("The provided path is not a directory"); + process.exit(1); + } + + console.log(`Scanning directory: ${targetDir}`); + + const files = await findFiles(targetDir); + + if (files.length === 0) { + console.log("No matching files found."); + process.exit(0); + } + + console.log(`Found ${files.length} files.`); + await updateFiles(files); + await renameFiles(files); + console.log("\nDone!"); + } catch (error) { + console.error("An error occurred:", error.message); + process.exit(1); + } +} + +main(); diff --git a/seed/ts-sdk/schemaless-request-body-examples/snippet.json b/seed/ts-sdk/schemaless-request-body-examples/snippet.json new file mode 100644 index 000000000000..ecf6cf593fd7 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/snippet.json @@ -0,0 +1,38 @@ +{ + "endpoints": [ + { + "id": { + "path": "/plants", + "method": "POST", + "identifier_override": "endpoint_.createPlant" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedApiClient } from \"@fern/schemaless-request-body-examples\";\n\nconst client = new SeedApiClient({ environment: \"YOUR_BASE_URL\" });\nawait client.createPlant({\n \"name\": \"Venus Flytrap\",\n \"species\": \"Dionaea muscipula\",\n \"care\": {\n \"light\": \"full sun\",\n \"water\": \"distilled only\",\n \"humidity\": \"high\"\n },\n \"tags\": [\n \"carnivorous\",\n \"tropical\"\n ]\n});\n" + } + }, + { + "id": { + "path": "/plants/{plantId}", + "method": "PUT", + "identifier_override": "endpoint_.updatePlant" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedApiClient } from \"@fern/schemaless-request-body-examples\";\n\nconst client = new SeedApiClient({ environment: \"YOUR_BASE_URL\" });\nawait client.updatePlant({\n plantId: \"plantId\",\n body: {\n \"name\": \"Updated Venus Flytrap\",\n \"care\": {\n \"light\": \"partial shade\"\n }\n }\n});\n" + } + }, + { + "id": { + "path": "/plants/with-schema", + "method": "POST", + "identifier_override": "endpoint_.createPlantWithSchema" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedApiClient } from \"@fern/schemaless-request-body-examples\";\n\nconst client = new SeedApiClient({ environment: \"YOUR_BASE_URL\" });\nawait client.createPlantWithSchema({\n name: \"Sundew\",\n species: \"Drosera capensis\"\n});\n" + } + } + ], + "types": {} +} \ No newline at end of file diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/BaseClient.ts b/seed/ts-sdk/schemaless-request-body-examples/src/BaseClient.ts new file mode 100644 index 000000000000..5d65344ce650 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/BaseClient.ts @@ -0,0 +1,59 @@ +// This file was auto-generated by Fern from our API Definition. + +import { mergeHeaders } from "./core/headers.js"; +import * as core from "./core/index.js"; + +export interface BaseClientOptions { + environment: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | null | undefined>; + /** The default maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The default number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** Provide a custom fetch implementation. Useful for platforms that don't have a built-in fetch or need a custom implementation. */ + fetch?: typeof fetch; + /** Configure logging for the client. */ + logging?: core.logging.LogConfig | core.logging.Logger; +} + +export interface BaseRequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; + /** Additional headers to include in the request. */ + headers?: Record | null | undefined>; +} + +export type NormalizedClientOptions = T & { + logging: core.logging.Logger; +}; + +export function normalizeClientOptions( + options: T, +): NormalizedClientOptions { + const headers = mergeHeaders( + { + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/schemaless-request-body-examples", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/schemaless-request-body-examples/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + options?.headers, + ); + + return { + ...options, + logging: core.logging.createLogger(options?.logging), + headers, + } as NormalizedClientOptions; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/Client.ts b/seed/ts-sdk/schemaless-request-body-examples/src/Client.ts new file mode 100644 index 000000000000..e53df11f785a --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/Client.ts @@ -0,0 +1,242 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedApi from "./api/index.js"; +import type { BaseClientOptions, BaseRequestOptions } from "./BaseClient.js"; +import { type NormalizedClientOptions, normalizeClientOptions } from "./BaseClient.js"; +import { mergeHeaders } from "./core/headers.js"; +import * as core from "./core/index.js"; +import { handleNonStatusCodeError } from "./errors/handleNonStatusCodeError.js"; +import * as errors from "./errors/index.js"; + +export declare namespace SeedApiClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class SeedApiClient { + protected readonly _options: NormalizedClientOptions; + + constructor(options: SeedApiClient.Options) { + this._options = normalizeClientOptions(options); + } + + /** + * Creates a plant with example JSON but no request body schema. + * + * @param {unknown} request + * @param {SeedApiClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.createPlant({ + * "name": "Venus Flytrap", + * "species": "Dionaea muscipula", + * "care": { + * "light": "full sun", + * "water": "distilled only", + * "humidity": "high" + * }, + * "tags": [ + * "carnivorous", + * "tropical" + * ] + * }) + */ + public createPlant( + request?: unknown, + requestOptions?: SeedApiClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__createPlant(request, requestOptions)); + } + + private async __createPlant( + request?: unknown, + requestOptions?: SeedApiClient.RequestOptions, + ): Promise> { + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + "plants", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { data: _response.body as SeedApi.CreatePlantResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedApiError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/plants"); + } + + /** + * Updates a plant with example JSON but no request body schema. + * + * @param {SeedApi.UpdatePlantRequest} request + * @param {SeedApiClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.updatePlant({ + * plantId: "plantId", + * body: { + * "name": "Updated Venus Flytrap", + * "care": { + * "light": "partial shade" + * } + * } + * }) + */ + public updatePlant( + request: SeedApi.UpdatePlantRequest, + requestOptions?: SeedApiClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__updatePlant(request, requestOptions)); + } + + private async __updatePlant( + request: SeedApi.UpdatePlantRequest, + requestOptions?: SeedApiClient.RequestOptions, + ): Promise> { + const { plantId, body: _body } = request; + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `plants/${core.url.encodePathParam(plantId)}`, + ), + method: "PUT", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: _body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { data: _response.body as SeedApi.UpdatePlantResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedApiError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/plants/{plantId}"); + } + + /** + * A control endpoint that has both schema and example defined. + * + * @param {SeedApi.CreatePlantWithSchemaRequest} request + * @param {SeedApiClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.createPlantWithSchema({ + * name: "Sundew", + * species: "Drosera capensis" + * }) + */ + public createPlantWithSchema( + request: SeedApi.CreatePlantWithSchemaRequest = {}, + requestOptions?: SeedApiClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__createPlantWithSchema(request, requestOptions)); + } + + private async __createPlantWithSchema( + request: SeedApi.CreatePlantWithSchemaRequest = {}, + requestOptions?: SeedApiClient.RequestOptions, + ): Promise> { + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + "plants/with-schema", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedApi.CreatePlantWithSchemaResponse, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedApiError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/plants/with-schema"); + } + + /** + * Make a passthrough request using the SDK's configured auth, retry, logging, etc. + * This is useful for making requests to endpoints not yet supported in the SDK. + * The input can be a URL string, URL object, or Request object. Relative paths are resolved against the configured base URL. + * + * @param {Request | string | URL} input - The URL, path, or Request object. + * @param {RequestInit} init - Standard fetch RequestInit options. + * @param {core.PassthroughRequest.RequestOptions} requestOptions - Per-request overrides (timeout, retries, headers, abort signal). + * @returns {Promise} A standard Response object. + */ + public async fetch( + input: Request | string | URL, + init?: RequestInit, + requestOptions?: core.PassthroughRequest.RequestOptions, + ): Promise { + return core.makePassthroughRequest( + input, + init, + { + baseUrl: this._options.baseUrl ?? this._options.environment, + headers: this._options.headers, + timeoutInSeconds: this._options.timeoutInSeconds, + maxRetries: this._options.maxRetries, + fetch: this._options.fetch, + logging: this._options.logging, + }, + requestOptions, + ); + } +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/api/client/index.ts b/seed/ts-sdk/schemaless-request-body-examples/src/api/client/index.ts new file mode 100644 index 000000000000..195f9aa8a846 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/api/client/index.ts @@ -0,0 +1 @@ +export * from "./requests/index.js"; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/api/client/requests/CreatePlantWithSchemaRequest.ts b/seed/ts-sdk/schemaless-request-body-examples/src/api/client/requests/CreatePlantWithSchemaRequest.ts new file mode 100644 index 000000000000..0273bcd4a828 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/api/client/requests/CreatePlantWithSchemaRequest.ts @@ -0,0 +1,13 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * name: "Sundew", + * species: "Drosera capensis" + * } + */ +export interface CreatePlantWithSchemaRequest { + name?: string; + species?: string; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/api/client/requests/UpdatePlantRequest.ts b/seed/ts-sdk/schemaless-request-body-examples/src/api/client/requests/UpdatePlantRequest.ts new file mode 100644 index 000000000000..37ca954504f2 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/api/client/requests/UpdatePlantRequest.ts @@ -0,0 +1,18 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * plantId: "plantId", + * body: { + * "name": "Updated Venus Flytrap", + * "care": { + * "light": "partial shade" + * } + * } + * } + */ +export interface UpdatePlantRequest { + plantId: string; + body?: unknown; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/api/client/requests/index.ts b/seed/ts-sdk/schemaless-request-body-examples/src/api/client/requests/index.ts new file mode 100644 index 000000000000..b0144faca5c6 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/api/client/requests/index.ts @@ -0,0 +1,2 @@ +export type { CreatePlantWithSchemaRequest } from "./CreatePlantWithSchemaRequest.js"; +export type { UpdatePlantRequest } from "./UpdatePlantRequest.js"; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/api/index.ts b/seed/ts-sdk/schemaless-request-body-examples/src/api/index.ts new file mode 100644 index 000000000000..d9adb1af9a93 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/api/index.ts @@ -0,0 +1,2 @@ +export * from "./client/index.js"; +export * from "./types/index.js"; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/api/types/CreatePlantResponse.ts b/seed/ts-sdk/schemaless-request-body-examples/src/api/types/CreatePlantResponse.ts new file mode 100644 index 000000000000..dcdaccb4e75a --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/api/types/CreatePlantResponse.ts @@ -0,0 +1,6 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface CreatePlantResponse { + id?: string | undefined; + name?: string | undefined; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/api/types/CreatePlantWithSchemaResponse.ts b/seed/ts-sdk/schemaless-request-body-examples/src/api/types/CreatePlantWithSchemaResponse.ts new file mode 100644 index 000000000000..e7cacb390bfc --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/api/types/CreatePlantWithSchemaResponse.ts @@ -0,0 +1,6 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface CreatePlantWithSchemaResponse { + id?: string | undefined; + name?: string | undefined; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/api/types/UpdatePlantResponse.ts b/seed/ts-sdk/schemaless-request-body-examples/src/api/types/UpdatePlantResponse.ts new file mode 100644 index 000000000000..95fb01b8a521 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/api/types/UpdatePlantResponse.ts @@ -0,0 +1,6 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface UpdatePlantResponse { + id?: string | undefined; + name?: string | undefined; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/api/types/index.ts b/seed/ts-sdk/schemaless-request-body-examples/src/api/types/index.ts new file mode 100644 index 000000000000..b887788343c0 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/api/types/index.ts @@ -0,0 +1,3 @@ +export * from "./CreatePlantResponse.js"; +export * from "./CreatePlantWithSchemaResponse.js"; +export * from "./UpdatePlantResponse.js"; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/exports.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/exports.ts new file mode 100644 index 000000000000..69296d7100d6 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/exports.ts @@ -0,0 +1 @@ +export * from "./logging/exports.js"; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/APIResponse.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/APIResponse.ts new file mode 100644 index 000000000000..97ab83c2b195 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/APIResponse.ts @@ -0,0 +1,23 @@ +import type { RawResponse } from "./RawResponse.js"; + +/** + * The response of an API call. + * It is a successful response or a failed response. + */ +export type APIResponse = SuccessfulResponse | FailedResponse; + +export interface SuccessfulResponse { + ok: true; + body: T; + /** + * @deprecated Use `rawResponse` instead + */ + headers?: Record; + rawResponse: RawResponse; +} + +export interface FailedResponse { + ok: false; + error: T; + rawResponse: RawResponse; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/BinaryResponse.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/BinaryResponse.ts new file mode 100644 index 000000000000..b9e40fb62cc4 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/BinaryResponse.ts @@ -0,0 +1,34 @@ +export type BinaryResponse = { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bodyUsed) */ + bodyUsed: Response["bodyUsed"]; + /** + * Returns a ReadableStream of the response body. + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/body) + */ + stream: () => Response["body"]; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/arrayBuffer) */ + arrayBuffer: () => ReturnType; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/blob) */ + blob: () => ReturnType; + /** + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bytes) + * Some versions of the Fetch API may not support this method. + */ + bytes?(): Promise; +}; + +export function getBinaryResponse(response: Response): BinaryResponse { + const binaryResponse: BinaryResponse = { + get bodyUsed() { + return response.bodyUsed; + }, + stream: () => response.body, + arrayBuffer: response.arrayBuffer.bind(response), + blob: response.blob.bind(response), + }; + if ("bytes" in response && typeof response.bytes === "function") { + binaryResponse.bytes = response.bytes.bind(response); + } + + return binaryResponse; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/EndpointMetadata.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/EndpointMetadata.ts new file mode 100644 index 000000000000..998d68f5c20c --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/EndpointMetadata.ts @@ -0,0 +1,13 @@ +export type SecuritySchemeKey = string; +/** + * A collection of security schemes, where the key is the name of the security scheme and the value is the list of scopes required for that scheme. + * All schemes in the collection must be satisfied for authentication to be successful. + */ +export type SecuritySchemeCollection = Record; +export type AuthScope = string; +export type EndpointMetadata = { + /** + * An array of security scheme collections. Each collection represents an alternative way to authenticate. + */ + security?: SecuritySchemeCollection[]; +}; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/EndpointSupplier.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/EndpointSupplier.ts new file mode 100644 index 000000000000..aad81f0d9040 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/EndpointSupplier.ts @@ -0,0 +1,14 @@ +import type { EndpointMetadata } from "./EndpointMetadata.js"; +import type { Supplier } from "./Supplier.js"; + +type EndpointSupplierFn = (arg: { endpointMetadata?: EndpointMetadata }) => T | Promise; +export type EndpointSupplier = Supplier | EndpointSupplierFn; +export const EndpointSupplier = { + get: async (supplier: EndpointSupplier, arg: { endpointMetadata?: EndpointMetadata }): Promise => { + if (typeof supplier === "function") { + return (supplier as EndpointSupplierFn)(arg); + } else { + return supplier; + } + }, +}; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/Fetcher.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/Fetcher.ts new file mode 100644 index 000000000000..928dfeaabae6 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/Fetcher.ts @@ -0,0 +1,404 @@ +import { toJson } from "../json.js"; +import { createLogger, type LogConfig, type Logger } from "../logging/logger.js"; +import type { APIResponse } from "./APIResponse.js"; +import { createRequestUrl } from "./createRequestUrl.js"; +import type { EndpointMetadata } from "./EndpointMetadata.js"; +import { EndpointSupplier } from "./EndpointSupplier.js"; +import { getErrorResponseBody } from "./getErrorResponseBody.js"; +import { getFetchFn } from "./getFetchFn.js"; +import { getRequestBody } from "./getRequestBody.js"; +import { getResponseBody } from "./getResponseBody.js"; +import { Headers } from "./Headers.js"; +import { makeRequest } from "./makeRequest.js"; +import { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse.js"; +import { requestWithRetries } from "./requestWithRetries.js"; + +export type FetchFunction = (args: Fetcher.Args) => Promise>; + +export declare namespace Fetcher { + export interface Args { + url: string; + method: string; + contentType?: string; + headers?: Record; + queryParameters?: Record; + body?: unknown; + timeoutMs?: number; + maxRetries?: number; + withCredentials?: boolean; + abortSignal?: AbortSignal; + requestType?: "json" | "file" | "bytes" | "form" | "other"; + responseType?: "json" | "blob" | "sse" | "streaming" | "text" | "arrayBuffer" | "binary-response"; + duplex?: "half"; + endpointMetadata?: EndpointMetadata; + fetchFn?: typeof fetch; + logging?: LogConfig | Logger; + } + + export type Error = FailedStatusCodeError | NonJsonError | BodyIsNullError | TimeoutError | UnknownError; + + export interface FailedStatusCodeError { + reason: "status-code"; + statusCode: number; + body: unknown; + } + + export interface NonJsonError { + reason: "non-json"; + statusCode: number; + rawBody: string; + } + + export interface BodyIsNullError { + reason: "body-is-null"; + statusCode: number; + } + + export interface TimeoutError { + reason: "timeout"; + cause?: unknown; + } + + export interface UnknownError { + reason: "unknown"; + errorMessage: string; + cause?: unknown; + } +} + +const SENSITIVE_HEADERS = new Set([ + "authorization", + "www-authenticate", + "x-api-key", + "api-key", + "apikey", + "x-api-token", + "x-auth-token", + "auth-token", + "cookie", + "set-cookie", + "proxy-authorization", + "proxy-authenticate", + "x-csrf-token", + "x-xsrf-token", + "x-session-token", + "x-access-token", +]); + +function redactHeaders(headers: Headers | Record): Record { + const filtered: Record = {}; + for (const [key, value] of headers instanceof Headers ? headers.entries() : Object.entries(headers)) { + if (SENSITIVE_HEADERS.has(key.toLowerCase())) { + filtered[key] = "[REDACTED]"; + } else { + filtered[key] = value; + } + } + return filtered; +} + +const SENSITIVE_QUERY_PARAMS = new Set([ + "api_key", + "api-key", + "apikey", + "token", + "access_token", + "access-token", + "auth_token", + "auth-token", + "password", + "passwd", + "secret", + "api_secret", + "api-secret", + "apisecret", + "key", + "session", + "session_id", + "session-id", +]); + +function redactQueryParameters(queryParameters?: Record): Record | undefined { + if (queryParameters == null) { + return queryParameters; + } + const redacted: Record = {}; + for (const [key, value] of Object.entries(queryParameters)) { + if (SENSITIVE_QUERY_PARAMS.has(key.toLowerCase())) { + redacted[key] = "[REDACTED]"; + } else { + redacted[key] = value; + } + } + return redacted; +} + +function redactUrl(url: string): string { + const protocolIndex = url.indexOf("://"); + if (protocolIndex === -1) return url; + + const afterProtocol = protocolIndex + 3; + + // Find the first delimiter that marks the end of the authority section + const pathStart = url.indexOf("/", afterProtocol); + let queryStart = url.indexOf("?", afterProtocol); + let fragmentStart = url.indexOf("#", afterProtocol); + + const firstDelimiter = Math.min( + pathStart === -1 ? url.length : pathStart, + queryStart === -1 ? url.length : queryStart, + fragmentStart === -1 ? url.length : fragmentStart, + ); + + // Find the LAST @ before the delimiter (handles multiple @ in credentials) + let atIndex = -1; + for (let i = afterProtocol; i < firstDelimiter; i++) { + if (url[i] === "@") { + atIndex = i; + } + } + + if (atIndex !== -1) { + url = `${url.slice(0, afterProtocol)}[REDACTED]@${url.slice(atIndex + 1)}`; + } + + // Recalculate queryStart since url might have changed + queryStart = url.indexOf("?"); + if (queryStart === -1) return url; + + fragmentStart = url.indexOf("#", queryStart); + const queryEnd = fragmentStart !== -1 ? fragmentStart : url.length; + const queryString = url.slice(queryStart + 1, queryEnd); + + if (queryString.length === 0) return url; + + // FAST PATH: Quick check if any sensitive keywords present + // Using indexOf is faster than regex for simple substring matching + const lower = queryString.toLowerCase(); + const hasSensitive = + lower.includes("token") || + lower.includes("key") || + lower.includes("password") || + lower.includes("passwd") || + lower.includes("secret") || + lower.includes("session") || + lower.includes("auth"); + + if (!hasSensitive) { + return url; + } + + // SLOW PATH: Parse and redact + const redactedParams: string[] = []; + const params = queryString.split("&"); + + for (const param of params) { + const equalIndex = param.indexOf("="); + if (equalIndex === -1) { + redactedParams.push(param); + continue; + } + + const key = param.slice(0, equalIndex); + let shouldRedact = SENSITIVE_QUERY_PARAMS.has(key.toLowerCase()); + + if (!shouldRedact && key.includes("%")) { + try { + const decodedKey = decodeURIComponent(key); + shouldRedact = SENSITIVE_QUERY_PARAMS.has(decodedKey.toLowerCase()); + } catch {} + } + + redactedParams.push(shouldRedact ? `${key}=[REDACTED]` : param); + } + + return url.slice(0, queryStart + 1) + redactedParams.join("&") + url.slice(queryEnd); +} + +async function getHeaders(args: Fetcher.Args): Promise { + const newHeaders: Headers = new Headers(); + + newHeaders.set( + "Accept", + args.responseType === "json" + ? "application/json" + : args.responseType === "text" + ? "text/plain" + : args.responseType === "sse" + ? "text/event-stream" + : "*/*", + ); + if (args.body !== undefined && args.contentType != null) { + newHeaders.set("Content-Type", args.contentType); + } + + if (args.headers == null) { + return newHeaders; + } + + for (const [key, value] of Object.entries(args.headers)) { + const result = await EndpointSupplier.get(value, { endpointMetadata: args.endpointMetadata ?? {} }); + if (typeof result === "string") { + newHeaders.set(key, result); + continue; + } + if (result == null) { + continue; + } + newHeaders.set(key, `${result}`); + } + return newHeaders; +} + +export async function fetcherImpl(args: Fetcher.Args): Promise> { + const url = createRequestUrl(args.url, args.queryParameters); + const requestBody: BodyInit | undefined = await getRequestBody({ + body: args.body, + type: args.requestType ?? "other", + }); + const fetchFn = args.fetchFn ?? (await getFetchFn()); + const headers = await getHeaders(args); + const logger = createLogger(args.logging); + + if (logger.isDebug()) { + const metadata = { + method: args.method, + url: redactUrl(url), + headers: redactHeaders(headers), + queryParameters: redactQueryParameters(args.queryParameters), + hasBody: requestBody != null, + }; + logger.debug("Making HTTP request", metadata); + } + + try { + const response = await requestWithRetries( + async () => + makeRequest( + fetchFn, + url, + args.method, + headers, + requestBody, + args.timeoutMs, + args.abortSignal, + args.withCredentials, + args.duplex, + args.responseType === "streaming" || args.responseType === "sse", + ), + args.maxRetries, + ); + + if (response.status >= 200 && response.status < 400) { + if (logger.isDebug()) { + const metadata = { + method: args.method, + url: redactUrl(url), + statusCode: response.status, + responseHeaders: redactHeaders(response.headers), + }; + logger.debug("HTTP request succeeded", metadata); + } + const body = await getResponseBody(response, args.responseType); + return { + ok: true, + body: body as R, + headers: response.headers, + rawResponse: toRawResponse(response), + }; + } else { + if (logger.isError()) { + const metadata = { + method: args.method, + url: redactUrl(url), + statusCode: response.status, + responseHeaders: redactHeaders(Object.fromEntries(response.headers.entries())), + }; + logger.error("HTTP request failed with error status", metadata); + } + return { + ok: false, + error: { + reason: "status-code", + statusCode: response.status, + body: await getErrorResponseBody(response), + }, + rawResponse: toRawResponse(response), + }; + } + } catch (error) { + if (args.abortSignal?.aborted) { + if (logger.isError()) { + const metadata = { + method: args.method, + url: redactUrl(url), + }; + logger.error("HTTP request was aborted", metadata); + } + return { + ok: false, + error: { + reason: "unknown", + errorMessage: "The user aborted a request", + cause: error, + }, + rawResponse: abortRawResponse, + }; + } else if (error instanceof Error && error.name === "AbortError") { + if (logger.isError()) { + const metadata = { + method: args.method, + url: redactUrl(url), + timeoutMs: args.timeoutMs, + }; + logger.error("HTTP request timed out", metadata); + } + return { + ok: false, + error: { + reason: "timeout", + cause: error, + }, + rawResponse: abortRawResponse, + }; + } else if (error instanceof Error) { + if (logger.isError()) { + const metadata = { + method: args.method, + url: redactUrl(url), + errorMessage: error.message, + }; + logger.error("HTTP request failed with error", metadata); + } + return { + ok: false, + error: { + reason: "unknown", + errorMessage: error.message, + cause: error, + }, + rawResponse: unknownRawResponse, + }; + } + + if (logger.isError()) { + const metadata = { + method: args.method, + url: redactUrl(url), + error: toJson(error), + }; + logger.error("HTTP request failed with unknown error", metadata); + } + return { + ok: false, + error: { + reason: "unknown", + errorMessage: toJson(error), + cause: error, + }, + rawResponse: unknownRawResponse, + }; + } +} + +export const fetcher: FetchFunction = fetcherImpl; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/Headers.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/Headers.ts new file mode 100644 index 000000000000..af841aa24f55 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/Headers.ts @@ -0,0 +1,93 @@ +let Headers: typeof globalThis.Headers; + +if (typeof globalThis.Headers !== "undefined") { + Headers = globalThis.Headers; +} else { + Headers = class Headers implements Headers { + private headers: Map; + + constructor(init?: HeadersInit) { + this.headers = new Map(); + + if (init) { + if (init instanceof Headers) { + init.forEach((value, key) => this.append(key, value)); + } else if (Array.isArray(init)) { + for (const [key, value] of init) { + if (typeof key === "string" && typeof value === "string") { + this.append(key, value); + } else { + throw new TypeError("Each header entry must be a [string, string] tuple"); + } + } + } else { + for (const [key, value] of Object.entries(init)) { + if (typeof value === "string") { + this.append(key, value); + } else { + throw new TypeError("Header values must be strings"); + } + } + } + } + } + + append(name: string, value: string): void { + const key = name.toLowerCase(); + const existing = this.headers.get(key) || []; + this.headers.set(key, [...existing, value]); + } + + delete(name: string): void { + const key = name.toLowerCase(); + this.headers.delete(key); + } + + get(name: string): string | null { + const key = name.toLowerCase(); + const values = this.headers.get(key); + return values ? values.join(", ") : null; + } + + has(name: string): boolean { + const key = name.toLowerCase(); + return this.headers.has(key); + } + + set(name: string, value: string): void { + const key = name.toLowerCase(); + this.headers.set(key, [value]); + } + + forEach(callbackfn: (value: string, key: string, parent: Headers) => void, thisArg?: unknown): void { + const boundCallback = thisArg ? callbackfn.bind(thisArg) : callbackfn; + this.headers.forEach((values, key) => boundCallback(values.join(", "), key, this)); + } + + getSetCookie(): string[] { + return this.headers.get("set-cookie") || []; + } + + *entries(): HeadersIterator<[string, string]> { + for (const [key, values] of this.headers.entries()) { + yield [key, values.join(", ")]; + } + } + + *keys(): HeadersIterator { + yield* this.headers.keys(); + } + + *values(): HeadersIterator { + for (const values of this.headers.values()) { + yield values.join(", "); + } + } + + [Symbol.iterator](): HeadersIterator<[string, string]> { + return this.entries(); + } + }; +} + +export { Headers }; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/HttpResponsePromise.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/HttpResponsePromise.ts new file mode 100644 index 000000000000..692ca7d795f0 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/HttpResponsePromise.ts @@ -0,0 +1,116 @@ +import type { WithRawResponse } from "./RawResponse.js"; + +/** + * A promise that returns the parsed response and lets you retrieve the raw response too. + */ +export class HttpResponsePromise extends Promise { + private innerPromise: Promise>; + private unwrappedPromise: Promise | undefined; + + private constructor(promise: Promise>) { + // Initialize with a no-op to avoid premature parsing + super((resolve) => { + resolve(undefined as unknown as T); + }); + this.innerPromise = promise; + } + + /** + * Creates an `HttpResponsePromise` from a function that returns a promise. + * + * @param fn - A function that returns a promise resolving to a `WithRawResponse` object. + * @param args - Arguments to pass to the function. + * @returns An `HttpResponsePromise` instance. + */ + public static fromFunction Promise>, T>( + fn: F, + ...args: Parameters + ): HttpResponsePromise { + return new HttpResponsePromise(fn(...args)); + } + + /** + * Creates a function that returns an `HttpResponsePromise` from a function that returns a promise. + * + * @param fn - A function that returns a promise resolving to a `WithRawResponse` object. + * @returns A function that returns an `HttpResponsePromise` instance. + */ + public static interceptFunction< + F extends (...args: never[]) => Promise>, + T = Awaited>["data"], + >(fn: F): (...args: Parameters) => HttpResponsePromise { + return (...args: Parameters): HttpResponsePromise => { + return HttpResponsePromise.fromPromise(fn(...args)); + }; + } + + /** + * Creates an `HttpResponsePromise` from an existing promise. + * + * @param promise - A promise resolving to a `WithRawResponse` object. + * @returns An `HttpResponsePromise` instance. + */ + public static fromPromise(promise: Promise>): HttpResponsePromise { + return new HttpResponsePromise(promise); + } + + /** + * Creates an `HttpResponsePromise` from an executor function. + * + * @param executor - A function that takes resolve and reject callbacks to create a promise. + * @returns An `HttpResponsePromise` instance. + */ + public static fromExecutor( + executor: (resolve: (value: WithRawResponse) => void, reject: (reason?: unknown) => void) => void, + ): HttpResponsePromise { + const promise = new Promise>(executor); + return new HttpResponsePromise(promise); + } + + /** + * Creates an `HttpResponsePromise` from a resolved result. + * + * @param result - A `WithRawResponse` object to resolve immediately. + * @returns An `HttpResponsePromise` instance. + */ + public static fromResult(result: WithRawResponse): HttpResponsePromise { + const promise = Promise.resolve(result); + return new HttpResponsePromise(promise); + } + + private unwrap(): Promise { + if (!this.unwrappedPromise) { + this.unwrappedPromise = this.innerPromise.then(({ data }) => data); + } + return this.unwrappedPromise; + } + + /** @inheritdoc */ + public override then( + onfulfilled?: ((value: T) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null, + ): Promise { + return this.unwrap().then(onfulfilled, onrejected); + } + + /** @inheritdoc */ + public override catch( + onrejected?: ((reason: unknown) => TResult | PromiseLike) | null, + ): Promise { + return this.unwrap().catch(onrejected); + } + + /** @inheritdoc */ + public override finally(onfinally?: (() => void) | null): Promise { + return this.unwrap().finally(onfinally); + } + + /** + * Retrieves the data and raw response. + * + * @returns A promise resolving to a `WithRawResponse` object. + */ + public async withRawResponse(): Promise> { + return await this.innerPromise; + } +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/RawResponse.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/RawResponse.ts new file mode 100644 index 000000000000..37fb44e2aa99 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/RawResponse.ts @@ -0,0 +1,61 @@ +import { Headers } from "./Headers.js"; + +/** + * The raw response from the fetch call excluding the body. + */ +export type RawResponse = Omit< + { + [K in keyof Response as Response[K] extends Function ? never : K]: Response[K]; // strips out functions + }, + "ok" | "body" | "bodyUsed" +>; // strips out body and bodyUsed + +/** + * A raw response indicating that the request was aborted. + */ +export const abortRawResponse: RawResponse = { + headers: new Headers(), + redirected: false, + status: 499, + statusText: "Client Closed Request", + type: "error", + url: "", +} as const; + +/** + * A raw response indicating an unknown error. + */ +export const unknownRawResponse: RawResponse = { + headers: new Headers(), + redirected: false, + status: 0, + statusText: "Unknown Error", + type: "error", + url: "", +} as const; + +/** + * Converts a `RawResponse` object into a `RawResponse` by extracting its properties, + * excluding the `body` and `bodyUsed` fields. + * + * @param response - The `RawResponse` object to convert. + * @returns A `RawResponse` object containing the extracted properties of the input response. + */ +export function toRawResponse(response: Response): RawResponse { + return { + headers: response.headers, + redirected: response.redirected, + status: response.status, + statusText: response.statusText, + type: response.type, + url: response.url, + }; +} + +/** + * Creates a `RawResponse` from a standard `Response` object. + */ +export interface WithRawResponse { + readonly data: T; + readonly rawResponse: RawResponse; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/Supplier.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/Supplier.ts new file mode 100644 index 000000000000..867c931c02f4 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/Supplier.ts @@ -0,0 +1,11 @@ +export type Supplier = T | Promise | (() => T | Promise); + +export const Supplier = { + get: async (supplier: Supplier): Promise => { + if (typeof supplier === "function") { + return (supplier as () => T)(); + } else { + return supplier; + } + }, +}; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/createRequestUrl.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/createRequestUrl.ts new file mode 100644 index 000000000000..88e13265e112 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/createRequestUrl.ts @@ -0,0 +1,6 @@ +import { toQueryString } from "../url/qs.js"; + +export function createRequestUrl(baseUrl: string, queryParameters?: Record): string { + const queryString = toQueryString(queryParameters, { arrayFormat: "repeat" }); + return queryString ? `${baseUrl}?${queryString}` : baseUrl; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getErrorResponseBody.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getErrorResponseBody.ts new file mode 100644 index 000000000000..7cf4e623c2f5 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getErrorResponseBody.ts @@ -0,0 +1,33 @@ +import { fromJson } from "../json.js"; +import { getResponseBody } from "./getResponseBody.js"; + +export async function getErrorResponseBody(response: Response): Promise { + let contentType = response.headers.get("Content-Type")?.toLowerCase(); + if (contentType == null || contentType.length === 0) { + return getResponseBody(response); + } + + if (contentType.indexOf(";") !== -1) { + contentType = contentType.split(";")[0]?.trim() ?? ""; + } + switch (contentType) { + case "application/hal+json": + case "application/json": + case "application/ld+json": + case "application/problem+json": + case "application/vnd.api+json": + case "text/json": { + const text = await response.text(); + return text.length > 0 ? fromJson(text) : undefined; + } + default: + if (contentType.startsWith("application/vnd.") && contentType.endsWith("+json")) { + const text = await response.text(); + return text.length > 0 ? fromJson(text) : undefined; + } + + // Fallback to plain text if content type is not recognized + // Even if no body is present, the response will be an empty string + return await response.text(); + } +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getFetchFn.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getFetchFn.ts new file mode 100644 index 000000000000..9f845b956392 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getFetchFn.ts @@ -0,0 +1,3 @@ +export async function getFetchFn(): Promise { + return fetch; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getHeader.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getHeader.ts new file mode 100644 index 000000000000..50f922b0e87f --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getHeader.ts @@ -0,0 +1,8 @@ +export function getHeader(headers: Record, header: string): string | undefined { + for (const [headerKey, headerValue] of Object.entries(headers)) { + if (headerKey.toLowerCase() === header.toLowerCase()) { + return headerValue; + } + } + return undefined; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getRequestBody.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getRequestBody.ts new file mode 100644 index 000000000000..91d9d81f50e5 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getRequestBody.ts @@ -0,0 +1,20 @@ +import { toJson } from "../json.js"; +import { toQueryString } from "../url/qs.js"; + +export declare namespace GetRequestBody { + interface Args { + body: unknown; + type: "json" | "file" | "bytes" | "form" | "other"; + } +} + +export async function getRequestBody({ body, type }: GetRequestBody.Args): Promise { + if (type === "form") { + return toQueryString(body, { arrayFormat: "repeat", encode: true }); + } + if (type.includes("json")) { + return toJson(body); + } else { + return body as BodyInit; + } +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getResponseBody.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getResponseBody.ts new file mode 100644 index 000000000000..708d55728f2b --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/getResponseBody.ts @@ -0,0 +1,58 @@ +import { fromJson } from "../json.js"; +import { getBinaryResponse } from "./BinaryResponse.js"; + +export async function getResponseBody(response: Response, responseType?: string): Promise { + switch (responseType) { + case "binary-response": + return getBinaryResponse(response); + case "blob": + return await response.blob(); + case "arrayBuffer": + return await response.arrayBuffer(); + case "sse": + if (response.body == null) { + return { + ok: false, + error: { + reason: "body-is-null", + statusCode: response.status, + }, + }; + } + return response.body; + case "streaming": + if (response.body == null) { + return { + ok: false, + error: { + reason: "body-is-null", + statusCode: response.status, + }, + }; + } + + return response.body; + + case "text": + return await response.text(); + } + + // if responseType is "json" or not specified, try to parse as JSON + const text = await response.text(); + if (text.length > 0) { + try { + const responseBody = fromJson(text); + return responseBody; + } catch (_err) { + return { + ok: false, + error: { + reason: "non-json", + statusCode: response.status, + rawBody: text, + }, + }; + } + } + return undefined; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/index.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/index.ts new file mode 100644 index 000000000000..bd5db362c778 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/index.ts @@ -0,0 +1,13 @@ +export type { APIResponse } from "./APIResponse.js"; +export type { BinaryResponse } from "./BinaryResponse.js"; +export type { EndpointMetadata } from "./EndpointMetadata.js"; +export { EndpointSupplier } from "./EndpointSupplier.js"; +export type { Fetcher, FetchFunction } from "./Fetcher.js"; +export { fetcher } from "./Fetcher.js"; +export { getHeader } from "./getHeader.js"; +export { HttpResponsePromise } from "./HttpResponsePromise.js"; +export type { PassthroughRequest } from "./makePassthroughRequest.js"; +export { makePassthroughRequest } from "./makePassthroughRequest.js"; +export type { RawResponse, WithRawResponse } from "./RawResponse.js"; +export { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse.js"; +export { Supplier } from "./Supplier.js"; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/makePassthroughRequest.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/makePassthroughRequest.ts new file mode 100644 index 000000000000..f5ba761400f8 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/makePassthroughRequest.ts @@ -0,0 +1,189 @@ +import { createLogger, type LogConfig, type Logger } from "../logging/logger.js"; +import { join } from "../url/join.js"; +import { EndpointSupplier } from "./EndpointSupplier.js"; +import { getFetchFn } from "./getFetchFn.js"; +import { makeRequest } from "./makeRequest.js"; +import { requestWithRetries } from "./requestWithRetries.js"; +import { Supplier } from "./Supplier.js"; + +export declare namespace PassthroughRequest { + /** + * Per-request options that can override the SDK client defaults. + */ + export interface RequestOptions { + /** Override the default timeout for this request (in seconds). */ + timeoutInSeconds?: number; + /** Override the default number of retries for this request. */ + maxRetries?: number; + /** Additional headers to include in this request. */ + headers?: Record; + /** Abort signal for this request. */ + abortSignal?: AbortSignal; + } + + /** + * SDK client configuration used by the passthrough fetch method. + */ + export interface ClientOptions { + /** The base URL or environment for the client. */ + environment?: Supplier; + /** Override the base URL. */ + baseUrl?: Supplier; + /** Default headers to include in requests. */ + headers?: Record; + /** Default maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** Default number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A custom fetch function. */ + fetch?: typeof fetch; + /** Logging configuration. */ + logging?: LogConfig | Logger; + /** A function that returns auth headers. */ + getAuthHeaders?: () => Promise>; + } +} + +/** + * Makes a passthrough HTTP request using the SDK's configuration (auth, retry, logging, etc.) + * while mimicking the standard `fetch` API. + * + * @param input - The URL, path, or Request object. If a relative path, it will be resolved against the configured base URL. + * @param init - Standard RequestInit options (method, headers, body, signal, etc.) + * @param clientOptions - SDK client options (auth, default headers, logging, etc.) + * @param requestOptions - Per-request overrides (timeout, retries, extra headers, abort signal). + * @returns A standard Response object. + */ +export async function makePassthroughRequest( + input: Request | string | URL, + init: RequestInit | undefined, + clientOptions: PassthroughRequest.ClientOptions, + requestOptions?: PassthroughRequest.RequestOptions, +): Promise { + const logger = createLogger(clientOptions.logging); + + // Extract URL and default init properties from Request object if provided + let url: string; + let effectiveInit: RequestInit | undefined = init; + if (input instanceof Request) { + url = input.url; + // If no explicit init provided, extract properties from the Request object + if (init == null) { + effectiveInit = { + method: input.method, + headers: Object.fromEntries(input.headers.entries()), + body: input.body, + signal: input.signal, + credentials: input.credentials, + cache: input.cache as RequestCache, + redirect: input.redirect, + referrer: input.referrer, + integrity: input.integrity, + mode: input.mode, + }; + } + } else { + url = input instanceof URL ? input.toString() : input; + } + + // Resolve the base URL + const baseUrl = + (clientOptions.baseUrl != null ? await Supplier.get(clientOptions.baseUrl) : undefined) ?? + (clientOptions.environment != null ? await Supplier.get(clientOptions.environment) : undefined); + + // Determine the full URL + let fullUrl: string; + if (url.startsWith("http://") || url.startsWith("https://")) { + fullUrl = url; + } else if (baseUrl != null) { + fullUrl = join(baseUrl, url); + } else { + fullUrl = url; + } + + // Merge headers: SDK default headers -> auth headers -> user-provided headers + const mergedHeaders: Record = {}; + + // Apply SDK default headers (resolve suppliers) + if (clientOptions.headers != null) { + for (const [key, value] of Object.entries(clientOptions.headers)) { + const resolved = await EndpointSupplier.get(value, { endpointMetadata: {} }); + if (resolved != null) { + mergedHeaders[key.toLowerCase()] = `${resolved}`; + } + } + } + + // Apply auth headers + if (clientOptions.getAuthHeaders != null) { + const authHeaders = await clientOptions.getAuthHeaders(); + for (const [key, value] of Object.entries(authHeaders)) { + mergedHeaders[key.toLowerCase()] = value; + } + } + + // Apply user-provided headers from init + if (effectiveInit?.headers != null) { + const initHeaders = + effectiveInit.headers instanceof Headers + ? Object.fromEntries(effectiveInit.headers.entries()) + : Array.isArray(effectiveInit.headers) + ? Object.fromEntries(effectiveInit.headers) + : effectiveInit.headers; + for (const [key, value] of Object.entries(initHeaders)) { + if (value != null) { + mergedHeaders[key.toLowerCase()] = value; + } + } + } + + // Apply per-request option headers (highest priority) + if (requestOptions?.headers != null) { + for (const [key, value] of Object.entries(requestOptions.headers)) { + mergedHeaders[key.toLowerCase()] = value; + } + } + + const method = effectiveInit?.method ?? "GET"; + const body = effectiveInit?.body; + const timeoutInSeconds = requestOptions?.timeoutInSeconds ?? clientOptions.timeoutInSeconds; + const timeoutMs = timeoutInSeconds != null ? timeoutInSeconds * 1000 : undefined; + const maxRetries = requestOptions?.maxRetries ?? clientOptions.maxRetries; + const abortSignal = requestOptions?.abortSignal ?? effectiveInit?.signal ?? undefined; + const fetchFn = clientOptions.fetch ?? (await getFetchFn()); + + if (logger.isDebug()) { + logger.debug("Making passthrough HTTP request", { + method, + url: fullUrl, + hasBody: body != null, + }); + } + + const response = await requestWithRetries( + async () => + makeRequest( + fetchFn, + fullUrl, + method, + mergedHeaders, + body ?? undefined, + timeoutMs, + abortSignal, + effectiveInit?.credentials === "include", + undefined, // duplex + false, // disableCache + ), + maxRetries, + ); + + if (logger.isDebug()) { + logger.debug("Passthrough HTTP request completed", { + method, + url: fullUrl, + statusCode: response.status, + }); + } + + return response; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/makeRequest.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/makeRequest.ts new file mode 100644 index 000000000000..360a86df40ad --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/makeRequest.ts @@ -0,0 +1,70 @@ +import { anySignal, getTimeoutSignal } from "./signals.js"; + +/** + * Cached result of checking whether the current runtime supports + * the `cache` option in `Request`. Some runtimes (e.g. Cloudflare Workers) + * throw a TypeError when this option is used. + */ +let _cacheNoStoreSupported: boolean | undefined; +export function isCacheNoStoreSupported(): boolean { + if (_cacheNoStoreSupported != null) { + return _cacheNoStoreSupported; + } + try { + new Request("http://localhost", { cache: "no-store" }); + _cacheNoStoreSupported = true; + } catch { + _cacheNoStoreSupported = false; + } + return _cacheNoStoreSupported; +} + +/** + * Reset the cached result of `isCacheNoStoreSupported`. Exposed for testing only. + */ +export function resetCacheNoStoreSupported(): void { + _cacheNoStoreSupported = undefined; +} + +export const makeRequest = async ( + fetchFn: (url: string, init: RequestInit) => Promise, + url: string, + method: string, + headers: Headers | Record, + requestBody: BodyInit | undefined, + timeoutMs?: number, + abortSignal?: AbortSignal, + withCredentials?: boolean, + duplex?: "half", + disableCache?: boolean, +): Promise => { + const signals: AbortSignal[] = []; + + let timeoutAbortId: ReturnType | undefined; + if (timeoutMs != null) { + const { signal, abortId } = getTimeoutSignal(timeoutMs); + timeoutAbortId = abortId; + signals.push(signal); + } + + if (abortSignal != null) { + signals.push(abortSignal); + } + const newSignals = anySignal(signals); + const response = await fetchFn(url, { + method: method, + headers, + body: requestBody, + signal: newSignals, + credentials: withCredentials ? "include" : undefined, + // @ts-ignore + duplex, + ...(disableCache && isCacheNoStoreSupported() ? { cache: "no-store" as RequestCache } : {}), + }); + + if (timeoutAbortId != null) { + clearTimeout(timeoutAbortId); + } + + return response; +}; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/requestWithRetries.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/requestWithRetries.ts new file mode 100644 index 000000000000..1f689688c4b2 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/requestWithRetries.ts @@ -0,0 +1,64 @@ +const INITIAL_RETRY_DELAY = 1000; // in milliseconds +const MAX_RETRY_DELAY = 60000; // in milliseconds +const DEFAULT_MAX_RETRIES = 2; +const JITTER_FACTOR = 0.2; // 20% random jitter + +function addPositiveJitter(delay: number): number { + const jitterMultiplier = 1 + Math.random() * JITTER_FACTOR; + return delay * jitterMultiplier; +} + +function addSymmetricJitter(delay: number): number { + const jitterMultiplier = 1 + (Math.random() - 0.5) * JITTER_FACTOR; + return delay * jitterMultiplier; +} + +function getRetryDelayFromHeaders(response: Response, retryAttempt: number): number { + const retryAfter = response.headers.get("Retry-After"); + if (retryAfter) { + const retryAfterSeconds = parseInt(retryAfter, 10); + if (!Number.isNaN(retryAfterSeconds) && retryAfterSeconds > 0) { + return Math.min(retryAfterSeconds * 1000, MAX_RETRY_DELAY); + } + + const retryAfterDate = new Date(retryAfter); + if (!Number.isNaN(retryAfterDate.getTime())) { + const delay = retryAfterDate.getTime() - Date.now(); + if (delay > 0) { + return Math.min(Math.max(delay, 0), MAX_RETRY_DELAY); + } + } + } + + const rateLimitReset = response.headers.get("X-RateLimit-Reset"); + if (rateLimitReset) { + const resetTime = parseInt(rateLimitReset, 10); + if (!Number.isNaN(resetTime)) { + const delay = resetTime * 1000 - Date.now(); + if (delay > 0) { + return addPositiveJitter(Math.min(delay, MAX_RETRY_DELAY)); + } + } + } + + return addSymmetricJitter(Math.min(INITIAL_RETRY_DELAY * 2 ** retryAttempt, MAX_RETRY_DELAY)); +} + +export async function requestWithRetries( + requestFn: () => Promise, + maxRetries: number = DEFAULT_MAX_RETRIES, +): Promise { + let response: Response = await requestFn(); + + for (let i = 0; i < maxRetries; ++i) { + if ([408, 429].includes(response.status) || response.status >= 500) { + const delay = getRetryDelayFromHeaders(response, i); + + await new Promise((resolve) => setTimeout(resolve, delay)); + response = await requestFn(); + } else { + break; + } + } + return response!; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/signals.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/signals.ts new file mode 100644 index 000000000000..7bd3757ec3a7 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/fetcher/signals.ts @@ -0,0 +1,26 @@ +const TIMEOUT = "timeout"; + +export function getTimeoutSignal(timeoutMs: number): { signal: AbortSignal; abortId: ReturnType } { + const controller = new AbortController(); + const abortId = setTimeout(() => controller.abort(TIMEOUT), timeoutMs); + return { signal: controller.signal, abortId }; +} + +export function anySignal(...args: AbortSignal[] | [AbortSignal[]]): AbortSignal { + const signals = (args.length === 1 && Array.isArray(args[0]) ? args[0] : args) as AbortSignal[]; + + const controller = new AbortController(); + + for (const signal of signals) { + if (signal.aborted) { + controller.abort((signal as any)?.reason); + break; + } + + signal.addEventListener("abort", () => controller.abort((signal as any)?.reason), { + signal: controller.signal, + }); + } + + return controller.signal; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/headers.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/headers.ts new file mode 100644 index 000000000000..be45c4552a35 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/headers.ts @@ -0,0 +1,33 @@ +export function mergeHeaders(...headersArray: (Record | null | undefined)[]): Record { + const result: Record = {}; + + for (const [key, value] of headersArray + .filter((headers) => headers != null) + .flatMap((headers) => Object.entries(headers))) { + const insensitiveKey = key.toLowerCase(); + if (value != null) { + result[insensitiveKey] = value; + } else if (insensitiveKey in result) { + delete result[insensitiveKey]; + } + } + + return result; +} + +export function mergeOnlyDefinedHeaders( + ...headersArray: (Record | null | undefined)[] +): Record { + const result: Record = {}; + + for (const [key, value] of headersArray + .filter((headers) => headers != null) + .flatMap((headers) => Object.entries(headers))) { + const insensitiveKey = key.toLowerCase(); + if (value != null) { + result[insensitiveKey] = value; + } + } + + return result; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/index.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/index.ts new file mode 100644 index 000000000000..afa8351fcf85 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/index.ts @@ -0,0 +1,4 @@ +export * from "./fetcher/index.js"; +export * as logging from "./logging/index.js"; +export * from "./runtime/index.js"; +export * as url from "./url/index.js"; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/json.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/json.ts new file mode 100644 index 000000000000..c052f3249f4f --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/json.ts @@ -0,0 +1,27 @@ +/** + * Serialize a value to JSON + * @param value A JavaScript value, usually an object or array, to be converted. + * @param replacer A function that transforms the results. + * @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. + * @returns JSON string + */ +export const toJson = ( + value: unknown, + replacer?: (this: unknown, key: string, value: unknown) => unknown, + space?: string | number, +): string => { + return JSON.stringify(value, replacer, space); +}; + +/** + * Parse JSON string to object, array, or other type + * @param text A valid JSON string. + * @param reviver A function that transforms the results. This function is called for each member of the object. If a member contains nested objects, the nested objects are transformed before the parent object is. + * @returns Parsed object, array, or other type + */ +export function fromJson( + text: string, + reviver?: (this: unknown, key: string, value: unknown) => unknown, +): T { + return JSON.parse(text, reviver); +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/logging/exports.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/logging/exports.ts new file mode 100644 index 000000000000..88f6c00db0cf --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/logging/exports.ts @@ -0,0 +1,19 @@ +import * as logger from "./logger.js"; + +export namespace logging { + /** + * Configuration for logger instances. + */ + export type LogConfig = logger.LogConfig; + export type LogLevel = logger.LogLevel; + export const LogLevel: typeof logger.LogLevel = logger.LogLevel; + export type ILogger = logger.ILogger; + /** + * Console logger implementation that outputs to the console. + */ + export type ConsoleLogger = logger.ConsoleLogger; + /** + * Console logger implementation that outputs to the console. + */ + export const ConsoleLogger: typeof logger.ConsoleLogger = logger.ConsoleLogger; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/logging/index.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/logging/index.ts new file mode 100644 index 000000000000..d81cc32c40f9 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/logging/index.ts @@ -0,0 +1 @@ +export * from "./logger.js"; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/logging/logger.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/logging/logger.ts new file mode 100644 index 000000000000..a3f3673cda93 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/logging/logger.ts @@ -0,0 +1,203 @@ +export const LogLevel = { + Debug: "debug", + Info: "info", + Warn: "warn", + Error: "error", +} as const; +export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel]; +const logLevelMap: Record = { + [LogLevel.Debug]: 1, + [LogLevel.Info]: 2, + [LogLevel.Warn]: 3, + [LogLevel.Error]: 4, +}; + +export interface ILogger { + /** + * Logs a debug message. + * @param message - The message to log + * @param args - Additional arguments to log + */ + debug(message: string, ...args: unknown[]): void; + /** + * Logs an info message. + * @param message - The message to log + * @param args - Additional arguments to log + */ + info(message: string, ...args: unknown[]): void; + /** + * Logs a warning message. + * @param message - The message to log + * @param args - Additional arguments to log + */ + warn(message: string, ...args: unknown[]): void; + /** + * Logs an error message. + * @param message - The message to log + * @param args - Additional arguments to log + */ + error(message: string, ...args: unknown[]): void; +} + +/** + * Configuration for logger initialization. + */ +export interface LogConfig { + /** + * Minimum log level to output. + * @default LogLevel.Info + */ + level?: LogLevel; + /** + * Logger implementation to use. + * @default new ConsoleLogger() + */ + logger?: ILogger; + /** + * Whether logging should be silenced. + * @default true + */ + silent?: boolean; +} + +/** + * Default console-based logger implementation. + */ +export class ConsoleLogger implements ILogger { + debug(message: string, ...args: unknown[]): void { + console.debug(message, ...args); + } + info(message: string, ...args: unknown[]): void { + console.info(message, ...args); + } + warn(message: string, ...args: unknown[]): void { + console.warn(message, ...args); + } + error(message: string, ...args: unknown[]): void { + console.error(message, ...args); + } +} + +/** + * Logger class that provides level-based logging functionality. + */ +export class Logger { + private readonly level: number; + private readonly logger: ILogger; + private readonly silent: boolean; + + /** + * Creates a new logger instance. + * @param config - Logger configuration + */ + constructor(config: Required) { + this.level = logLevelMap[config.level]; + this.logger = config.logger; + this.silent = config.silent; + } + + /** + * Checks if a log level should be output based on configuration. + * @param level - The log level to check + * @returns True if the level should be logged + */ + public shouldLog(level: LogLevel): boolean { + return !this.silent && this.level <= logLevelMap[level]; + } + + /** + * Checks if debug logging is enabled. + * @returns True if debug logs should be output + */ + public isDebug(): boolean { + return this.shouldLog(LogLevel.Debug); + } + + /** + * Logs a debug message if debug logging is enabled. + * @param message - The message to log + * @param args - Additional arguments to log + */ + public debug(message: string, ...args: unknown[]): void { + if (this.isDebug()) { + this.logger.debug(message, ...args); + } + } + + /** + * Checks if info logging is enabled. + * @returns True if info logs should be output + */ + public isInfo(): boolean { + return this.shouldLog(LogLevel.Info); + } + + /** + * Logs an info message if info logging is enabled. + * @param message - The message to log + * @param args - Additional arguments to log + */ + public info(message: string, ...args: unknown[]): void { + if (this.isInfo()) { + this.logger.info(message, ...args); + } + } + + /** + * Checks if warning logging is enabled. + * @returns True if warning logs should be output + */ + public isWarn(): boolean { + return this.shouldLog(LogLevel.Warn); + } + + /** + * Logs a warning message if warning logging is enabled. + * @param message - The message to log + * @param args - Additional arguments to log + */ + public warn(message: string, ...args: unknown[]): void { + if (this.isWarn()) { + this.logger.warn(message, ...args); + } + } + + /** + * Checks if error logging is enabled. + * @returns True if error logs should be output + */ + public isError(): boolean { + return this.shouldLog(LogLevel.Error); + } + + /** + * Logs an error message if error logging is enabled. + * @param message - The message to log + * @param args - Additional arguments to log + */ + public error(message: string, ...args: unknown[]): void { + if (this.isError()) { + this.logger.error(message, ...args); + } + } +} + +export function createLogger(config?: LogConfig | Logger): Logger { + if (config == null) { + return defaultLogger; + } + if (config instanceof Logger) { + return config; + } + config = config ?? {}; + config.level ??= LogLevel.Info; + config.logger ??= new ConsoleLogger(); + config.silent ??= true; + return new Logger(config as Required); +} + +const defaultLogger: Logger = new Logger({ + level: LogLevel.Info, + logger: new ConsoleLogger(), + silent: true, +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/runtime/index.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/runtime/index.ts new file mode 100644 index 000000000000..cfab23f9a834 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/runtime/index.ts @@ -0,0 +1 @@ +export { RUNTIME } from "./runtime.js"; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/runtime/runtime.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/runtime/runtime.ts new file mode 100644 index 000000000000..e6e66b2a7bce --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/runtime/runtime.ts @@ -0,0 +1,134 @@ +interface DenoGlobal { + version: { + deno: string; + }; +} + +interface BunGlobal { + version: string; +} + +declare const Deno: DenoGlobal | undefined; +declare const Bun: BunGlobal | undefined; +declare const EdgeRuntime: string | undefined; +declare const self: typeof globalThis.self & { + importScripts?: unknown; +}; + +/** + * A constant that indicates which environment and version the SDK is running in. + */ +export const RUNTIME: Runtime = evaluateRuntime(); + +export interface Runtime { + type: "browser" | "web-worker" | "deno" | "bun" | "node" | "react-native" | "unknown" | "workerd" | "edge-runtime"; + version?: string; + parsedVersion?: number; +} + +function evaluateRuntime(): Runtime { + /** + * A constant that indicates whether the environment the code is running is a Web Browser. + */ + const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined"; + if (isBrowser) { + return { + type: "browser", + version: window.navigator.userAgent, + }; + } + + /** + * A constant that indicates whether the environment the code is running is Cloudflare. + * https://developers.cloudflare.com/workers/runtime-apis/web-standards/#navigatoruseragent + */ + const isCloudflare = typeof globalThis !== "undefined" && globalThis?.navigator?.userAgent === "Cloudflare-Workers"; + if (isCloudflare) { + return { + type: "workerd", + }; + } + + /** + * A constant that indicates whether the environment the code is running is Edge Runtime. + * https://vercel.com/docs/functions/runtimes/edge-runtime#check-if-you're-running-on-the-edge-runtime + */ + const isEdgeRuntime = typeof EdgeRuntime === "string"; + if (isEdgeRuntime) { + return { + type: "edge-runtime", + }; + } + + /** + * A constant that indicates whether the environment the code is running is a Web Worker. + */ + const isWebWorker = + typeof self === "object" && + typeof self?.importScripts === "function" && + (self.constructor?.name === "DedicatedWorkerGlobalScope" || + self.constructor?.name === "ServiceWorkerGlobalScope" || + self.constructor?.name === "SharedWorkerGlobalScope"); + if (isWebWorker) { + return { + type: "web-worker", + }; + } + + /** + * A constant that indicates whether the environment the code is running is Deno. + * FYI Deno spoofs process.versions.node, see https://deno.land/std@0.177.0/node/process.ts?s=versions + */ + const isDeno = + typeof Deno !== "undefined" && typeof Deno.version !== "undefined" && typeof Deno.version.deno !== "undefined"; + if (isDeno) { + return { + type: "deno", + version: Deno.version.deno, + }; + } + + /** + * A constant that indicates whether the environment the code is running is Bun.sh. + */ + const isBun = typeof Bun !== "undefined" && typeof Bun.version !== "undefined"; + if (isBun) { + return { + type: "bun", + version: Bun.version, + }; + } + + /** + * A constant that indicates whether the environment the code is running is in React-Native. + * This check should come before Node.js detection since React Native may have a process polyfill. + * https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Core/setUpNavigator.js + */ + const isReactNative = typeof navigator !== "undefined" && navigator?.product === "ReactNative"; + if (isReactNative) { + return { + type: "react-native", + }; + } + + /** + * A constant that indicates whether the environment the code is running is Node.JS. + * + * We assign `process` to a local variable first to avoid being flagged by + * bundlers that perform static analysis on `process.versions` (e.g. Next.js + * Edge Runtime warns about Node.js APIs even when they are guarded). + */ + const _process = typeof process !== "undefined" ? process : undefined; + const isNode = typeof _process !== "undefined" && typeof _process.versions?.node === "string"; + if (isNode) { + return { + type: "node", + version: _process.versions.node, + parsedVersion: Number(_process.versions.node.split(".")[0]), + }; + } + + return { + type: "unknown", + }; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/url/encodePathParam.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/url/encodePathParam.ts new file mode 100644 index 000000000000..19b901244218 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/url/encodePathParam.ts @@ -0,0 +1,18 @@ +export function encodePathParam(param: unknown): string { + if (param === null) { + return "null"; + } + const typeofParam = typeof param; + switch (typeofParam) { + case "undefined": + return "undefined"; + case "string": + case "number": + case "boolean": + break; + default: + param = String(param); + break; + } + return encodeURIComponent(param as string | number | boolean); +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/url/index.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/url/index.ts new file mode 100644 index 000000000000..f2e0fa2d2221 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/url/index.ts @@ -0,0 +1,3 @@ +export { encodePathParam } from "./encodePathParam.js"; +export { join } from "./join.js"; +export { toQueryString } from "./qs.js"; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/url/join.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/url/join.ts new file mode 100644 index 000000000000..7ca7daef094d --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/url/join.ts @@ -0,0 +1,79 @@ +export function join(base: string, ...segments: string[]): string { + if (!base) { + return ""; + } + + if (segments.length === 0) { + return base; + } + + if (base.includes("://")) { + let url: URL; + try { + url = new URL(base); + } catch { + return joinPath(base, ...segments); + } + + const lastSegment = segments[segments.length - 1]; + const shouldPreserveTrailingSlash = lastSegment?.endsWith("/"); + + for (const segment of segments) { + const cleanSegment = trimSlashes(segment); + if (cleanSegment) { + url.pathname = joinPathSegments(url.pathname, cleanSegment); + } + } + + if (shouldPreserveTrailingSlash && !url.pathname.endsWith("/")) { + url.pathname += "/"; + } + + return url.toString(); + } + + return joinPath(base, ...segments); +} + +function joinPath(base: string, ...segments: string[]): string { + if (segments.length === 0) { + return base; + } + + let result = base; + + const lastSegment = segments[segments.length - 1]; + const shouldPreserveTrailingSlash = lastSegment?.endsWith("/"); + + for (const segment of segments) { + const cleanSegment = trimSlashes(segment); + if (cleanSegment) { + result = joinPathSegments(result, cleanSegment); + } + } + + if (shouldPreserveTrailingSlash && !result.endsWith("/")) { + result += "/"; + } + + return result; +} + +function joinPathSegments(left: string, right: string): string { + if (left.endsWith("/")) { + return left + right; + } + return `${left}/${right}`; +} + +function trimSlashes(str: string): string { + if (!str) return str; + + let start = 0; + let end = str.length; + + if (str.startsWith("/")) start = 1; + if (str.endsWith("/")) end = str.length - 1; + + return start === 0 && end === str.length ? str : str.slice(start, end); +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/core/url/qs.ts b/seed/ts-sdk/schemaless-request-body-examples/src/core/url/qs.ts new file mode 100644 index 000000000000..13e89be9d9a6 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/core/url/qs.ts @@ -0,0 +1,74 @@ +interface QueryStringOptions { + arrayFormat?: "indices" | "repeat"; + encode?: boolean; +} + +const defaultQsOptions: Required = { + arrayFormat: "indices", + encode: true, +} as const; + +function encodeValue(value: unknown, shouldEncode: boolean): string { + if (value === undefined) { + return ""; + } + if (value === null) { + return ""; + } + const stringValue = String(value); + return shouldEncode ? encodeURIComponent(stringValue) : stringValue; +} + +function stringifyObject(obj: Record, prefix = "", options: Required): string[] { + const parts: string[] = []; + + for (const [key, value] of Object.entries(obj)) { + const fullKey = prefix ? `${prefix}[${key}]` : key; + + if (value === undefined) { + continue; + } + + if (Array.isArray(value)) { + if (value.length === 0) { + continue; + } + for (let i = 0; i < value.length; i++) { + const item = value[i]; + if (item === undefined) { + continue; + } + if (typeof item === "object" && !Array.isArray(item) && item !== null) { + const arrayKey = options.arrayFormat === "indices" ? `${fullKey}[${i}]` : fullKey; + parts.push(...stringifyObject(item as Record, arrayKey, options)); + } else { + const arrayKey = options.arrayFormat === "indices" ? `${fullKey}[${i}]` : fullKey; + const encodedKey = options.encode ? encodeURIComponent(arrayKey) : arrayKey; + parts.push(`${encodedKey}=${encodeValue(item, options.encode)}`); + } + } + } else if (typeof value === "object" && value !== null) { + if (Object.keys(value as Record).length === 0) { + continue; + } + parts.push(...stringifyObject(value as Record, fullKey, options)); + } else { + const encodedKey = options.encode ? encodeURIComponent(fullKey) : fullKey; + parts.push(`${encodedKey}=${encodeValue(value, options.encode)}`); + } + } + + return parts; +} + +export function toQueryString(obj: unknown, options?: QueryStringOptions): string { + if (obj == null || typeof obj !== "object") { + return ""; + } + + const parts = stringifyObject(obj as Record, "", { + ...defaultQsOptions, + ...options, + }); + return parts.join("&"); +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/errors/SeedApiError.ts b/seed/ts-sdk/schemaless-request-body-examples/src/errors/SeedApiError.ts new file mode 100644 index 000000000000..ec2bc570e2a7 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/errors/SeedApiError.ts @@ -0,0 +1,64 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as core from "../core/index.js"; +import { toJson } from "../core/json.js"; + +export class SeedApiError extends Error { + public readonly statusCode?: number; + public readonly body?: unknown; + public readonly rawResponse?: core.RawResponse; + public readonly cause?: unknown; + + constructor({ + message, + statusCode, + body, + rawResponse, + cause, + }: { + message?: string; + statusCode?: number; + body?: unknown; + rawResponse?: core.RawResponse; + cause?: unknown; + }) { + super(buildMessage({ message, statusCode, body })); + Object.setPrototypeOf(this, new.target.prototype); + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + + this.name = this.constructor.name; + this.statusCode = statusCode; + this.body = body; + this.rawResponse = rawResponse; + if (cause != null) { + this.cause = cause; + } + } +} + +function buildMessage({ + message, + statusCode, + body, +}: { + message: string | undefined; + statusCode: number | undefined; + body: unknown | undefined; +}): string { + const lines: string[] = []; + if (message != null) { + lines.push(message); + } + + if (statusCode != null) { + lines.push(`Status code: ${statusCode.toString()}`); + } + + if (body != null) { + lines.push(`Body: ${toJson(body, undefined, 2)}`); + } + + return lines.join("\n"); +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/errors/SeedApiTimeoutError.ts b/seed/ts-sdk/schemaless-request-body-examples/src/errors/SeedApiTimeoutError.ts new file mode 100644 index 000000000000..f8f6a5f95430 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/errors/SeedApiTimeoutError.ts @@ -0,0 +1,18 @@ +// This file was auto-generated by Fern from our API Definition. + +export class SeedApiTimeoutError extends Error { + public readonly cause?: unknown; + + constructor(message: string, opts?: { cause?: unknown }) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + + this.name = this.constructor.name; + if (opts?.cause != null) { + this.cause = opts.cause; + } + } +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/errors/handleNonStatusCodeError.ts b/seed/ts-sdk/schemaless-request-body-examples/src/errors/handleNonStatusCodeError.ts new file mode 100644 index 000000000000..27d1ebec132d --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/errors/handleNonStatusCodeError.ts @@ -0,0 +1,40 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as core from "../core/index.js"; +import * as errors from "./index.js"; + +export function handleNonStatusCodeError( + error: core.Fetcher.Error, + rawResponse: core.RawResponse, + method: string, + path: string, +): never { + switch (error.reason) { + case "non-json": + throw new errors.SeedApiError({ + statusCode: error.statusCode, + body: error.rawBody, + rawResponse: rawResponse, + }); + case "body-is-null": + throw new errors.SeedApiError({ + statusCode: error.statusCode, + rawResponse: rawResponse, + }); + case "timeout": + throw new errors.SeedApiTimeoutError(`Timeout exceeded when calling ${method} ${path}.`, { + cause: error.cause, + }); + case "unknown": + throw new errors.SeedApiError({ + message: error.errorMessage, + rawResponse: rawResponse, + cause: error.cause, + }); + default: + throw new errors.SeedApiError({ + message: "Unknown error", + rawResponse: rawResponse, + }); + } +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/errors/index.ts b/seed/ts-sdk/schemaless-request-body-examples/src/errors/index.ts new file mode 100644 index 000000000000..09e82b954c26 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/errors/index.ts @@ -0,0 +1,2 @@ +export { SeedApiError } from "./SeedApiError.js"; +export { SeedApiTimeoutError } from "./SeedApiTimeoutError.js"; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/exports.ts b/seed/ts-sdk/schemaless-request-body-examples/src/exports.ts new file mode 100644 index 000000000000..7b70ee14fc02 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/exports.ts @@ -0,0 +1 @@ +export * from "./core/exports.js"; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/index.ts b/seed/ts-sdk/schemaless-request-body-examples/src/index.ts new file mode 100644 index 000000000000..b5794fd541c9 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/index.ts @@ -0,0 +1,5 @@ +export * as SeedApi from "./api/index.js"; +export type { BaseClientOptions, BaseRequestOptions } from "./BaseClient.js"; +export { SeedApiClient } from "./Client.js"; +export { SeedApiError, SeedApiTimeoutError } from "./errors/index.js"; +export * from "./exports.js"; diff --git a/seed/ts-sdk/schemaless-request-body-examples/src/version.ts b/seed/ts-sdk/schemaless-request-body-examples/src/version.ts new file mode 100644 index 000000000000..b643a3e3ea27 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/src/version.ts @@ -0,0 +1 @@ +export const SDK_VERSION = "0.0.1"; diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/custom.test.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/custom.test.ts new file mode 100644 index 000000000000..7f5e031c8396 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/custom.test.ts @@ -0,0 +1,13 @@ +/** + * This is a custom test file, if you wish to add more tests + * to your SDK. + * Be sure to mark this file in `.fernignore`. + * + * If you include example requests/responses in your fern definition, + * you will have tests automatically generated for you. + */ +describe("test", () => { + it("default", () => { + expect(true).toBe(true); + }); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/MockServer.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/MockServer.ts new file mode 100644 index 000000000000..954872157d52 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/MockServer.ts @@ -0,0 +1,29 @@ +import type { RequestHandlerOptions } from "msw"; +import type { SetupServer } from "msw/node"; + +import { mockEndpointBuilder } from "./mockEndpointBuilder"; + +export interface MockServerOptions { + baseUrl: string; + server: SetupServer; +} + +export class MockServer { + private readonly server: SetupServer; + public readonly baseUrl: string; + + constructor({ baseUrl, server }: MockServerOptions) { + this.baseUrl = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl; + this.server = server; + } + + public mockEndpoint(options?: RequestHandlerOptions): ReturnType { + const builder = mockEndpointBuilder({ + once: options?.once ?? true, + onBuild: (handler) => { + this.server.use(handler); + }, + }).baseUrl(this.baseUrl); + return builder; + } +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/MockServerPool.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/MockServerPool.ts new file mode 100644 index 000000000000..d7d891a2d80b --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/MockServerPool.ts @@ -0,0 +1,106 @@ +import { setupServer } from "msw/node"; + +import { fromJson, toJson } from "../../src/core/json"; +import { MockServer } from "./MockServer"; +import { randomBaseUrl } from "./randomBaseUrl"; + +const mswServer = setupServer(); +interface MockServerOptions { + baseUrl?: string; +} + +async function formatHttpRequest(request: Request, id?: string): Promise { + try { + const clone = request.clone(); + const headers = [...clone.headers.entries()].map(([k, v]) => `${k}: ${v}`).join("\n"); + + let body = ""; + try { + const contentType = clone.headers.get("content-type"); + if (contentType?.includes("application/json")) { + body = toJson(fromJson(await clone.text()), undefined, 2); + } else if (clone.body) { + body = await clone.text(); + } + } catch (_e) { + body = "(unable to parse body)"; + } + + const title = id ? `### Request ${id} ###\n` : ""; + const firstLine = `${title}${request.method} ${request.url.toString()} HTTP/1.1`; + + return `\n${firstLine}\n${headers}\n\n${body || "(no body)"}\n`; + } catch (e) { + return `Error formatting request: ${e}`; + } +} + +async function formatHttpResponse(response: Response, id?: string): Promise { + try { + const clone = response.clone(); + const headers = [...clone.headers.entries()].map(([k, v]) => `${k}: ${v}`).join("\n"); + + let body = ""; + try { + const contentType = clone.headers.get("content-type"); + if (contentType?.includes("application/json")) { + body = toJson(fromJson(await clone.text()), undefined, 2); + } else if (clone.body) { + body = await clone.text(); + } + } catch (_e) { + body = "(unable to parse body)"; + } + + const title = id ? `### Response for ${id} ###\n` : ""; + const firstLine = `${title}HTTP/1.1 ${response.status} ${response.statusText}`; + + return `\n${firstLine}\n${headers}\n\n${body || "(no body)"}\n`; + } catch (e) { + return `Error formatting response: ${e}`; + } +} + +class MockServerPool { + private servers: MockServer[] = []; + + public createServer(options?: Partial): MockServer { + const baseUrl = options?.baseUrl || randomBaseUrl(); + const server = new MockServer({ baseUrl, server: mswServer }); + this.servers.push(server); + return server; + } + + public getServers(): MockServer[] { + return [...this.servers]; + } + + public listen(): void { + const onUnhandledRequest = process.env.LOG_LEVEL === "debug" ? "warn" : "bypass"; + mswServer.listen({ onUnhandledRequest }); + + if (process.env.LOG_LEVEL === "debug") { + mswServer.events.on("request:start", async ({ request, requestId }) => { + const formattedRequest = await formatHttpRequest(request, requestId); + console.debug(`request:start\n${formattedRequest}`); + }); + + mswServer.events.on("request:unhandled", async ({ request, requestId }) => { + const formattedRequest = await formatHttpRequest(request, requestId); + console.debug(`request:unhandled\n${formattedRequest}`); + }); + + mswServer.events.on("response:mocked", async ({ request, response, requestId }) => { + const formattedResponse = await formatHttpResponse(response, requestId); + console.debug(`response:mocked\n${formattedResponse}`); + }); + } + } + + public close(): void { + this.servers = []; + mswServer.close(); + } +} + +export const mockServerPool: MockServerPool = new MockServerPool(); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/mockEndpointBuilder.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/mockEndpointBuilder.ts new file mode 100644 index 000000000000..3e8540a3ba5a --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/mockEndpointBuilder.ts @@ -0,0 +1,234 @@ +import { type DefaultBodyType, type HttpHandler, HttpResponse, type HttpResponseResolver, http } from "msw"; + +import { url } from "../../src/core"; +import { toJson } from "../../src/core/json"; +import { type WithFormUrlEncodedOptions, withFormUrlEncoded } from "./withFormUrlEncoded"; +import { withHeaders } from "./withHeaders"; +import { type WithJsonOptions, withJson } from "./withJson"; + +type HttpMethod = "all" | "get" | "post" | "put" | "delete" | "patch" | "options" | "head"; + +interface MethodStage { + baseUrl(baseUrl: string): MethodStage; + all(path: string): RequestHeadersStage; + get(path: string): RequestHeadersStage; + post(path: string): RequestHeadersStage; + put(path: string): RequestHeadersStage; + delete(path: string): RequestHeadersStage; + patch(path: string): RequestHeadersStage; + options(path: string): RequestHeadersStage; + head(path: string): RequestHeadersStage; +} + +interface RequestHeadersStage extends RequestBodyStage, ResponseStage { + header(name: string, value: string): RequestHeadersStage; + headers(headers: Record): RequestBodyStage; +} + +interface RequestBodyStage extends ResponseStage { + jsonBody(body: unknown, options?: WithJsonOptions): ResponseStage; + formUrlEncodedBody(body: unknown, options?: WithFormUrlEncodedOptions): ResponseStage; +} + +interface ResponseStage { + respondWith(): ResponseStatusStage; +} +interface ResponseStatusStage { + statusCode(statusCode: number): ResponseHeaderStage; +} + +interface ResponseHeaderStage extends ResponseBodyStage, BuildStage { + header(name: string, value: string): ResponseHeaderStage; + headers(headers: Record): ResponseHeaderStage; +} + +interface ResponseBodyStage { + jsonBody(body: unknown): BuildStage; + sseBody(body: string): BuildStage; +} + +interface BuildStage { + build(): HttpHandler; +} + +export interface HttpHandlerBuilderOptions { + onBuild?: (handler: HttpHandler) => void; + once?: boolean; +} + +class RequestBuilder implements MethodStage, RequestHeadersStage, RequestBodyStage, ResponseStage { + private method: HttpMethod = "get"; + private _baseUrl: string = ""; + private path: string = "/"; + private readonly predicates: ((resolver: HttpResponseResolver) => HttpResponseResolver)[] = []; + private readonly handlerOptions?: HttpHandlerBuilderOptions; + + constructor(options?: HttpHandlerBuilderOptions) { + this.handlerOptions = options; + } + + baseUrl(baseUrl: string): MethodStage { + this._baseUrl = baseUrl; + return this; + } + + all(path: string): RequestHeadersStage { + this.method = "all"; + this.path = path; + return this; + } + + get(path: string): RequestHeadersStage { + this.method = "get"; + this.path = path; + return this; + } + + post(path: string): RequestHeadersStage { + this.method = "post"; + this.path = path; + return this; + } + + put(path: string): RequestHeadersStage { + this.method = "put"; + this.path = path; + return this; + } + + delete(path: string): RequestHeadersStage { + this.method = "delete"; + this.path = path; + return this; + } + + patch(path: string): RequestHeadersStage { + this.method = "patch"; + this.path = path; + return this; + } + + options(path: string): RequestHeadersStage { + this.method = "options"; + this.path = path; + return this; + } + + head(path: string): RequestHeadersStage { + this.method = "head"; + this.path = path; + return this; + } + + header(name: string, value: string): RequestHeadersStage { + this.predicates.push((resolver) => withHeaders({ [name]: value }, resolver)); + return this; + } + + headers(headers: Record): RequestBodyStage { + this.predicates.push((resolver) => withHeaders(headers, resolver)); + return this; + } + + jsonBody(body: unknown, options?: WithJsonOptions): ResponseStage { + if (body === undefined) { + throw new Error("Undefined is not valid JSON. Do not call jsonBody if you want an empty body."); + } + this.predicates.push((resolver) => withJson(body, resolver, options)); + return this; + } + + formUrlEncodedBody(body: unknown, options?: WithFormUrlEncodedOptions): ResponseStage { + if (body === undefined) { + throw new Error( + "Undefined is not valid for form-urlencoded. Do not call formUrlEncodedBody if you want an empty body.", + ); + } + this.predicates.push((resolver) => withFormUrlEncoded(body, resolver, options)); + return this; + } + + respondWith(): ResponseStatusStage { + return new ResponseBuilder(this.method, this.buildUrl(), this.predicates, this.handlerOptions); + } + + private buildUrl(): string { + return url.join(this._baseUrl, this.path); + } +} + +class ResponseBuilder implements ResponseStatusStage, ResponseHeaderStage, ResponseBodyStage, BuildStage { + private readonly method: HttpMethod; + private readonly url: string; + private readonly requestPredicates: ((resolver: HttpResponseResolver) => HttpResponseResolver)[]; + private readonly handlerOptions?: HttpHandlerBuilderOptions; + + private responseStatusCode: number = 200; + private responseHeaders: Record = {}; + private responseBody: DefaultBodyType = undefined; + + constructor( + method: HttpMethod, + url: string, + requestPredicates: ((resolver: HttpResponseResolver) => HttpResponseResolver)[], + options?: HttpHandlerBuilderOptions, + ) { + this.method = method; + this.url = url; + this.requestPredicates = requestPredicates; + this.handlerOptions = options; + } + + public statusCode(code: number): ResponseHeaderStage { + this.responseStatusCode = code; + return this; + } + + public header(name: string, value: string): ResponseHeaderStage { + this.responseHeaders[name] = value; + return this; + } + + public headers(headers: Record): ResponseHeaderStage { + this.responseHeaders = { ...this.responseHeaders, ...headers }; + return this; + } + + public jsonBody(body: unknown): BuildStage { + if (body === undefined) { + throw new Error("Undefined is not valid JSON. Do not call jsonBody if you expect an empty body."); + } + this.responseBody = toJson(body); + return this; + } + + public sseBody(body: string): BuildStage { + this.responseHeaders["Content-Type"] = "text/event-stream"; + this.responseBody = body; + return this; + } + + public build(): HttpHandler { + const responseResolver: HttpResponseResolver = () => { + const response = new HttpResponse(this.responseBody, { + status: this.responseStatusCode, + headers: this.responseHeaders, + }); + // if no Content-Type header is set, delete the default text content type that is set + if (Object.keys(this.responseHeaders).some((key) => key.toLowerCase() === "content-type") === false) { + response.headers.delete("Content-Type"); + } + return response; + }; + + const finalResolver = this.requestPredicates.reduceRight((acc, predicate) => predicate(acc), responseResolver); + + const handler = http[this.method](this.url, finalResolver, this.handlerOptions); + this.handlerOptions?.onBuild?.(handler); + return handler; + } +} + +export function mockEndpointBuilder(options?: HttpHandlerBuilderOptions): MethodStage { + return new RequestBuilder(options); +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/randomBaseUrl.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/randomBaseUrl.ts new file mode 100644 index 000000000000..031aa6408aca --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/randomBaseUrl.ts @@ -0,0 +1,4 @@ +export function randomBaseUrl(): string { + const randomString = Math.random().toString(36).substring(2, 15); + return `http://${randomString}.localhost`; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/setup.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/setup.ts new file mode 100644 index 000000000000..aeb3a95af7dc --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/setup.ts @@ -0,0 +1,10 @@ +import { afterAll, beforeAll } from "vitest"; + +import { mockServerPool } from "./MockServerPool"; + +beforeAll(() => { + mockServerPool.listen(); +}); +afterAll(() => { + mockServerPool.close(); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/withFormUrlEncoded.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/withFormUrlEncoded.ts new file mode 100644 index 000000000000..2b23448e3102 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/withFormUrlEncoded.ts @@ -0,0 +1,104 @@ +import { type HttpResponseResolver, passthrough } from "msw"; + +import { toJson } from "../../src/core/json"; + +export interface WithFormUrlEncodedOptions { + /** + * List of field names to ignore when comparing request bodies. + * This is useful for pagination cursor fields that change between requests. + */ + ignoredFields?: string[]; +} + +/** + * Creates a request matcher that validates if the request form-urlencoded body exactly matches the expected object + * @param expectedBody - The exact body object to match against + * @param resolver - Response resolver to execute if body matches + * @param options - Optional configuration including fields to ignore + */ +export function withFormUrlEncoded( + expectedBody: unknown, + resolver: HttpResponseResolver, + options?: WithFormUrlEncodedOptions, +): HttpResponseResolver { + const ignoredFields = options?.ignoredFields ?? []; + return async (args) => { + const { request } = args; + + let clonedRequest: Request; + let bodyText: string | undefined; + let actualBody: Record; + try { + clonedRequest = request.clone(); + bodyText = await clonedRequest.text(); + if (bodyText === "") { + // Empty body is valid if expected body is also empty + const isExpectedEmpty = + expectedBody != null && + typeof expectedBody === "object" && + Object.keys(expectedBody as Record).length === 0; + if (!isExpectedEmpty) { + console.error("Request body is empty, expected a form-urlencoded body."); + return passthrough(); + } + actualBody = {}; + } else { + const params = new URLSearchParams(bodyText); + actualBody = {}; + for (const [key, value] of params.entries()) { + actualBody[key] = value; + } + } + } catch (error) { + console.error(`Error processing form-urlencoded request body:\n\tError: ${error}\n\tBody: ${bodyText}`); + return passthrough(); + } + + const mismatches = findMismatches(actualBody, expectedBody); + const filteredMismatches = Object.keys(mismatches).filter((key) => !ignoredFields.includes(key)); + if (filteredMismatches.length > 0) { + console.error("Form-urlencoded body mismatch:", toJson(mismatches, undefined, 2)); + return passthrough(); + } + + return resolver(args); + }; +} + +function findMismatches(actual: any, expected: any): Record { + const mismatches: Record = {}; + + if (typeof actual !== typeof expected) { + return { value: { actual, expected } }; + } + + if (typeof actual !== "object" || actual === null || expected === null) { + if (actual !== expected) { + return { value: { actual, expected } }; + } + return {}; + } + + const actualKeys = Object.keys(actual); + const expectedKeys = Object.keys(expected); + + const allKeys = new Set([...actualKeys, ...expectedKeys]); + + for (const key of allKeys) { + if (!expectedKeys.includes(key)) { + if (actual[key] === undefined) { + continue; + } + mismatches[key] = { actual: actual[key], expected: undefined }; + } else if (!actualKeys.includes(key)) { + if (expected[key] === undefined) { + continue; + } + mismatches[key] = { actual: undefined, expected: expected[key] }; + } else if (actual[key] !== expected[key]) { + mismatches[key] = { actual: actual[key], expected: expected[key] }; + } + } + + return mismatches; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/withHeaders.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/withHeaders.ts new file mode 100644 index 000000000000..6599d2b4a92d --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/withHeaders.ts @@ -0,0 +1,70 @@ +import { type HttpResponseResolver, passthrough } from "msw"; + +/** + * Creates a request matcher that validates if request headers match specified criteria + * @param expectedHeaders - Headers to match against + * @param resolver - Response resolver to execute if headers match + */ +export function withHeaders( + expectedHeaders: Record boolean)>, + resolver: HttpResponseResolver, +): HttpResponseResolver { + return (args) => { + const { request } = args; + const { headers } = request; + + const mismatches: Record< + string, + { actual: string | null; expected: string | RegExp | ((value: string) => boolean) } + > = {}; + + for (const [key, expectedValue] of Object.entries(expectedHeaders)) { + const actualValue = headers.get(key); + + if (actualValue === null) { + mismatches[key] = { actual: null, expected: expectedValue }; + continue; + } + + if (typeof expectedValue === "function") { + if (!expectedValue(actualValue)) { + mismatches[key] = { actual: actualValue, expected: expectedValue }; + } + } else if (expectedValue instanceof RegExp) { + if (!expectedValue.test(actualValue)) { + mismatches[key] = { actual: actualValue, expected: expectedValue }; + } + } else if (expectedValue !== actualValue) { + mismatches[key] = { actual: actualValue, expected: expectedValue }; + } + } + + if (Object.keys(mismatches).length > 0) { + const formattedMismatches = formatHeaderMismatches(mismatches); + console.error("Header mismatch:", formattedMismatches); + return passthrough(); + } + + return resolver(args); + }; +} + +function formatHeaderMismatches( + mismatches: Record boolean) }>, +): Record { + const formatted: Record = {}; + + for (const [key, { actual, expected }] of Object.entries(mismatches)) { + formatted[key] = { + actual, + expected: + expected instanceof RegExp + ? expected.toString() + : typeof expected === "function" + ? "[Function]" + : expected, + }; + } + + return formatted; +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/withJson.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/withJson.ts new file mode 100644 index 000000000000..3e8800a0c374 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/mock-server/withJson.ts @@ -0,0 +1,173 @@ +import { type HttpResponseResolver, passthrough } from "msw"; + +import { fromJson, toJson } from "../../src/core/json"; + +export interface WithJsonOptions { + /** + * List of field names to ignore when comparing request bodies. + * This is useful for pagination cursor fields that change between requests. + */ + ignoredFields?: string[]; +} + +/** + * Creates a request matcher that validates if the request JSON body exactly matches the expected object + * @param expectedBody - The exact body object to match against + * @param resolver - Response resolver to execute if body matches + * @param options - Optional configuration including fields to ignore + */ +export function withJson( + expectedBody: unknown, + resolver: HttpResponseResolver, + options?: WithJsonOptions, +): HttpResponseResolver { + const ignoredFields = options?.ignoredFields ?? []; + return async (args) => { + const { request } = args; + + let clonedRequest: Request; + let bodyText: string | undefined; + let actualBody: unknown; + try { + clonedRequest = request.clone(); + bodyText = await clonedRequest.text(); + if (bodyText === "") { + console.error("Request body is empty, expected a JSON object."); + return passthrough(); + } + actualBody = fromJson(bodyText); + } catch (error) { + console.error(`Error processing request body:\n\tError: ${error}\n\tBody: ${bodyText}`); + return passthrough(); + } + + const mismatches = findMismatches(actualBody, expectedBody); + const filteredMismatches = Object.keys(mismatches).filter((key) => !ignoredFields.includes(key)); + if (filteredMismatches.length > 0) { + console.error("JSON body mismatch:", toJson(mismatches, undefined, 2)); + return passthrough(); + } + + return resolver(args); + }; +} + +function findMismatches(actual: any, expected: any): Record { + const mismatches: Record = {}; + + if (typeof actual !== typeof expected) { + if (areEquivalent(actual, expected)) { + return {}; + } + return { value: { actual, expected } }; + } + + if (typeof actual !== "object" || actual === null || expected === null) { + if (actual !== expected) { + if (areEquivalent(actual, expected)) { + return {}; + } + return { value: { actual, expected } }; + } + return {}; + } + + if (Array.isArray(actual) && Array.isArray(expected)) { + if (actual.length !== expected.length) { + return { length: { actual: actual.length, expected: expected.length } }; + } + + const arrayMismatches: Record = {}; + for (let i = 0; i < actual.length; i++) { + const itemMismatches = findMismatches(actual[i], expected[i]); + if (Object.keys(itemMismatches).length > 0) { + for (const [mismatchKey, mismatchValue] of Object.entries(itemMismatches)) { + arrayMismatches[`[${i}]${mismatchKey === "value" ? "" : `.${mismatchKey}`}`] = mismatchValue; + } + } + } + return arrayMismatches; + } + + const actualKeys = Object.keys(actual); + const expectedKeys = Object.keys(expected); + + const allKeys = new Set([...actualKeys, ...expectedKeys]); + + for (const key of allKeys) { + if (!expectedKeys.includes(key)) { + if (actual[key] === undefined) { + continue; // Skip undefined values in actual + } + mismatches[key] = { actual: actual[key], expected: undefined }; + } else if (!actualKeys.includes(key)) { + if (expected[key] === undefined) { + continue; // Skip undefined values in expected + } + mismatches[key] = { actual: undefined, expected: expected[key] }; + } else if ( + typeof actual[key] === "object" && + actual[key] !== null && + typeof expected[key] === "object" && + expected[key] !== null + ) { + const nestedMismatches = findMismatches(actual[key], expected[key]); + if (Object.keys(nestedMismatches).length > 0) { + for (const [nestedKey, nestedValue] of Object.entries(nestedMismatches)) { + mismatches[`${key}${nestedKey === "value" ? "" : `.${nestedKey}`}`] = nestedValue; + } + } + } else if (actual[key] !== expected[key]) { + if (areEquivalent(actual[key], expected[key])) { + continue; + } + mismatches[key] = { actual: actual[key], expected: expected[key] }; + } + } + + return mismatches; +} + +function areEquivalent(actual: unknown, expected: unknown): boolean { + if (actual === expected) { + return true; + } + if (isEquivalentBigInt(actual, expected)) { + return true; + } + if (isEquivalentDatetime(actual, expected)) { + return true; + } + return false; +} + +function isEquivalentBigInt(actual: unknown, expected: unknown) { + if (typeof actual === "number") { + actual = BigInt(actual); + } + if (typeof expected === "number") { + expected = BigInt(expected); + } + if (typeof actual === "bigint" && typeof expected === "bigint") { + return actual === expected; + } + return false; +} + +function isEquivalentDatetime(str1: unknown, str2: unknown): boolean { + if (typeof str1 !== "string" || typeof str2 !== "string") { + return false; + } + const isoDatePattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/; + if (!isoDatePattern.test(str1) || !isoDatePattern.test(str2)) { + return false; + } + + try { + const date1 = new Date(str1).getTime(); + const date2 = new Date(str2).getTime(); + return date1 === date2; + } catch { + return false; + } +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/setup.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/setup.ts new file mode 100644 index 000000000000..a5651f81ba10 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/setup.ts @@ -0,0 +1,80 @@ +import { expect } from "vitest"; + +interface CustomMatchers { + toContainHeaders(expectedHeaders: Record): R; +} + +declare module "vitest" { + interface Assertion extends CustomMatchers {} + interface AsymmetricMatchersContaining extends CustomMatchers {} +} + +expect.extend({ + toContainHeaders(actual: unknown, expectedHeaders: Record) { + const isHeaders = actual instanceof Headers; + const isPlainObject = typeof actual === "object" && actual !== null && !Array.isArray(actual); + + if (!isHeaders && !isPlainObject) { + throw new TypeError("Received value must be an instance of Headers or a plain object!"); + } + + if (typeof expectedHeaders !== "object" || expectedHeaders === null || Array.isArray(expectedHeaders)) { + throw new TypeError("Expected headers must be a plain object!"); + } + + const missingHeaders: string[] = []; + const mismatchedHeaders: Array<{ key: string; expected: string; actual: string | null }> = []; + + for (const [key, value] of Object.entries(expectedHeaders)) { + let actualValue: string | null = null; + + if (isHeaders) { + // Headers.get() is already case-insensitive + actualValue = (actual as Headers).get(key); + } else { + // For plain objects, do case-insensitive lookup + const actualObj = actual as Record; + const lowerKey = key.toLowerCase(); + const foundKey = Object.keys(actualObj).find((k) => k.toLowerCase() === lowerKey); + actualValue = foundKey ? actualObj[foundKey] : null; + } + + if (actualValue === null || actualValue === undefined) { + missingHeaders.push(key); + } else if (actualValue !== value) { + mismatchedHeaders.push({ key, expected: value, actual: actualValue }); + } + } + + const pass = missingHeaders.length === 0 && mismatchedHeaders.length === 0; + + const actualType = isHeaders ? "Headers" : "object"; + + if (pass) { + return { + message: () => `expected ${actualType} not to contain ${this.utils.printExpected(expectedHeaders)}`, + pass: true, + }; + } else { + const messages: string[] = []; + + if (missingHeaders.length > 0) { + messages.push(`Missing headers: ${this.utils.printExpected(missingHeaders.join(", "))}`); + } + + if (mismatchedHeaders.length > 0) { + const mismatches = mismatchedHeaders.map( + ({ key, expected, actual }) => + `${key}: expected ${this.utils.printExpected(expected)} but got ${this.utils.printReceived(actual)}`, + ); + messages.push(mismatches.join("\n")); + } + + return { + message: () => + `expected ${actualType} to contain ${this.utils.printExpected(expectedHeaders)}\n\n${messages.join("\n")}`, + pass: false, + }; + } + }, +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/tsconfig.json b/seed/ts-sdk/schemaless-request-body-examples/tests/tsconfig.json new file mode 100644 index 000000000000..ac39744de7b2 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "outDir": null, + "rootDir": "..", + "types": ["vitest/globals"] + }, + "include": ["../src", "../tests"], + "exclude": [] +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/Fetcher.test.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/Fetcher.test.ts new file mode 100644 index 000000000000..6c17624228bb --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/Fetcher.test.ts @@ -0,0 +1,262 @@ +import fs from "fs"; +import { join } from "path"; +import stream from "stream"; +import type { BinaryResponse } from "../../../src/core"; +import { type Fetcher, fetcherImpl } from "../../../src/core/fetcher/Fetcher"; + +describe("Test fetcherImpl", () => { + it("should handle successful request", async () => { + const mockArgs: Fetcher.Args = { + url: "https://httpbin.org/post", + method: "POST", + headers: { "X-Test": "x-test-header" }, + body: { data: "test" }, + contentType: "application/json", + requestType: "json", + maxRetries: 0, + responseType: "json", + }; + + global.fetch = vi.fn().mockResolvedValue( + new Response(JSON.stringify({ data: "test" }), { + status: 200, + statusText: "OK", + }), + ); + + const result = await fetcherImpl(mockArgs); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.body).toEqual({ data: "test" }); + } + + expect(global.fetch).toHaveBeenCalledWith( + "https://httpbin.org/post", + expect.objectContaining({ + method: "POST", + headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), + body: JSON.stringify({ data: "test" }), + }), + ); + }); + + it("should send octet stream", async () => { + const url = "https://httpbin.org/post/file"; + const mockArgs: Fetcher.Args = { + url, + method: "POST", + headers: { "X-Test": "x-test-header" }, + contentType: "application/octet-stream", + requestType: "bytes", + maxRetries: 0, + responseType: "json", + body: fs.createReadStream(join(__dirname, "test-file.txt")), + }; + + global.fetch = vi.fn().mockResolvedValue( + new Response(JSON.stringify({ data: "test" }), { + status: 200, + statusText: "OK", + }), + ); + + const result = await fetcherImpl(mockArgs); + + expect(global.fetch).toHaveBeenCalledWith( + url, + expect.objectContaining({ + method: "POST", + headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), + body: expect.any(fs.ReadStream), + }), + ); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.body).toEqual({ data: "test" }); + } + }); + + it("should receive file as stream", async () => { + const url = "https://httpbin.org/post/file"; + const mockArgs: Fetcher.Args = { + url, + method: "GET", + headers: { "X-Test": "x-test-header" }, + maxRetries: 0, + responseType: "binary-response", + }; + + global.fetch = vi.fn().mockResolvedValue( + new Response( + stream.Readable.toWeb(fs.createReadStream(join(__dirname, "test-file.txt"))) as ReadableStream, + { + status: 200, + statusText: "OK", + }, + ), + ); + + const result = await fetcherImpl(mockArgs); + + expect(global.fetch).toHaveBeenCalledWith( + url, + expect.objectContaining({ + method: "GET", + headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), + }), + ); + expect(result.ok).toBe(true); + if (result.ok) { + const body = result.body as BinaryResponse; + expect(body).toBeDefined(); + expect(body.bodyUsed).toBe(false); + expect(typeof body.stream).toBe("function"); + const stream = body.stream(); + expect(stream).toBeInstanceOf(ReadableStream); + const readableStream = stream as ReadableStream; + const reader = readableStream.getReader(); + const { value } = await reader.read(); + const decoder = new TextDecoder(); + const streamContent = decoder.decode(value); + expect(streamContent.trim()).toBe("This is a test file!"); + expect(body.bodyUsed).toBe(true); + } + }); + + it("should receive file as blob", async () => { + const url = "https://httpbin.org/post/file"; + const mockArgs: Fetcher.Args = { + url, + method: "GET", + headers: { "X-Test": "x-test-header" }, + maxRetries: 0, + responseType: "binary-response", + }; + + global.fetch = vi.fn().mockResolvedValue( + new Response( + stream.Readable.toWeb(fs.createReadStream(join(__dirname, "test-file.txt"))) as ReadableStream, + { + status: 200, + statusText: "OK", + }, + ), + ); + + const result = await fetcherImpl(mockArgs); + + expect(global.fetch).toHaveBeenCalledWith( + url, + expect.objectContaining({ + method: "GET", + headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), + }), + ); + expect(result.ok).toBe(true); + if (result.ok) { + const body = result.body as BinaryResponse; + expect(body).toBeDefined(); + expect(body.bodyUsed).toBe(false); + expect(typeof body.blob).toBe("function"); + const blob = await body.blob(); + expect(blob).toBeInstanceOf(Blob); + const reader = blob.stream().getReader(); + const { value } = await reader.read(); + const decoder = new TextDecoder(); + const streamContent = decoder.decode(value); + expect(streamContent.trim()).toBe("This is a test file!"); + expect(body.bodyUsed).toBe(true); + } + }); + + it("should receive file as arraybuffer", async () => { + const url = "https://httpbin.org/post/file"; + const mockArgs: Fetcher.Args = { + url, + method: "GET", + headers: { "X-Test": "x-test-header" }, + maxRetries: 0, + responseType: "binary-response", + }; + + global.fetch = vi.fn().mockResolvedValue( + new Response( + stream.Readable.toWeb(fs.createReadStream(join(__dirname, "test-file.txt"))) as ReadableStream, + { + status: 200, + statusText: "OK", + }, + ), + ); + + const result = await fetcherImpl(mockArgs); + + expect(global.fetch).toHaveBeenCalledWith( + url, + expect.objectContaining({ + method: "GET", + headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), + }), + ); + expect(result.ok).toBe(true); + if (result.ok) { + const body = result.body as BinaryResponse; + expect(body).toBeDefined(); + expect(body.bodyUsed).toBe(false); + expect(typeof body.arrayBuffer).toBe("function"); + const arrayBuffer = await body.arrayBuffer(); + expect(arrayBuffer).toBeInstanceOf(ArrayBuffer); + const decoder = new TextDecoder(); + const streamContent = decoder.decode(new Uint8Array(arrayBuffer)); + expect(streamContent.trim()).toBe("This is a test file!"); + expect(body.bodyUsed).toBe(true); + } + }); + + it("should receive file as bytes", async () => { + const url = "https://httpbin.org/post/file"; + const mockArgs: Fetcher.Args = { + url, + method: "GET", + headers: { "X-Test": "x-test-header" }, + maxRetries: 0, + responseType: "binary-response", + }; + + global.fetch = vi.fn().mockResolvedValue( + new Response( + stream.Readable.toWeb(fs.createReadStream(join(__dirname, "test-file.txt"))) as ReadableStream, + { + status: 200, + statusText: "OK", + }, + ), + ); + + const result = await fetcherImpl(mockArgs); + + expect(global.fetch).toHaveBeenCalledWith( + url, + expect.objectContaining({ + method: "GET", + headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), + }), + ); + expect(result.ok).toBe(true); + if (result.ok) { + const body = result.body as BinaryResponse; + expect(body).toBeDefined(); + expect(body.bodyUsed).toBe(false); + expect(typeof body.bytes).toBe("function"); + if (!body.bytes) { + return; + } + const bytes = await body.bytes(); + expect(bytes).toBeInstanceOf(Uint8Array); + const decoder = new TextDecoder(); + const streamContent = decoder.decode(bytes); + expect(streamContent.trim()).toBe("This is a test file!"); + expect(body.bodyUsed).toBe(true); + } + }); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/HttpResponsePromise.test.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/HttpResponsePromise.test.ts new file mode 100644 index 000000000000..2ec008e581d8 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/HttpResponsePromise.test.ts @@ -0,0 +1,143 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { HttpResponsePromise } from "../../../src/core/fetcher/HttpResponsePromise"; +import type { RawResponse, WithRawResponse } from "../../../src/core/fetcher/RawResponse"; + +describe("HttpResponsePromise", () => { + const mockRawResponse: RawResponse = { + headers: new Headers(), + redirected: false, + status: 200, + statusText: "OK", + type: "basic" as ResponseType, + url: "https://example.com", + }; + const mockData = { id: "123", name: "test" }; + const mockWithRawResponse: WithRawResponse = { + data: mockData, + rawResponse: mockRawResponse, + }; + + describe("fromFunction", () => { + it("should create an HttpResponsePromise from a function", async () => { + const mockFn = vi + .fn<(arg1: string, arg2: string) => Promise>>() + .mockResolvedValue(mockWithRawResponse); + + const responsePromise = HttpResponsePromise.fromFunction(mockFn, "arg1", "arg2"); + + const result = await responsePromise; + expect(result).toEqual(mockData); + expect(mockFn).toHaveBeenCalledWith("arg1", "arg2"); + + const resultWithRawResponse = await responsePromise.withRawResponse(); + expect(resultWithRawResponse).toEqual({ + data: mockData, + rawResponse: mockRawResponse, + }); + }); + }); + + describe("fromPromise", () => { + it("should create an HttpResponsePromise from a promise", async () => { + const promise = Promise.resolve(mockWithRawResponse); + + const responsePromise = HttpResponsePromise.fromPromise(promise); + + const result = await responsePromise; + expect(result).toEqual(mockData); + + const resultWithRawResponse = await responsePromise.withRawResponse(); + expect(resultWithRawResponse).toEqual({ + data: mockData, + rawResponse: mockRawResponse, + }); + }); + }); + + describe("fromExecutor", () => { + it("should create an HttpResponsePromise from an executor function", async () => { + const responsePromise = HttpResponsePromise.fromExecutor((resolve) => { + resolve(mockWithRawResponse); + }); + + const result = await responsePromise; + expect(result).toEqual(mockData); + + const resultWithRawResponse = await responsePromise.withRawResponse(); + expect(resultWithRawResponse).toEqual({ + data: mockData, + rawResponse: mockRawResponse, + }); + }); + }); + + describe("fromResult", () => { + it("should create an HttpResponsePromise from a result", async () => { + const responsePromise = HttpResponsePromise.fromResult(mockWithRawResponse); + + const result = await responsePromise; + expect(result).toEqual(mockData); + + const resultWithRawResponse = await responsePromise.withRawResponse(); + expect(resultWithRawResponse).toEqual({ + data: mockData, + rawResponse: mockRawResponse, + }); + }); + }); + + describe("Promise methods", () => { + let responsePromise: HttpResponsePromise; + + beforeEach(() => { + responsePromise = HttpResponsePromise.fromResult(mockWithRawResponse); + }); + + it("should support then() method", async () => { + const result = await responsePromise.then((data) => ({ + ...data, + modified: true, + })); + + expect(result).toEqual({ + ...mockData, + modified: true, + }); + }); + + it("should support catch() method", async () => { + const errorResponsePromise = HttpResponsePromise.fromExecutor((_, reject) => { + reject(new Error("Test error")); + }); + + const catchSpy = vi.fn(); + await errorResponsePromise.catch(catchSpy); + + expect(catchSpy).toHaveBeenCalled(); + const error = catchSpy.mock.calls[0]?.[0]; + expect(error).toBeInstanceOf(Error); + expect((error as Error).message).toBe("Test error"); + }); + + it("should support finally() method", async () => { + const finallySpy = vi.fn(); + await responsePromise.finally(finallySpy); + + expect(finallySpy).toHaveBeenCalled(); + }); + }); + + describe("withRawResponse", () => { + it("should return both data and raw response", async () => { + const responsePromise = HttpResponsePromise.fromResult(mockWithRawResponse); + + const result = await responsePromise.withRawResponse(); + + expect(result).toEqual({ + data: mockData, + rawResponse: mockRawResponse, + }); + }); + }); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/RawResponse.test.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/RawResponse.test.ts new file mode 100644 index 000000000000..375ee3f38064 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/RawResponse.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from "vitest"; + +import { toRawResponse } from "../../../src/core/fetcher/RawResponse"; + +describe("RawResponse", () => { + describe("toRawResponse", () => { + it("should convert Response to RawResponse by removing body, bodyUsed, and ok properties", () => { + const mockHeaders = new Headers({ "content-type": "application/json" }); + const mockResponse = { + body: "test body", + bodyUsed: false, + ok: true, + headers: mockHeaders, + redirected: false, + status: 200, + statusText: "OK", + type: "basic" as ResponseType, + url: "https://example.com", + }; + + const result = toRawResponse(mockResponse as unknown as Response); + + expect("body" in result).toBe(false); + expect("bodyUsed" in result).toBe(false); + expect("ok" in result).toBe(false); + expect(result.headers).toBe(mockHeaders); + expect(result.redirected).toBe(false); + expect(result.status).toBe(200); + expect(result.statusText).toBe("OK"); + expect(result.type).toBe("basic"); + expect(result.url).toBe("https://example.com"); + }); + }); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/createRequestUrl.test.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/createRequestUrl.test.ts new file mode 100644 index 000000000000..a92f1b5e81d1 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/createRequestUrl.test.ts @@ -0,0 +1,163 @@ +import { createRequestUrl } from "../../../src/core/fetcher/createRequestUrl"; + +describe("Test createRequestUrl", () => { + const BASE_URL = "https://api.example.com"; + + interface TestCase { + description: string; + baseUrl: string; + queryParams?: Record; + expected: string; + } + + const testCases: TestCase[] = [ + { + description: "should return the base URL when no query parameters are provided", + baseUrl: BASE_URL, + expected: BASE_URL, + }, + { + description: "should append simple query parameters", + baseUrl: BASE_URL, + queryParams: { key: "value", another: "param" }, + expected: "https://api.example.com?key=value&another=param", + }, + { + description: "should handle array query parameters", + baseUrl: BASE_URL, + queryParams: { items: ["a", "b", "c"] }, + expected: "https://api.example.com?items=a&items=b&items=c", + }, + { + description: "should handle object query parameters", + baseUrl: BASE_URL, + queryParams: { filter: { name: "John", age: 30 } }, + expected: "https://api.example.com?filter%5Bname%5D=John&filter%5Bage%5D=30", + }, + { + description: "should handle mixed types of query parameters", + baseUrl: BASE_URL, + queryParams: { + simple: "value", + array: ["x", "y"], + object: { key: "value" }, + }, + expected: "https://api.example.com?simple=value&array=x&array=y&object%5Bkey%5D=value", + }, + { + description: "should handle empty query parameters object", + baseUrl: BASE_URL, + queryParams: {}, + expected: BASE_URL, + }, + { + description: "should encode special characters in query parameters", + baseUrl: BASE_URL, + queryParams: { special: "a&b=c d" }, + expected: "https://api.example.com?special=a%26b%3Dc%20d", + }, + { + description: "should handle numeric values", + baseUrl: BASE_URL, + queryParams: { count: 42, price: 19.99, active: 1, inactive: 0 }, + expected: "https://api.example.com?count=42&price=19.99&active=1&inactive=0", + }, + { + description: "should handle boolean values", + baseUrl: BASE_URL, + queryParams: { enabled: true, disabled: false }, + expected: "https://api.example.com?enabled=true&disabled=false", + }, + { + description: "should handle null and undefined values", + baseUrl: BASE_URL, + queryParams: { + valid: "value", + nullValue: null, + undefinedValue: undefined, + emptyString: "", + }, + expected: "https://api.example.com?valid=value&nullValue=&emptyString=", + }, + { + description: "should handle deeply nested objects", + baseUrl: BASE_URL, + queryParams: { + user: { + profile: { + name: "John", + settings: { theme: "dark" }, + }, + }, + }, + expected: + "https://api.example.com?user%5Bprofile%5D%5Bname%5D=John&user%5Bprofile%5D%5Bsettings%5D%5Btheme%5D=dark", + }, + { + description: "should handle arrays of objects", + baseUrl: BASE_URL, + queryParams: { + users: [ + { name: "John", age: 30 }, + { name: "Jane", age: 25 }, + ], + }, + expected: + "https://api.example.com?users%5Bname%5D=John&users%5Bage%5D=30&users%5Bname%5D=Jane&users%5Bage%5D=25", + }, + { + description: "should handle mixed arrays", + baseUrl: BASE_URL, + queryParams: { + mixed: ["string", 42, true, { key: "value" }], + }, + expected: "https://api.example.com?mixed=string&mixed=42&mixed=true&mixed%5Bkey%5D=value", + }, + { + description: "should handle empty arrays", + baseUrl: BASE_URL, + queryParams: { emptyArray: [] }, + expected: BASE_URL, + }, + { + description: "should handle empty objects", + baseUrl: BASE_URL, + queryParams: { emptyObject: {} }, + expected: BASE_URL, + }, + { + description: "should handle special characters in keys", + baseUrl: BASE_URL, + queryParams: { "key with spaces": "value", "key[with]brackets": "value" }, + expected: "https://api.example.com?key%20with%20spaces=value&key%5Bwith%5Dbrackets=value", + }, + { + description: "should handle URL with existing query parameters", + baseUrl: "https://api.example.com?existing=param", + queryParams: { new: "value" }, + expected: "https://api.example.com?existing=param?new=value", + }, + { + description: "should handle complex nested structures", + baseUrl: BASE_URL, + queryParams: { + filters: { + status: ["active", "pending"], + category: { + type: "electronics", + subcategories: ["phones", "laptops"], + }, + }, + sort: { field: "name", direction: "asc" }, + }, + expected: + "https://api.example.com?filters%5Bstatus%5D=active&filters%5Bstatus%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", + }, + ]; + + testCases.forEach(({ description, baseUrl, queryParams, expected }) => { + it(description, () => { + expect(createRequestUrl(baseUrl, queryParams)).toBe(expected); + }); + }); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/getRequestBody.test.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/getRequestBody.test.ts new file mode 100644 index 000000000000..8a6c3a57e211 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/getRequestBody.test.ts @@ -0,0 +1,129 @@ +import { getRequestBody } from "../../../src/core/fetcher/getRequestBody"; +import { RUNTIME } from "../../../src/core/runtime"; + +describe("Test getRequestBody", () => { + interface TestCase { + description: string; + input: any; + type: "json" | "form" | "file" | "bytes" | "other"; + expected: any; + skipCondition?: () => boolean; + } + + const testCases: TestCase[] = [ + { + description: "should stringify body if not FormData in Node environment", + input: { key: "value" }, + type: "json", + expected: '{"key":"value"}', + skipCondition: () => RUNTIME.type !== "node", + }, + { + description: "should stringify body if not FormData in browser environment", + input: { key: "value" }, + type: "json", + expected: '{"key":"value"}', + skipCondition: () => RUNTIME.type !== "browser", + }, + { + description: "should return the Uint8Array", + input: new Uint8Array([1, 2, 3]), + type: "bytes", + expected: new Uint8Array([1, 2, 3]), + }, + { + description: "should serialize objects for form-urlencoded content type", + input: { username: "johndoe", email: "john@example.com" }, + type: "form", + expected: "username=johndoe&email=john%40example.com", + }, + { + description: "should serialize complex nested objects and arrays for form-urlencoded content type", + input: { + user: { + profile: { + name: "John Doe", + settings: { + theme: "dark", + notifications: true, + }, + }, + tags: ["admin", "user"], + contacts: [ + { type: "email", value: "john@example.com" }, + { type: "phone", value: "+1234567890" }, + ], + }, + filters: { + status: ["active", "pending"], + metadata: { + created: "2024-01-01", + categories: ["electronics", "books"], + }, + }, + preferences: ["notifications", "updates"], + }, + type: "form", + expected: + "user%5Bprofile%5D%5Bname%5D=John%20Doe&" + + "user%5Bprofile%5D%5Bsettings%5D%5Btheme%5D=dark&" + + "user%5Bprofile%5D%5Bsettings%5D%5Bnotifications%5D=true&" + + "user%5Btags%5D=admin&" + + "user%5Btags%5D=user&" + + "user%5Bcontacts%5D%5Btype%5D=email&" + + "user%5Bcontacts%5D%5Bvalue%5D=john%40example.com&" + + "user%5Bcontacts%5D%5Btype%5D=phone&" + + "user%5Bcontacts%5D%5Bvalue%5D=%2B1234567890&" + + "filters%5Bstatus%5D=active&" + + "filters%5Bstatus%5D=pending&" + + "filters%5Bmetadata%5D%5Bcreated%5D=2024-01-01&" + + "filters%5Bmetadata%5D%5Bcategories%5D=electronics&" + + "filters%5Bmetadata%5D%5Bcategories%5D=books&" + + "preferences=notifications&" + + "preferences=updates", + }, + { + description: "should return the input for pre-serialized form-urlencoded strings", + input: "key=value&another=param", + type: "other", + expected: "key=value&another=param", + }, + { + description: "should JSON stringify objects", + input: { key: "value" }, + type: "json", + expected: '{"key":"value"}', + }, + ]; + + testCases.forEach(({ description, input, type, expected, skipCondition }) => { + it(description, async () => { + if (skipCondition?.()) { + return; + } + + const result = await getRequestBody({ + body: input, + type, + }); + + if (input instanceof Uint8Array) { + expect(result).toBe(input); + } else { + expect(result).toBe(expected); + } + }); + }); + + it("should return FormData in browser environment", async () => { + if (RUNTIME.type === "browser") { + const formData = new FormData(); + formData.append("key", "value"); + const result = await getRequestBody({ + body: formData, + type: "file", + }); + expect(result).toBe(formData); + } + }); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/getResponseBody.test.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/getResponseBody.test.ts new file mode 100644 index 000000000000..ad6be7fc2c9b --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/getResponseBody.test.ts @@ -0,0 +1,97 @@ +import { getResponseBody } from "../../../src/core/fetcher/getResponseBody"; + +import { RUNTIME } from "../../../src/core/runtime"; + +describe("Test getResponseBody", () => { + interface SimpleTestCase { + description: string; + responseData: string | Record; + responseType?: "blob" | "sse" | "streaming" | "text"; + expected: any; + skipCondition?: () => boolean; + } + + const simpleTestCases: SimpleTestCase[] = [ + { + description: "should handle text response type", + responseData: "test text", + responseType: "text", + expected: "test text", + }, + { + description: "should handle JSON response", + responseData: { key: "value" }, + expected: { key: "value" }, + }, + { + description: "should handle empty response", + responseData: "", + expected: undefined, + }, + { + description: "should handle non-JSON response", + responseData: "invalid json", + expected: { + ok: false, + error: { + reason: "non-json", + statusCode: 200, + rawBody: "invalid json", + }, + }, + }, + ]; + + simpleTestCases.forEach(({ description, responseData, responseType, expected, skipCondition }) => { + it(description, async () => { + if (skipCondition?.()) { + return; + } + + const mockResponse = new Response( + typeof responseData === "string" ? responseData : JSON.stringify(responseData), + ); + const result = await getResponseBody(mockResponse, responseType); + expect(result).toEqual(expected); + }); + }); + + it("should handle blob response type", async () => { + const mockBlob = new Blob(["test"], { type: "text/plain" }); + const mockResponse = new Response(mockBlob); + const result = await getResponseBody(mockResponse, "blob"); + // @ts-expect-error + expect(result.constructor.name).toBe("Blob"); + }); + + it("should handle sse response type", async () => { + if (RUNTIME.type === "node") { + const mockStream = new ReadableStream(); + const mockResponse = new Response(mockStream); + const result = await getResponseBody(mockResponse, "sse"); + expect(result).toBe(mockStream); + } + }); + + it("should handle streaming response type", async () => { + const encoder = new TextEncoder(); + const testData = "test stream data"; + const mockStream = new ReadableStream({ + start(controller) { + controller.enqueue(encoder.encode(testData)); + controller.close(); + }, + }); + + const mockResponse = new Response(mockStream); + const result = (await getResponseBody(mockResponse, "streaming")) as ReadableStream; + + expect(result).toBeInstanceOf(ReadableStream); + + const reader = result.getReader(); + const decoder = new TextDecoder(); + const { value } = await reader.read(); + const streamContent = decoder.decode(value); + expect(streamContent).toBe(testData); + }); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/logging.test.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/logging.test.ts new file mode 100644 index 000000000000..366c9b6ced61 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/logging.test.ts @@ -0,0 +1,517 @@ +import { fetcherImpl } from "../../../src/core/fetcher/Fetcher"; + +function createMockLogger() { + return { + debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + }; +} + +function mockSuccessResponse(data: unknown = { data: "test" }, status = 200, statusText = "OK") { + global.fetch = vi.fn().mockResolvedValue( + new Response(JSON.stringify(data), { + status, + statusText, + }), + ); +} + +function mockErrorResponse(data: unknown = { error: "Error" }, status = 404, statusText = "Not Found") { + global.fetch = vi.fn().mockResolvedValue( + new Response(JSON.stringify(data), { + status, + statusText, + }), + ); +} + +describe("Fetcher Logging Integration", () => { + describe("Request Logging", () => { + it("should log successful request at debug level", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "POST", + headers: { "Content-Type": "application/json" }, + body: { test: "data" }, + contentType: "application/json", + requestType: "json", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + method: "POST", + url: "https://example.com/api", + headers: expect.toContainHeaders({ + "Content-Type": "application/json", + }), + hasBody: true, + }), + ); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "HTTP request succeeded", + expect.objectContaining({ + method: "POST", + url: "https://example.com/api", + statusCode: 200, + }), + ); + }); + + it("should not log debug messages at info level for successful requests", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "info", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).not.toHaveBeenCalled(); + expect(mockLogger.info).not.toHaveBeenCalled(); + }); + + it("should log request with body flag", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "POST", + body: { data: "test" }, + contentType: "application/json", + requestType: "json", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + hasBody: true, + }), + ); + }); + + it("should log request without body flag", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + hasBody: false, + }), + ); + }); + + it("should not log when silent mode is enabled", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: true, + }, + }); + + expect(mockLogger.debug).not.toHaveBeenCalled(); + expect(mockLogger.info).not.toHaveBeenCalled(); + expect(mockLogger.warn).not.toHaveBeenCalled(); + expect(mockLogger.error).not.toHaveBeenCalled(); + }); + + it("should not log when no logging config is provided", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + }); + + expect(mockLogger.debug).not.toHaveBeenCalled(); + }); + }); + + describe("Error Logging", () => { + it("should log 4xx errors at error level", async () => { + const mockLogger = createMockLogger(); + mockErrorResponse({ error: "Not found" }, 404, "Not Found"); + + const result = await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "error", + logger: mockLogger, + silent: false, + }, + }); + + expect(result.ok).toBe(false); + expect(mockLogger.error).toHaveBeenCalledWith( + "HTTP request failed with error status", + expect.objectContaining({ + method: "GET", + url: "https://example.com/api", + statusCode: 404, + }), + ); + }); + + it("should log 5xx errors at error level", async () => { + const mockLogger = createMockLogger(); + mockErrorResponse({ error: "Internal error" }, 500, "Internal Server Error"); + + const result = await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "error", + logger: mockLogger, + silent: false, + }, + }); + + expect(result.ok).toBe(false); + expect(mockLogger.error).toHaveBeenCalledWith( + "HTTP request failed with error status", + expect.objectContaining({ + method: "GET", + url: "https://example.com/api", + statusCode: 500, + }), + ); + }); + + it("should log aborted request errors", async () => { + const mockLogger = createMockLogger(); + + const abortController = new AbortController(); + abortController.abort(); + + global.fetch = vi.fn().mockRejectedValue(new Error("Aborted")); + + const result = await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + abortSignal: abortController.signal, + maxRetries: 0, + logging: { + level: "error", + logger: mockLogger, + silent: false, + }, + }); + + expect(result.ok).toBe(false); + expect(mockLogger.error).toHaveBeenCalledWith( + "HTTP request was aborted", + expect.objectContaining({ + method: "GET", + url: "https://example.com/api", + }), + ); + }); + + it("should log timeout errors", async () => { + const mockLogger = createMockLogger(); + + const timeoutError = new Error("Request timeout"); + timeoutError.name = "AbortError"; + + global.fetch = vi.fn().mockRejectedValue(timeoutError); + + const result = await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "error", + logger: mockLogger, + silent: false, + }, + }); + + expect(result.ok).toBe(false); + expect(mockLogger.error).toHaveBeenCalledWith( + "HTTP request timed out", + expect.objectContaining({ + method: "GET", + url: "https://example.com/api", + timeoutMs: undefined, + }), + ); + }); + + it("should log unknown errors", async () => { + const mockLogger = createMockLogger(); + + const unknownError = new Error("Unknown error"); + + global.fetch = vi.fn().mockRejectedValue(unknownError); + + const result = await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "error", + logger: mockLogger, + silent: false, + }, + }); + + expect(result.ok).toBe(false); + expect(mockLogger.error).toHaveBeenCalledWith( + "HTTP request failed with error", + expect.objectContaining({ + method: "GET", + url: "https://example.com/api", + errorMessage: "Unknown error", + }), + ); + }); + }); + + describe("Logging with Redaction", () => { + it("should redact sensitive data in error logs", async () => { + const mockLogger = createMockLogger(); + mockErrorResponse({ error: "Unauthorized" }, 401, "Unauthorized"); + + await fetcherImpl({ + url: "https://example.com/api?api_key=secret", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "error", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.error).toHaveBeenCalledWith( + "HTTP request failed with error status", + expect.objectContaining({ + url: "https://example.com/api?api_key=[REDACTED]", + }), + ); + }); + }); + + describe("Different HTTP Methods", () => { + it("should log GET requests", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + method: "GET", + }), + ); + }); + + it("should log POST requests", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse({ data: "test" }, 201, "Created"); + + await fetcherImpl({ + url: "https://example.com/api", + method: "POST", + body: { data: "test" }, + contentType: "application/json", + requestType: "json", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + method: "POST", + }), + ); + }); + + it("should log PUT requests", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "PUT", + body: { data: "test" }, + contentType: "application/json", + requestType: "json", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + method: "PUT", + }), + ); + }); + + it("should log DELETE requests", async () => { + const mockLogger = createMockLogger(); + global.fetch = vi.fn().mockResolvedValue( + new Response(null, { + status: 200, + statusText: "OK", + }), + ); + + await fetcherImpl({ + url: "https://example.com/api", + method: "DELETE", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + method: "DELETE", + }), + ); + }); + }); + + describe("Status Code Logging", () => { + it("should log 2xx success status codes", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse({ data: "test" }, 201, "Created"); + + await fetcherImpl({ + url: "https://example.com/api", + method: "POST", + body: { data: "test" }, + contentType: "application/json", + requestType: "json", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "HTTP request succeeded", + expect.objectContaining({ + statusCode: 201, + }), + ); + }); + + it("should log 3xx redirect status codes as success", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse({ data: "test" }, 301, "Moved Permanently"); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "HTTP request succeeded", + expect.objectContaining({ + statusCode: 301, + }), + ); + }); + }); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/makePassthroughRequest.test.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/makePassthroughRequest.test.ts new file mode 100644 index 000000000000..1850d1fda959 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/makePassthroughRequest.test.ts @@ -0,0 +1,398 @@ +import type { Mock } from "vitest"; +import { makePassthroughRequest } from "../../../src/core/fetcher/makePassthroughRequest"; + +describe("makePassthroughRequest", () => { + let mockFetch: Mock; + + beforeEach(() => { + mockFetch = vi.fn(); + mockFetch.mockResolvedValue(new Response(JSON.stringify({ ok: true }), { status: 200 })); + }); + + describe("URL resolution", () => { + it("should use absolute URL directly", async () => { + await makePassthroughRequest("https://api.example.com/v1/users", undefined, { + fetch: mockFetch, + }); + const [calledUrl] = mockFetch.mock.calls[0]; + expect(calledUrl).toBe("https://api.example.com/v1/users"); + }); + + it("should resolve relative path against baseUrl", async () => { + await makePassthroughRequest("/v1/users", undefined, { + baseUrl: "https://api.example.com", + fetch: mockFetch, + }); + const [calledUrl] = mockFetch.mock.calls[0]; + expect(calledUrl).toBe("https://api.example.com/v1/users"); + }); + + it("should resolve relative path against environment when baseUrl is not set", async () => { + await makePassthroughRequest("/v1/users", undefined, { + environment: "https://env.example.com", + fetch: mockFetch, + }); + const [calledUrl] = mockFetch.mock.calls[0]; + expect(calledUrl).toBe("https://env.example.com/v1/users"); + }); + + it("should prefer baseUrl over environment", async () => { + await makePassthroughRequest("/v1/users", undefined, { + baseUrl: "https://base.example.com", + environment: "https://env.example.com", + fetch: mockFetch, + }); + const [calledUrl] = mockFetch.mock.calls[0]; + expect(calledUrl).toBe("https://base.example.com/v1/users"); + }); + + it("should pass relative URL through as-is when no baseUrl or environment", async () => { + await makePassthroughRequest("/v1/users", undefined, { + fetch: mockFetch, + }); + const [calledUrl] = mockFetch.mock.calls[0]; + expect(calledUrl).toBe("/v1/users"); + }); + + it("should resolve baseUrl supplier", async () => { + await makePassthroughRequest("/v1/users", undefined, { + baseUrl: () => "https://dynamic.example.com", + fetch: mockFetch, + }); + const [calledUrl] = mockFetch.mock.calls[0]; + expect(calledUrl).toBe("https://dynamic.example.com/v1/users"); + }); + + it("should ignore absolute URL even when baseUrl is set", async () => { + await makePassthroughRequest("https://other.example.com/path", undefined, { + baseUrl: "https://base.example.com", + fetch: mockFetch, + }); + const [calledUrl] = mockFetch.mock.calls[0]; + expect(calledUrl).toBe("https://other.example.com/path"); + }); + + it("should accept a URL object", async () => { + await makePassthroughRequest(new URL("https://api.example.com/v1/users"), undefined, { + fetch: mockFetch, + }); + const [calledUrl] = mockFetch.mock.calls[0]; + expect(calledUrl).toBe("https://api.example.com/v1/users"); + }); + }); + + describe("header merge order", () => { + it("should merge headers in correct priority: SDK defaults < auth < init < requestOptions", async () => { + await makePassthroughRequest( + "https://api.example.com", + { + headers: { "X-Custom": "from-init", Authorization: "from-init" }, + }, + { + headers: { + "X-Custom": "from-sdk", + "X-SDK-Only": "sdk-value", + Authorization: "from-sdk", + }, + getAuthHeaders: async () => ({ + Authorization: "Bearer auth-token", + "X-Auth-Only": "auth-value", + }), + fetch: mockFetch, + }, + { + headers: { Authorization: "from-request-options" }, + }, + ); + const [, calledOptions] = mockFetch.mock.calls[0]; + const headers = calledOptions.headers; + + // requestOptions.headers wins for Authorization (highest priority) + expect(headers.authorization).toBe("from-request-options"); + // init.headers wins over SDK defaults for X-Custom + expect(headers["x-custom"]).toBe("from-init"); + // SDK-only header is preserved + expect(headers["x-sdk-only"]).toBe("sdk-value"); + // Auth-only header is preserved + expect(headers["x-auth-only"]).toBe("auth-value"); + }); + + it("should lowercase all header keys", async () => { + await makePassthroughRequest( + "https://api.example.com", + { + headers: { "Content-Type": "application/json" }, + }, + { + headers: { "X-Fern-Language": "JavaScript" }, + fetch: mockFetch, + }, + ); + const [, calledOptions] = mockFetch.mock.calls[0]; + const headers = calledOptions.headers; + expect(headers["content-type"]).toBe("application/json"); + expect(headers["x-fern-language"]).toBe("JavaScript"); + expect(headers["Content-Type"]).toBeUndefined(); + expect(headers["X-Fern-Language"]).toBeUndefined(); + }); + + it("should handle Headers object in init", async () => { + const initHeaders = new Headers(); + initHeaders.set("X-From-Headers-Object", "value"); + await makePassthroughRequest("https://api.example.com", { headers: initHeaders }, { fetch: mockFetch }); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.headers["x-from-headers-object"]).toBe("value"); + }); + + it("should handle array-style headers in init", async () => { + await makePassthroughRequest( + "https://api.example.com", + { headers: [["X-Array-Header", "array-value"]] }, + { fetch: mockFetch }, + ); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.headers["x-array-header"]).toBe("array-value"); + }); + + it("should skip null SDK default header values", async () => { + await makePassthroughRequest("https://api.example.com", undefined, { + headers: { "X-Present": "value", "X-Null": null }, + fetch: mockFetch, + }); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.headers["x-present"]).toBe("value"); + expect(calledOptions.headers["x-null"]).toBeUndefined(); + }); + }); + + describe("auth headers", () => { + it("should include auth headers when getAuthHeaders is provided", async () => { + await makePassthroughRequest("https://api.example.com", undefined, { + getAuthHeaders: async () => ({ Authorization: "Bearer my-token" }), + fetch: mockFetch, + }); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.headers.authorization).toBe("Bearer my-token"); + }); + + it("should work without auth headers", async () => { + await makePassthroughRequest("https://api.example.com", undefined, { + fetch: mockFetch, + }); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.headers.authorization).toBeUndefined(); + }); + + it("should allow init headers to override auth headers", async () => { + await makePassthroughRequest( + "https://api.example.com", + { headers: { Authorization: "Bearer override" } }, + { + getAuthHeaders: async () => ({ Authorization: "Bearer sdk-auth" }), + fetch: mockFetch, + }, + ); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.headers.authorization).toBe("Bearer override"); + }); + }); + + describe("method and body", () => { + it("should default to GET when no method specified", async () => { + await makePassthroughRequest("https://api.example.com", undefined, { + fetch: mockFetch, + }); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.method).toBe("GET"); + }); + + it("should use the method from init", async () => { + await makePassthroughRequest( + "https://api.example.com", + { method: "POST", body: JSON.stringify({ key: "value" }) }, + { fetch: mockFetch }, + ); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.method).toBe("POST"); + expect(calledOptions.body).toBe(JSON.stringify({ key: "value" })); + }); + + it("should pass body as undefined when not provided", async () => { + await makePassthroughRequest("https://api.example.com", { method: "GET" }, { fetch: mockFetch }); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.body).toBeUndefined(); + }); + }); + + describe("timeout and retries", () => { + it("should use requestOptions timeout over client timeout", async () => { + await makePassthroughRequest( + "https://api.example.com", + undefined, + { timeoutInSeconds: 30, fetch: mockFetch }, + { timeoutInSeconds: 10 }, + ); + // The timeout is passed to makeRequest which converts to ms + // We verify via the signal timing behavior (indirectly tested through makeRequest) + expect(mockFetch).toHaveBeenCalledTimes(1); + }); + + it("should use client timeout when requestOptions timeout is not set", async () => { + await makePassthroughRequest("https://api.example.com", undefined, { + timeoutInSeconds: 30, + fetch: mockFetch, + }); + expect(mockFetch).toHaveBeenCalledTimes(1); + }); + + it("should use requestOptions maxRetries over client maxRetries", async () => { + mockFetch.mockResolvedValue(new Response("", { status: 500 })); + vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + await makePassthroughRequest( + "https://api.example.com", + undefined, + { maxRetries: 5, fetch: mockFetch }, + { maxRetries: 1 }, + ); + // 1 initial + 1 retry = 2 calls + expect(mockFetch).toHaveBeenCalledTimes(2); + + vi.restoreAllMocks(); + }); + }); + + describe("abort signal", () => { + it("should use requestOptions.abortSignal over init.signal", async () => { + const initController = new AbortController(); + const requestController = new AbortController(); + + await makePassthroughRequest( + "https://api.example.com", + { signal: initController.signal }, + { fetch: mockFetch }, + { abortSignal: requestController.signal }, + ); + const [, calledOptions] = mockFetch.mock.calls[0]; + // The signal passed to makeRequest is combined with timeout signal via anySignal, + // but the requestOptions.abortSignal should be the one that's used (not init.signal) + expect(calledOptions.signal).toBeDefined(); + }); + + it("should use init.signal when requestOptions.abortSignal is not set", async () => { + const initController = new AbortController(); + + await makePassthroughRequest( + "https://api.example.com", + { signal: initController.signal }, + { fetch: mockFetch }, + ); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.signal).toBeDefined(); + }); + }); + + describe("credentials", () => { + it("should pass credentials include when set", async () => { + await makePassthroughRequest("https://api.example.com", { credentials: "include" }, { fetch: mockFetch }); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.credentials).toBe("include"); + }); + + it("should not pass credentials when not set to include", async () => { + await makePassthroughRequest( + "https://api.example.com", + { credentials: "same-origin" }, + { + fetch: mockFetch, + }, + ); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.credentials).toBeUndefined(); + }); + }); + + describe("response", () => { + it("should return the Response object from fetch", async () => { + const mockResponse = new Response(JSON.stringify({ data: "test" }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + mockFetch.mockResolvedValue(mockResponse); + + const response = await makePassthroughRequest("https://api.example.com", undefined, { + fetch: mockFetch, + }); + expect(response).toBe(mockResponse); + expect(response.status).toBe(200); + }); + + it("should return error responses without throwing", async () => { + const errorResponse = new Response("Not Found", { status: 404 }); + mockFetch.mockResolvedValue(errorResponse); + + const response = await makePassthroughRequest("https://api.example.com", undefined, { + fetch: mockFetch, + }); + expect(response.status).toBe(404); + }); + }); + + describe("Request object input", () => { + it("should extract URL from Request object", async () => { + const request = new Request("https://api.example.com/v1/resource", { method: "POST" }); + await makePassthroughRequest(request, undefined, { + fetch: mockFetch, + }); + const [calledUrl, calledOptions] = mockFetch.mock.calls[0]; + expect(calledUrl).toBe("https://api.example.com/v1/resource"); + expect(calledOptions.method).toBe("POST"); + }); + + it("should extract headers from Request object when no init provided", async () => { + const request = new Request("https://api.example.com", { + headers: { "X-From-Request": "request-value" }, + }); + await makePassthroughRequest(request, undefined, { + fetch: mockFetch, + }); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.headers["x-from-request"]).toBe("request-value"); + }); + + it("should use explicit init over Request object properties", async () => { + const request = new Request("https://api.example.com", { + method: "POST", + headers: { "X-From-Request": "request-value" }, + }); + await makePassthroughRequest( + request, + { method: "PUT", headers: { "X-From-Init": "init-value" } }, + { fetch: mockFetch }, + ); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.method).toBe("PUT"); + expect(calledOptions.headers["x-from-init"]).toBe("init-value"); + // Request headers should NOT be present since explicit init was provided + expect(calledOptions.headers["x-from-request"]).toBeUndefined(); + }); + }); + + describe("SDK default header suppliers", () => { + it("should resolve supplier functions for SDK default headers", async () => { + await makePassthroughRequest("https://api.example.com", undefined, { + headers: { + "X-Static": "static-value", + "X-Dynamic": () => "dynamic-value", + }, + fetch: mockFetch, + }); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.headers["x-static"]).toBe("static-value"); + expect(calledOptions.headers["x-dynamic"]).toBe("dynamic-value"); + }); + }); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/makeRequest.test.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/makeRequest.test.ts new file mode 100644 index 000000000000..bde194554dd8 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/makeRequest.test.ts @@ -0,0 +1,158 @@ +import type { Mock } from "vitest"; +import { + isCacheNoStoreSupported, + makeRequest, + resetCacheNoStoreSupported, +} from "../../../src/core/fetcher/makeRequest"; + +describe("Test makeRequest", () => { + const mockPostUrl = "https://httpbin.org/post"; + const mockGetUrl = "https://httpbin.org/get"; + const mockHeaders = { "Content-Type": "application/json" }; + const mockBody = JSON.stringify({ key: "value" }); + + let mockFetch: Mock; + + beforeEach(() => { + mockFetch = vi.fn(); + mockFetch.mockResolvedValue(new Response(JSON.stringify({ test: "successful" }), { status: 200 })); + resetCacheNoStoreSupported(); + }); + + it("should handle POST request correctly", async () => { + const response = await makeRequest(mockFetch, mockPostUrl, "POST", mockHeaders, mockBody); + const responseBody = await response.json(); + expect(responseBody).toEqual({ test: "successful" }); + expect(mockFetch).toHaveBeenCalledTimes(1); + const [calledUrl, calledOptions] = mockFetch.mock.calls[0]; + expect(calledUrl).toBe(mockPostUrl); + expect(calledOptions).toEqual( + expect.objectContaining({ + method: "POST", + headers: mockHeaders, + body: mockBody, + credentials: undefined, + }), + ); + expect(calledOptions.signal).toBeDefined(); + expect(calledOptions.signal).toBeInstanceOf(AbortSignal); + }); + + it("should handle GET request correctly", async () => { + const response = await makeRequest(mockFetch, mockGetUrl, "GET", mockHeaders, undefined); + const responseBody = await response.json(); + expect(responseBody).toEqual({ test: "successful" }); + expect(mockFetch).toHaveBeenCalledTimes(1); + const [calledUrl, calledOptions] = mockFetch.mock.calls[0]; + expect(calledUrl).toBe(mockGetUrl); + expect(calledOptions).toEqual( + expect.objectContaining({ + method: "GET", + headers: mockHeaders, + body: undefined, + credentials: undefined, + }), + ); + expect(calledOptions.signal).toBeDefined(); + expect(calledOptions.signal).toBeInstanceOf(AbortSignal); + }); + + it("should not include cache option when disableCache is not set", async () => { + await makeRequest(mockFetch, mockGetUrl, "GET", mockHeaders, undefined); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.cache).toBeUndefined(); + }); + + it("should not include cache option when disableCache is false", async () => { + await makeRequest( + mockFetch, + mockGetUrl, + "GET", + mockHeaders, + undefined, + undefined, + undefined, + undefined, + undefined, + false, + ); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.cache).toBeUndefined(); + }); + + it("should include cache: no-store when disableCache is true and runtime supports it", async () => { + // In Node.js test environment, Request supports the cache option + expect(isCacheNoStoreSupported()).toBe(true); + await makeRequest( + mockFetch, + mockGetUrl, + "GET", + mockHeaders, + undefined, + undefined, + undefined, + undefined, + undefined, + true, + ); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.cache).toBe("no-store"); + }); + + it("should cache the result of isCacheNoStoreSupported", () => { + const first = isCacheNoStoreSupported(); + const second = isCacheNoStoreSupported(); + expect(first).toBe(second); + }); + + it("should reset cache detection state with resetCacheNoStoreSupported", () => { + // First call caches the result + const first = isCacheNoStoreSupported(); + expect(first).toBe(true); + + // Reset clears the cache + resetCacheNoStoreSupported(); + + // After reset, it should re-detect (and still return true in Node.js) + const second = isCacheNoStoreSupported(); + expect(second).toBe(true); + }); + + it("should not include cache option when runtime does not support it (e.g. Cloudflare Workers)", async () => { + // Mock Request constructor to throw when cache option is passed, + // simulating runtimes like Cloudflare Workers + const OriginalRequest = globalThis.Request; + globalThis.Request = class MockRequest { + constructor(_url: string, init?: RequestInit) { + if (init?.cache != null) { + throw new TypeError("The 'cache' field on 'RequestInitializerDict' is not implemented."); + } + } + } as unknown as typeof Request; + + try { + // Reset so the detection runs fresh with the mocked Request + resetCacheNoStoreSupported(); + expect(isCacheNoStoreSupported()).toBe(false); + + await makeRequest( + mockFetch, + mockGetUrl, + "GET", + mockHeaders, + undefined, + undefined, + undefined, + undefined, + undefined, + true, + ); + const [, calledOptions] = mockFetch.mock.calls[0]; + expect(calledOptions.cache).toBeUndefined(); + } finally { + // Restore original Request + globalThis.Request = OriginalRequest; + resetCacheNoStoreSupported(); + } + }); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/redacting.test.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/redacting.test.ts new file mode 100644 index 000000000000..d599376b9bcf --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/redacting.test.ts @@ -0,0 +1,1115 @@ +import { fetcherImpl } from "../../../src/core/fetcher/Fetcher"; + +function createMockLogger() { + return { + debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + }; +} + +function mockSuccessResponse(data: unknown = { data: "test" }, status = 200, statusText = "OK") { + global.fetch = vi.fn().mockResolvedValue( + new Response(JSON.stringify(data), { + status, + statusText, + }), + ); +} + +describe("Redacting Logic", () => { + describe("Header Redaction", () => { + it("should redact authorization header", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { Authorization: "Bearer secret-token-12345" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + Authorization: "[REDACTED]", + }), + }), + ); + }); + + it("should redact api-key header (case-insensitive)", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { "X-API-KEY": "secret-api-key" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + "X-API-KEY": "[REDACTED]", + }), + }), + ); + }); + + it("should redact cookie header", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { Cookie: "session=abc123; token=xyz789" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + Cookie: "[REDACTED]", + }), + }), + ); + }); + + it("should redact x-auth-token header", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { "x-auth-token": "auth-token-12345" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + "x-auth-token": "[REDACTED]", + }), + }), + ); + }); + + it("should redact proxy-authorization header", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { "Proxy-Authorization": "Basic credentials" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + "Proxy-Authorization": "[REDACTED]", + }), + }), + ); + }); + + it("should redact x-csrf-token header", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { "X-CSRF-Token": "csrf-token-abc" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + "X-CSRF-Token": "[REDACTED]", + }), + }), + ); + }); + + it("should redact www-authenticate header", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { "WWW-Authenticate": "Bearer realm=example" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + "WWW-Authenticate": "[REDACTED]", + }), + }), + ); + }); + + it("should redact x-session-token header", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { "X-Session-Token": "session-token-xyz" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + "X-Session-Token": "[REDACTED]", + }), + }), + ); + }); + + it("should not redact non-sensitive headers", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { + "Content-Type": "application/json", + "User-Agent": "Test/1.0", + Accept: "application/json", + }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + "Content-Type": "application/json", + "User-Agent": "Test/1.0", + Accept: "application/json", + }), + }), + ); + }); + + it("should redact multiple sensitive headers at once", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { + Authorization: "Bearer token", + "X-API-Key": "api-key", + Cookie: "session=123", + "Content-Type": "application/json", + }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + Authorization: "[REDACTED]", + "X-API-Key": "[REDACTED]", + Cookie: "[REDACTED]", + "Content-Type": "application/json", + }), + }), + ); + }); + }); + + describe("Response Header Redaction", () => { + it("should redact Set-Cookie in response headers", async () => { + const mockLogger = createMockLogger(); + + const mockHeaders = new Headers(); + mockHeaders.set("Set-Cookie", "session=abc123; HttpOnly; Secure"); + mockHeaders.set("Content-Type", "application/json"); + + global.fetch = vi.fn().mockResolvedValue( + new Response(JSON.stringify({ data: "test" }), { + status: 200, + statusText: "OK", + headers: mockHeaders, + }), + ); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "HTTP request succeeded", + expect.objectContaining({ + responseHeaders: expect.toContainHeaders({ + "set-cookie": "[REDACTED]", + "content-type": "application/json", + }), + }), + ); + }); + + it("should redact authorization in response headers", async () => { + const mockLogger = createMockLogger(); + + const mockHeaders = new Headers(); + mockHeaders.set("Authorization", "Bearer token-123"); + mockHeaders.set("Content-Type", "application/json"); + + global.fetch = vi.fn().mockResolvedValue( + new Response(JSON.stringify({ data: "test" }), { + status: 200, + statusText: "OK", + headers: mockHeaders, + }), + ); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "HTTP request succeeded", + expect.objectContaining({ + responseHeaders: expect.toContainHeaders({ + authorization: "[REDACTED]", + "content-type": "application/json", + }), + }), + ); + }); + + it("should redact response headers in error responses", async () => { + const mockLogger = createMockLogger(); + + const mockHeaders = new Headers(); + mockHeaders.set("WWW-Authenticate", "Bearer realm=example"); + mockHeaders.set("Content-Type", "application/json"); + + global.fetch = vi.fn().mockResolvedValue( + new Response(JSON.stringify({ error: "Unauthorized" }), { + status: 401, + statusText: "Unauthorized", + headers: mockHeaders, + }), + ); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "error", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.error).toHaveBeenCalledWith( + "HTTP request failed with error status", + expect.objectContaining({ + responseHeaders: expect.toContainHeaders({ + "www-authenticate": "[REDACTED]", + "content-type": "application/json", + }), + }), + ); + }); + }); + + describe("Query Parameter Redaction", () => { + it("should redact api_key query parameter", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { api_key: "secret-key" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + api_key: "[REDACTED]", + }), + }), + ); + }); + + it("should redact token query parameter", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { token: "secret-token" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + token: "[REDACTED]", + }), + }), + ); + }); + + it("should redact access_token query parameter", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { access_token: "secret-access-token" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + access_token: "[REDACTED]", + }), + }), + ); + }); + + it("should redact password query parameter", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { password: "secret-password" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + password: "[REDACTED]", + }), + }), + ); + }); + + it("should redact secret query parameter", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { secret: "secret-value" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + secret: "[REDACTED]", + }), + }), + ); + }); + + it("should redact session_id query parameter", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { session_id: "session-123" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + session_id: "[REDACTED]", + }), + }), + ); + }); + + it("should not redact non-sensitive query parameters", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { + page: "1", + limit: "10", + sort: "name", + }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + page: "1", + limit: "10", + sort: "name", + }), + }), + ); + }); + + it("should not redact parameters containing 'auth' substring like 'author'", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { + author: "john", + authenticate: "false", + authorization_level: "user", + }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + author: "john", + authenticate: "false", + authorization_level: "user", + }), + }), + ); + }); + + it("should handle undefined query parameters", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: undefined, + }), + ); + }); + + it("should redact case-insensitive query parameters", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { API_KEY: "secret-key", Token: "secret-token" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + API_KEY: "[REDACTED]", + Token: "[REDACTED]", + }), + }), + ); + }); + }); + + describe("URL Redaction", () => { + it("should redact credentials in URL", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://user:password@example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://[REDACTED]@example.com/api", + }), + ); + }); + + it("should redact api_key in query string", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?api_key=secret-key&page=1", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?api_key=[REDACTED]&page=1", + }), + ); + }); + + it("should redact token in query string", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?token=secret-token", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?token=[REDACTED]", + }), + ); + }); + + it("should redact password in query string", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?username=user&password=secret", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?username=user&password=[REDACTED]", + }), + ); + }); + + it("should not redact non-sensitive query strings", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?page=1&limit=10&sort=name", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?page=1&limit=10&sort=name", + }), + ); + }); + + it("should not redact URL parameters containing 'auth' substring like 'author'", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?author=john&authenticate=false&page=1", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?author=john&authenticate=false&page=1", + }), + ); + }); + + it("should handle URL with fragment", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?token=secret#section", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?token=[REDACTED]#section", + }), + ); + }); + + it("should redact URL-encoded query parameters", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?api%5Fkey=secret", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?api%5Fkey=[REDACTED]", + }), + ); + }); + + it("should handle URL without query string", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api", + }), + ); + }); + + it("should handle empty query string", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?", + }), + ); + }); + + it("should redact multiple sensitive parameters in URL", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?api_key=secret1&token=secret2&page=1", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?api_key=[REDACTED]&token=[REDACTED]&page=1", + }), + ); + }); + + it("should redact both credentials and query parameters", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://user:pass@example.com/api?token=secret", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://[REDACTED]@example.com/api?token=[REDACTED]", + }), + ); + }); + + it("should use fast path for URLs without sensitive keywords", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?page=1&limit=10&sort=name&filter=value", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?page=1&limit=10&sort=name&filter=value", + }), + ); + }); + + it("should handle query parameter without value", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?flag&token=secret", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?flag&token=[REDACTED]", + }), + ); + }); + + it("should handle URL with multiple @ symbols in credentials", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://user@example.com:pass@host.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://[REDACTED]@host.com/api", + }), + ); + }); + + it("should handle URL with @ in query parameter but not in credentials", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?email=user@example.com", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?email=user@example.com", + }), + ); + }); + + it("should handle URL with both credentials and @ in path", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://user:pass@example.com/users/@username", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://[REDACTED]@example.com/users/@username", + }), + ); + }); + }); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/requestWithRetries.test.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/requestWithRetries.test.ts new file mode 100644 index 000000000000..d22661367f4e --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/requestWithRetries.test.ts @@ -0,0 +1,230 @@ +import type { Mock, MockInstance } from "vitest"; +import { requestWithRetries } from "../../../src/core/fetcher/requestWithRetries"; + +describe("requestWithRetries", () => { + let mockFetch: Mock; + let originalMathRandom: typeof Math.random; + let setTimeoutSpy: MockInstance; + + beforeEach(() => { + mockFetch = vi.fn(); + originalMathRandom = Math.random; + + Math.random = vi.fn(() => 0.5); + + vi.useFakeTimers({ + toFake: [ + "setTimeout", + "clearTimeout", + "setInterval", + "clearInterval", + "setImmediate", + "clearImmediate", + "Date", + "performance", + "requestAnimationFrame", + "cancelAnimationFrame", + "requestIdleCallback", + "cancelIdleCallback", + ], + }); + }); + + afterEach(() => { + Math.random = originalMathRandom; + vi.clearAllMocks(); + vi.clearAllTimers(); + }); + + it("should retry on retryable status codes", async () => { + setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + const retryableStatuses = [408, 429, 500, 502]; + let callCount = 0; + + mockFetch.mockImplementation(async () => { + if (callCount < retryableStatuses.length) { + return new Response("", { status: retryableStatuses[callCount++] }); + } + return new Response("", { status: 200 }); + }); + + const responsePromise = requestWithRetries(() => mockFetch(), retryableStatuses.length); + await vi.runAllTimersAsync(); + const response = await responsePromise; + + expect(mockFetch).toHaveBeenCalledTimes(retryableStatuses.length + 1); + expect(response.status).toBe(200); + }); + + it("should respect maxRetries limit", async () => { + setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + const maxRetries = 2; + mockFetch.mockResolvedValue(new Response("", { status: 500 })); + + const responsePromise = requestWithRetries(() => mockFetch(), maxRetries); + await vi.runAllTimersAsync(); + const response = await responsePromise; + + expect(mockFetch).toHaveBeenCalledTimes(maxRetries + 1); + expect(response.status).toBe(500); + }); + + it("should not retry on success status codes", async () => { + setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + const successStatuses = [200, 201, 202]; + + for (const status of successStatuses) { + mockFetch.mockReset(); + setTimeoutSpy.mockClear(); + mockFetch.mockResolvedValueOnce(new Response("", { status })); + + const responsePromise = requestWithRetries(() => mockFetch(), 3); + await vi.runAllTimersAsync(); + await responsePromise; + + expect(mockFetch).toHaveBeenCalledTimes(1); + expect(setTimeoutSpy).not.toHaveBeenCalled(); + } + }); + + interface RetryHeaderTestCase { + description: string; + headerName: string; + headerValue: string | (() => string); + expectedDelayMin: number; + expectedDelayMax: number; + } + + const retryHeaderTests: RetryHeaderTestCase[] = [ + { + description: "should respect retry-after header with seconds value", + headerName: "retry-after", + headerValue: "5", + expectedDelayMin: 4000, + expectedDelayMax: 6000, + }, + { + description: "should respect retry-after header with HTTP date value", + headerName: "retry-after", + headerValue: () => new Date(Date.now() + 3000).toUTCString(), + expectedDelayMin: 2000, + expectedDelayMax: 4000, + }, + { + description: "should respect x-ratelimit-reset header", + headerName: "x-ratelimit-reset", + headerValue: () => Math.floor((Date.now() + 4000) / 1000).toString(), + expectedDelayMin: 3000, + expectedDelayMax: 6000, + }, + ]; + + retryHeaderTests.forEach(({ description, headerName, headerValue, expectedDelayMin, expectedDelayMax }) => { + it(description, async () => { + setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + const value = typeof headerValue === "function" ? headerValue() : headerValue; + mockFetch + .mockResolvedValueOnce( + new Response("", { + status: 429, + headers: new Headers({ [headerName]: value }), + }), + ) + .mockResolvedValueOnce(new Response("", { status: 200 })); + + const responsePromise = requestWithRetries(() => mockFetch(), 1); + await vi.runAllTimersAsync(); + const response = await responsePromise; + + expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), expect.any(Number)); + const actualDelay = setTimeoutSpy.mock.calls[0][1]; + expect(actualDelay).toBeGreaterThan(expectedDelayMin); + expect(actualDelay).toBeLessThan(expectedDelayMax); + expect(response.status).toBe(200); + }); + }); + + it("should apply correct exponential backoff with jitter", async () => { + setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + mockFetch.mockResolvedValue(new Response("", { status: 500 })); + const maxRetries = 3; + const expectedDelays = [1000, 2000, 4000]; + + const responsePromise = requestWithRetries(() => mockFetch(), maxRetries); + await vi.runAllTimersAsync(); + await responsePromise; + + expect(setTimeoutSpy).toHaveBeenCalledTimes(expectedDelays.length); + + expectedDelays.forEach((delay, index) => { + expect(setTimeoutSpy).toHaveBeenNthCalledWith(index + 1, expect.any(Function), delay); + }); + + expect(mockFetch).toHaveBeenCalledTimes(maxRetries + 1); + }); + + it("should handle concurrent retries independently", async () => { + setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + mockFetch + .mockResolvedValueOnce(new Response("", { status: 500 })) + .mockResolvedValueOnce(new Response("", { status: 500 })) + .mockResolvedValueOnce(new Response("", { status: 200 })) + .mockResolvedValueOnce(new Response("", { status: 200 })); + + const promise1 = requestWithRetries(() => mockFetch(), 1); + const promise2 = requestWithRetries(() => mockFetch(), 1); + + await vi.runAllTimersAsync(); + const [response1, response2] = await Promise.all([promise1, promise2]); + + expect(response1.status).toBe(200); + expect(response2.status).toBe(200); + }); + + it("should cap delay at MAX_RETRY_DELAY for large header values", async () => { + setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + mockFetch + .mockResolvedValueOnce( + new Response("", { + status: 429, + headers: new Headers({ "retry-after": "120" }), // 120 seconds = 120000ms > MAX_RETRY_DELAY (60000ms) + }), + ) + .mockResolvedValueOnce(new Response("", { status: 200 })); + + const responsePromise = requestWithRetries(() => mockFetch(), 1); + await vi.runAllTimersAsync(); + const response = await responsePromise; + + expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), 60000); + expect(response.status).toBe(200); + }); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/signals.test.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/signals.test.ts new file mode 100644 index 000000000000..d7b6d1e63caa --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/signals.test.ts @@ -0,0 +1,69 @@ +import { anySignal, getTimeoutSignal } from "../../../src/core/fetcher/signals"; + +describe("Test getTimeoutSignal", () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it("should return an object with signal and abortId", () => { + const { signal, abortId } = getTimeoutSignal(1000); + + expect(signal).toBeDefined(); + expect(abortId).toBeDefined(); + expect(signal).toBeInstanceOf(AbortSignal); + expect(signal.aborted).toBe(false); + }); + + it("should create a signal that aborts after the specified timeout", () => { + const timeoutMs = 5000; + const { signal } = getTimeoutSignal(timeoutMs); + + expect(signal.aborted).toBe(false); + + vi.advanceTimersByTime(timeoutMs - 1); + expect(signal.aborted).toBe(false); + + vi.advanceTimersByTime(1); + expect(signal.aborted).toBe(true); + }); +}); + +describe("Test anySignal", () => { + it("should return an AbortSignal", () => { + const signal = anySignal(new AbortController().signal); + expect(signal).toBeInstanceOf(AbortSignal); + }); + + it("should abort when any of the input signals is aborted", () => { + const controller1 = new AbortController(); + const controller2 = new AbortController(); + const signal = anySignal(controller1.signal, controller2.signal); + + expect(signal.aborted).toBe(false); + controller1.abort(); + expect(signal.aborted).toBe(true); + }); + + it("should handle an array of signals", () => { + const controller1 = new AbortController(); + const controller2 = new AbortController(); + const signal = anySignal([controller1.signal, controller2.signal]); + + expect(signal.aborted).toBe(false); + controller2.abort(); + expect(signal.aborted).toBe(true); + }); + + it("should abort immediately if one of the input signals is already aborted", () => { + const controller1 = new AbortController(); + const controller2 = new AbortController(); + controller1.abort(); + + const signal = anySignal(controller1.signal, controller2.signal); + expect(signal.aborted).toBe(true); + }); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/test-file.txt b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/fetcher/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/unit/logging/logger.test.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/logging/logger.test.ts new file mode 100644 index 000000000000..2e0b5fe5040c --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/logging/logger.test.ts @@ -0,0 +1,454 @@ +import { ConsoleLogger, createLogger, Logger, LogLevel } from "../../../src/core/logging/logger"; + +function createMockLogger() { + return { + debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + }; +} + +describe("Logger", () => { + describe("LogLevel", () => { + it("should have correct log levels", () => { + expect(LogLevel.Debug).toBe("debug"); + expect(LogLevel.Info).toBe("info"); + expect(LogLevel.Warn).toBe("warn"); + expect(LogLevel.Error).toBe("error"); + }); + }); + + describe("ConsoleLogger", () => { + let consoleLogger: ConsoleLogger; + let consoleSpy: { + debug: ReturnType; + info: ReturnType; + warn: ReturnType; + error: ReturnType; + }; + + beforeEach(() => { + consoleLogger = new ConsoleLogger(); + consoleSpy = { + debug: vi.spyOn(console, "debug").mockImplementation(() => {}), + info: vi.spyOn(console, "info").mockImplementation(() => {}), + warn: vi.spyOn(console, "warn").mockImplementation(() => {}), + error: vi.spyOn(console, "error").mockImplementation(() => {}), + }; + }); + + afterEach(() => { + consoleSpy.debug.mockRestore(); + consoleSpy.info.mockRestore(); + consoleSpy.warn.mockRestore(); + consoleSpy.error.mockRestore(); + }); + + it("should log debug messages", () => { + consoleLogger.debug("debug message", { data: "test" }); + expect(consoleSpy.debug).toHaveBeenCalledWith("debug message", { data: "test" }); + }); + + it("should log info messages", () => { + consoleLogger.info("info message", { data: "test" }); + expect(consoleSpy.info).toHaveBeenCalledWith("info message", { data: "test" }); + }); + + it("should log warn messages", () => { + consoleLogger.warn("warn message", { data: "test" }); + expect(consoleSpy.warn).toHaveBeenCalledWith("warn message", { data: "test" }); + }); + + it("should log error messages", () => { + consoleLogger.error("error message", { data: "test" }); + expect(consoleSpy.error).toHaveBeenCalledWith("error message", { data: "test" }); + }); + + it("should handle multiple arguments", () => { + consoleLogger.debug("message", "arg1", "arg2", { key: "value" }); + expect(consoleSpy.debug).toHaveBeenCalledWith("message", "arg1", "arg2", { key: "value" }); + }); + }); + + describe("Logger with level filtering", () => { + let mockLogger: { + debug: ReturnType; + info: ReturnType; + warn: ReturnType; + error: ReturnType; + }; + + beforeEach(() => { + mockLogger = createMockLogger(); + }); + + describe("Debug level", () => { + it("should log all levels when set to debug", () => { + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: false, + }); + + logger.debug("debug"); + logger.info("info"); + logger.warn("warn"); + logger.error("error"); + + expect(mockLogger.debug).toHaveBeenCalledWith("debug"); + expect(mockLogger.info).toHaveBeenCalledWith("info"); + expect(mockLogger.warn).toHaveBeenCalledWith("warn"); + expect(mockLogger.error).toHaveBeenCalledWith("error"); + }); + + it("should report correct level checks", () => { + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: false, + }); + + expect(logger.isDebug()).toBe(true); + expect(logger.isInfo()).toBe(true); + expect(logger.isWarn()).toBe(true); + expect(logger.isError()).toBe(true); + }); + }); + + describe("Info level", () => { + it("should log info, warn, and error when set to info", () => { + const logger = new Logger({ + level: LogLevel.Info, + logger: mockLogger, + silent: false, + }); + + logger.debug("debug"); + logger.info("info"); + logger.warn("warn"); + logger.error("error"); + + expect(mockLogger.debug).not.toHaveBeenCalled(); + expect(mockLogger.info).toHaveBeenCalledWith("info"); + expect(mockLogger.warn).toHaveBeenCalledWith("warn"); + expect(mockLogger.error).toHaveBeenCalledWith("error"); + }); + + it("should report correct level checks", () => { + const logger = new Logger({ + level: LogLevel.Info, + logger: mockLogger, + silent: false, + }); + + expect(logger.isDebug()).toBe(false); + expect(logger.isInfo()).toBe(true); + expect(logger.isWarn()).toBe(true); + expect(logger.isError()).toBe(true); + }); + }); + + describe("Warn level", () => { + it("should log warn and error when set to warn", () => { + const logger = new Logger({ + level: LogLevel.Warn, + logger: mockLogger, + silent: false, + }); + + logger.debug("debug"); + logger.info("info"); + logger.warn("warn"); + logger.error("error"); + + expect(mockLogger.debug).not.toHaveBeenCalled(); + expect(mockLogger.info).not.toHaveBeenCalled(); + expect(mockLogger.warn).toHaveBeenCalledWith("warn"); + expect(mockLogger.error).toHaveBeenCalledWith("error"); + }); + + it("should report correct level checks", () => { + const logger = new Logger({ + level: LogLevel.Warn, + logger: mockLogger, + silent: false, + }); + + expect(logger.isDebug()).toBe(false); + expect(logger.isInfo()).toBe(false); + expect(logger.isWarn()).toBe(true); + expect(logger.isError()).toBe(true); + }); + }); + + describe("Error level", () => { + it("should only log error when set to error", () => { + const logger = new Logger({ + level: LogLevel.Error, + logger: mockLogger, + silent: false, + }); + + logger.debug("debug"); + logger.info("info"); + logger.warn("warn"); + logger.error("error"); + + expect(mockLogger.debug).not.toHaveBeenCalled(); + expect(mockLogger.info).not.toHaveBeenCalled(); + expect(mockLogger.warn).not.toHaveBeenCalled(); + expect(mockLogger.error).toHaveBeenCalledWith("error"); + }); + + it("should report correct level checks", () => { + const logger = new Logger({ + level: LogLevel.Error, + logger: mockLogger, + silent: false, + }); + + expect(logger.isDebug()).toBe(false); + expect(logger.isInfo()).toBe(false); + expect(logger.isWarn()).toBe(false); + expect(logger.isError()).toBe(true); + }); + }); + + describe("Silent mode", () => { + it("should not log anything when silent is true", () => { + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: true, + }); + + logger.debug("debug"); + logger.info("info"); + logger.warn("warn"); + logger.error("error"); + + expect(mockLogger.debug).not.toHaveBeenCalled(); + expect(mockLogger.info).not.toHaveBeenCalled(); + expect(mockLogger.warn).not.toHaveBeenCalled(); + expect(mockLogger.error).not.toHaveBeenCalled(); + }); + + it("should report all level checks as false when silent", () => { + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: true, + }); + + expect(logger.isDebug()).toBe(false); + expect(logger.isInfo()).toBe(false); + expect(logger.isWarn()).toBe(false); + expect(logger.isError()).toBe(false); + }); + }); + + describe("shouldLog", () => { + it("should correctly determine if level should be logged", () => { + const logger = new Logger({ + level: LogLevel.Info, + logger: mockLogger, + silent: false, + }); + + expect(logger.shouldLog(LogLevel.Debug)).toBe(false); + expect(logger.shouldLog(LogLevel.Info)).toBe(true); + expect(logger.shouldLog(LogLevel.Warn)).toBe(true); + expect(logger.shouldLog(LogLevel.Error)).toBe(true); + }); + + it("should return false for all levels when silent", () => { + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: true, + }); + + expect(logger.shouldLog(LogLevel.Debug)).toBe(false); + expect(logger.shouldLog(LogLevel.Info)).toBe(false); + expect(logger.shouldLog(LogLevel.Warn)).toBe(false); + expect(logger.shouldLog(LogLevel.Error)).toBe(false); + }); + }); + + describe("Multiple arguments", () => { + it("should pass multiple arguments to logger", () => { + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: false, + }); + + logger.debug("message", "arg1", { key: "value" }, 123); + expect(mockLogger.debug).toHaveBeenCalledWith("message", "arg1", { key: "value" }, 123); + }); + }); + }); + + describe("createLogger", () => { + it("should return default logger when no config provided", () => { + const logger = createLogger(); + expect(logger).toBeInstanceOf(Logger); + }); + + it("should return same logger instance when Logger is passed", () => { + const customLogger = new Logger({ + level: LogLevel.Debug, + logger: new ConsoleLogger(), + silent: false, + }); + + const result = createLogger(customLogger); + expect(result).toBe(customLogger); + }); + + it("should create logger with custom config", () => { + const mockLogger = createMockLogger(); + + const logger = createLogger({ + level: LogLevel.Warn, + logger: mockLogger, + silent: false, + }); + + expect(logger).toBeInstanceOf(Logger); + logger.warn("test"); + expect(mockLogger.warn).toHaveBeenCalledWith("test"); + }); + + it("should use default values for missing config", () => { + const logger = createLogger({}); + expect(logger).toBeInstanceOf(Logger); + }); + + it("should override default level", () => { + const mockLogger = createMockLogger(); + + const logger = createLogger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: false, + }); + + logger.debug("test"); + expect(mockLogger.debug).toHaveBeenCalledWith("test"); + }); + + it("should override default silent mode", () => { + const mockLogger = createMockLogger(); + + const logger = createLogger({ + logger: mockLogger, + silent: false, + }); + + logger.info("test"); + expect(mockLogger.info).toHaveBeenCalledWith("test"); + }); + + it("should use provided logger implementation", () => { + const customLogger = createMockLogger(); + + const logger = createLogger({ + logger: customLogger, + level: LogLevel.Debug, + silent: false, + }); + + logger.debug("test"); + expect(customLogger.debug).toHaveBeenCalledWith("test"); + }); + + it("should default to silent: true", () => { + const mockLogger = createMockLogger(); + + const logger = createLogger({ + logger: mockLogger, + level: LogLevel.Debug, + }); + + logger.debug("test"); + expect(mockLogger.debug).not.toHaveBeenCalled(); + }); + }); + + describe("Default logger", () => { + it("should have silent: true by default", () => { + const logger = createLogger(); + expect(logger.shouldLog(LogLevel.Info)).toBe(false); + }); + + it("should not log when using default logger", () => { + const logger = createLogger(); + + logger.info("test"); + expect(logger.isInfo()).toBe(false); + }); + }); + + describe("Edge cases", () => { + it("should handle empty message", () => { + const mockLogger = createMockLogger(); + + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: false, + }); + + logger.debug(""); + expect(mockLogger.debug).toHaveBeenCalledWith(""); + }); + + it("should handle no arguments", () => { + const mockLogger = createMockLogger(); + + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: false, + }); + + logger.debug("message"); + expect(mockLogger.debug).toHaveBeenCalledWith("message"); + }); + + it("should handle complex objects", () => { + const mockLogger = createMockLogger(); + + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: false, + }); + + const complexObject = { + nested: { key: "value" }, + array: [1, 2, 3], + fn: () => "test", + }; + + logger.debug("message", complexObject); + expect(mockLogger.debug).toHaveBeenCalledWith("message", complexObject); + }); + + it("should handle errors as arguments", () => { + const mockLogger = createMockLogger(); + + const logger = new Logger({ + level: LogLevel.Error, + logger: mockLogger, + silent: false, + }); + + const error = new Error("Test error"); + logger.error("Error occurred", error); + expect(mockLogger.error).toHaveBeenCalledWith("Error occurred", error); + }); + }); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/unit/url/join.test.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/url/join.test.ts new file mode 100644 index 000000000000..123488f084ea --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/url/join.test.ts @@ -0,0 +1,284 @@ +import { join } from "../../../src/core/url/index"; + +describe("join", () => { + interface TestCase { + description: string; + base: string; + segments: string[]; + expected: string; + } + + describe("basic functionality", () => { + const basicTests: TestCase[] = [ + { description: "should return empty string for empty base", base: "", segments: [], expected: "" }, + { + description: "should return empty string for empty base with path", + base: "", + segments: ["path"], + expected: "", + }, + { + description: "should handle single segment", + base: "base", + segments: ["segment"], + expected: "base/segment", + }, + { + description: "should handle single segment with trailing slash on base", + base: "base/", + segments: ["segment"], + expected: "base/segment", + }, + { + description: "should handle single segment with leading slash", + base: "base", + segments: ["/segment"], + expected: "base/segment", + }, + { + description: "should handle single segment with both slashes", + base: "base/", + segments: ["/segment"], + expected: "base/segment", + }, + { + description: "should handle multiple segments", + base: "base", + segments: ["path1", "path2", "path3"], + expected: "base/path1/path2/path3", + }, + { + description: "should handle multiple segments with slashes", + base: "base/", + segments: ["/path1/", "/path2/", "/path3/"], + expected: "base/path1/path2/path3/", + }, + ]; + + basicTests.forEach(({ description, base, segments, expected }) => { + it(description, () => { + expect(join(base, ...segments)).toBe(expected); + }); + }); + }); + + describe("URL handling", () => { + const urlTests: TestCase[] = [ + { + description: "should handle absolute URLs", + base: "https://example.com", + segments: ["api", "v1"], + expected: "https://example.com/api/v1", + }, + { + description: "should handle absolute URLs with slashes", + base: "https://example.com/", + segments: ["/api/", "/v1/"], + expected: "https://example.com/api/v1/", + }, + { + description: "should handle absolute URLs with base path", + base: "https://example.com/base", + segments: ["api", "v1"], + expected: "https://example.com/base/api/v1", + }, + { + description: "should preserve URL query parameters", + base: "https://example.com?query=1", + segments: ["api"], + expected: "https://example.com/api?query=1", + }, + { + description: "should preserve URL fragments", + base: "https://example.com#fragment", + segments: ["api"], + expected: "https://example.com/api#fragment", + }, + { + description: "should preserve URL query and fragments", + base: "https://example.com?query=1#fragment", + segments: ["api"], + expected: "https://example.com/api?query=1#fragment", + }, + { + description: "should handle http protocol", + base: "http://example.com", + segments: ["api"], + expected: "http://example.com/api", + }, + { + description: "should handle ftp protocol", + base: "ftp://example.com", + segments: ["files"], + expected: "ftp://example.com/files", + }, + { + description: "should handle ws protocol", + base: "ws://example.com", + segments: ["socket"], + expected: "ws://example.com/socket", + }, + { + description: "should fallback to path joining for malformed URLs", + base: "not-a-url://", + segments: ["path"], + expected: "not-a-url:///path", + }, + ]; + + urlTests.forEach(({ description, base, segments, expected }) => { + it(description, () => { + expect(join(base, ...segments)).toBe(expected); + }); + }); + }); + + describe("edge cases", () => { + const edgeCaseTests: TestCase[] = [ + { + description: "should handle empty segments", + base: "base", + segments: ["", "path"], + expected: "base/path", + }, + { + description: "should handle null segments", + base: "base", + segments: [null as any, "path"], + expected: "base/path", + }, + { + description: "should handle undefined segments", + base: "base", + segments: [undefined as any, "path"], + expected: "base/path", + }, + { + description: "should handle segments with only single slash", + base: "base", + segments: ["/", "path"], + expected: "base/path", + }, + { + description: "should handle segments with only double slash", + base: "base", + segments: ["//", "path"], + expected: "base/path", + }, + { + description: "should handle base paths with trailing slashes", + base: "base/", + segments: ["path"], + expected: "base/path", + }, + { + description: "should handle complex nested paths", + base: "api/v1/", + segments: ["/users/", "/123/", "/profile"], + expected: "api/v1/users/123/profile", + }, + ]; + + edgeCaseTests.forEach(({ description, base, segments, expected }) => { + it(description, () => { + expect(join(base, ...segments)).toBe(expected); + }); + }); + }); + + describe("real-world scenarios", () => { + const realWorldTests: TestCase[] = [ + { + description: "should handle API endpoint construction", + base: "https://api.example.com/v1", + segments: ["users", "123", "posts"], + expected: "https://api.example.com/v1/users/123/posts", + }, + { + description: "should handle file path construction", + base: "/var/www", + segments: ["html", "assets", "images"], + expected: "/var/www/html/assets/images", + }, + { + description: "should handle relative path construction", + base: "../parent", + segments: ["child", "grandchild"], + expected: "../parent/child/grandchild", + }, + { + description: "should handle Windows-style paths", + base: "C:\\Users", + segments: ["Documents", "file.txt"], + expected: "C:\\Users/Documents/file.txt", + }, + ]; + + realWorldTests.forEach(({ description, base, segments, expected }) => { + it(description, () => { + expect(join(base, ...segments)).toBe(expected); + }); + }); + }); + + describe("performance scenarios", () => { + it("should handle many segments efficiently", () => { + const segments = Array(100).fill("segment"); + const result = join("base", ...segments); + expect(result).toBe(`base/${segments.join("/")}`); + }); + + it("should handle long URLs", () => { + const longPath = "a".repeat(1000); + expect(join("https://example.com", longPath)).toBe(`https://example.com/${longPath}`); + }); + }); + + describe("trailing slash preservation", () => { + const trailingSlashTests: TestCase[] = [ + { + description: + "should preserve trailing slash on final result when base has trailing slash and no segments", + base: "https://api.example.com/", + segments: [], + expected: "https://api.example.com/", + }, + { + description: "should preserve trailing slash on v1 path", + base: "https://api.example.com/v1/", + segments: [], + expected: "https://api.example.com/v1/", + }, + { + description: "should preserve trailing slash when last segment has trailing slash", + base: "https://api.example.com", + segments: ["users/"], + expected: "https://api.example.com/users/", + }, + { + description: "should preserve trailing slash with relative path", + base: "api/v1", + segments: ["users/"], + expected: "api/v1/users/", + }, + { + description: "should preserve trailing slash with multiple segments", + base: "https://api.example.com", + segments: ["v1", "collections/"], + expected: "https://api.example.com/v1/collections/", + }, + { + description: "should preserve trailing slash with base path", + base: "base", + segments: ["path1", "path2/"], + expected: "base/path1/path2/", + }, + ]; + + trailingSlashTests.forEach(({ description, base, segments, expected }) => { + it(description, () => { + expect(join(base, ...segments)).toBe(expected); + }); + }); + }); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/unit/url/qs.test.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/url/qs.test.ts new file mode 100644 index 000000000000..42cdffb9e5ea --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/unit/url/qs.test.ts @@ -0,0 +1,278 @@ +import { toQueryString } from "../../../src/core/url/index"; + +describe("Test qs toQueryString", () => { + interface BasicTestCase { + description: string; + input: any; + expected: string; + } + + describe("Basic functionality", () => { + const basicTests: BasicTestCase[] = [ + { description: "should return empty string for null", input: null, expected: "" }, + { description: "should return empty string for undefined", input: undefined, expected: "" }, + { description: "should return empty string for string primitive", input: "hello", expected: "" }, + { description: "should return empty string for number primitive", input: 42, expected: "" }, + { description: "should return empty string for true boolean", input: true, expected: "" }, + { description: "should return empty string for false boolean", input: false, expected: "" }, + { description: "should handle empty objects", input: {}, expected: "" }, + { + description: "should handle simple key-value pairs", + input: { name: "John", age: 30 }, + expected: "name=John&age=30", + }, + ]; + + basicTests.forEach(({ description, input, expected }) => { + it(description, () => { + expect(toQueryString(input)).toBe(expected); + }); + }); + }); + + describe("Array handling", () => { + interface ArrayTestCase { + description: string; + input: any; + options?: { arrayFormat?: "repeat" | "indices" }; + expected: string; + } + + const arrayTests: ArrayTestCase[] = [ + { + description: "should handle arrays with indices format (default)", + input: { items: ["a", "b", "c"] }, + expected: "items%5B0%5D=a&items%5B1%5D=b&items%5B2%5D=c", + }, + { + description: "should handle arrays with repeat format", + input: { items: ["a", "b", "c"] }, + options: { arrayFormat: "repeat" }, + expected: "items=a&items=b&items=c", + }, + { + description: "should handle empty arrays", + input: { items: [] }, + expected: "", + }, + { + description: "should handle arrays with mixed types", + input: { mixed: ["string", 42, true, false] }, + expected: "mixed%5B0%5D=string&mixed%5B1%5D=42&mixed%5B2%5D=true&mixed%5B3%5D=false", + }, + { + description: "should handle arrays with objects", + input: { users: [{ name: "John" }, { name: "Jane" }] }, + expected: "users%5B0%5D%5Bname%5D=John&users%5B1%5D%5Bname%5D=Jane", + }, + { + description: "should handle arrays with objects in repeat format", + input: { users: [{ name: "John" }, { name: "Jane" }] }, + options: { arrayFormat: "repeat" }, + expected: "users%5Bname%5D=John&users%5Bname%5D=Jane", + }, + ]; + + arrayTests.forEach(({ description, input, options, expected }) => { + it(description, () => { + expect(toQueryString(input, options)).toBe(expected); + }); + }); + }); + + describe("Nested objects", () => { + const nestedTests: BasicTestCase[] = [ + { + description: "should handle nested objects", + input: { user: { name: "John", age: 30 } }, + expected: "user%5Bname%5D=John&user%5Bage%5D=30", + }, + { + description: "should handle deeply nested objects", + input: { user: { profile: { name: "John", settings: { theme: "dark" } } } }, + expected: "user%5Bprofile%5D%5Bname%5D=John&user%5Bprofile%5D%5Bsettings%5D%5Btheme%5D=dark", + }, + { + description: "should handle empty nested objects", + input: { user: {} }, + expected: "", + }, + ]; + + nestedTests.forEach(({ description, input, expected }) => { + it(description, () => { + expect(toQueryString(input)).toBe(expected); + }); + }); + }); + + describe("Encoding", () => { + interface EncodingTestCase { + description: string; + input: any; + options?: { encode?: boolean }; + expected: string; + } + + const encodingTests: EncodingTestCase[] = [ + { + description: "should encode by default", + input: { name: "John Doe", email: "john@example.com" }, + expected: "name=John%20Doe&email=john%40example.com", + }, + { + description: "should not encode when encode is false", + input: { name: "John Doe", email: "john@example.com" }, + options: { encode: false }, + expected: "name=John Doe&email=john@example.com", + }, + { + description: "should encode special characters in keys", + input: { "user name": "John", "email[primary]": "john@example.com" }, + expected: "user%20name=John&email%5Bprimary%5D=john%40example.com", + }, + { + description: "should not encode special characters in keys when encode is false", + input: { "user name": "John", "email[primary]": "john@example.com" }, + options: { encode: false }, + expected: "user name=John&email[primary]=john@example.com", + }, + ]; + + encodingTests.forEach(({ description, input, options, expected }) => { + it(description, () => { + expect(toQueryString(input, options)).toBe(expected); + }); + }); + }); + + describe("Mixed scenarios", () => { + interface MixedTestCase { + description: string; + input: any; + options?: { arrayFormat?: "repeat" | "indices" }; + expected: string; + } + + const mixedTests: MixedTestCase[] = [ + { + description: "should handle complex nested structures", + input: { + filters: { + status: ["active", "pending"], + category: { + type: "electronics", + subcategories: ["phones", "laptops"], + }, + }, + sort: { field: "name", direction: "asc" }, + }, + expected: + "filters%5Bstatus%5D%5B0%5D=active&filters%5Bstatus%5D%5B1%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D%5B0%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D%5B1%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", + }, + { + description: "should handle complex nested structures with repeat format", + input: { + filters: { + status: ["active", "pending"], + category: { + type: "electronics", + subcategories: ["phones", "laptops"], + }, + }, + sort: { field: "name", direction: "asc" }, + }, + options: { arrayFormat: "repeat" }, + expected: + "filters%5Bstatus%5D=active&filters%5Bstatus%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", + }, + { + description: "should handle arrays with null/undefined values", + input: { items: ["a", null, "c", undefined, "e"] }, + expected: "items%5B0%5D=a&items%5B1%5D=&items%5B2%5D=c&items%5B4%5D=e", + }, + { + description: "should handle objects with null/undefined values", + input: { name: "John", age: null, email: undefined, active: true }, + expected: "name=John&age=&active=true", + }, + ]; + + mixedTests.forEach(({ description, input, options, expected }) => { + it(description, () => { + expect(toQueryString(input, options)).toBe(expected); + }); + }); + }); + + describe("Edge cases", () => { + const edgeCaseTests: BasicTestCase[] = [ + { + description: "should handle numeric keys", + input: { "0": "zero", "1": "one" }, + expected: "0=zero&1=one", + }, + { + description: "should handle boolean values in objects", + input: { enabled: true, disabled: false }, + expected: "enabled=true&disabled=false", + }, + { + description: "should handle empty strings", + input: { name: "", description: "test" }, + expected: "name=&description=test", + }, + { + description: "should handle zero values", + input: { count: 0, price: 0.0 }, + expected: "count=0&price=0", + }, + { + description: "should handle arrays with empty strings", + input: { items: ["a", "", "c"] }, + expected: "items%5B0%5D=a&items%5B1%5D=&items%5B2%5D=c", + }, + ]; + + edgeCaseTests.forEach(({ description, input, expected }) => { + it(description, () => { + expect(toQueryString(input)).toBe(expected); + }); + }); + }); + + describe("Options combinations", () => { + interface OptionsTestCase { + description: string; + input: any; + options?: { arrayFormat?: "repeat" | "indices"; encode?: boolean }; + expected: string; + } + + const optionsTests: OptionsTestCase[] = [ + { + description: "should respect both arrayFormat and encode options", + input: { items: ["a & b", "c & d"] }, + options: { arrayFormat: "repeat", encode: false }, + expected: "items=a & b&items=c & d", + }, + { + description: "should use default options when none provided", + input: { items: ["a", "b"] }, + expected: "items%5B0%5D=a&items%5B1%5D=b", + }, + { + description: "should merge provided options with defaults", + input: { items: ["a", "b"], name: "John Doe" }, + options: { encode: false }, + expected: "items[0]=a&items[1]=b&name=John Doe", + }, + ]; + + optionsTests.forEach(({ description, input, options, expected }) => { + it(description, () => { + expect(toQueryString(input, options)).toBe(expected); + }); + }); + }); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/wire/.gitkeep b/seed/ts-sdk/schemaless-request-body-examples/tests/wire/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/ts-sdk/schemaless-request-body-examples/tests/wire/main.test.ts b/seed/ts-sdk/schemaless-request-body-examples/tests/wire/main.test.ts new file mode 100644 index 000000000000..f3f86fd09d1f --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tests/wire/main.test.ts @@ -0,0 +1,97 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedApiClient } from "../../src/Client"; +import { mockServerPool } from "../mock-server/MockServerPool"; + +describe("SeedApiClient", () => { + test("createPlant", async () => { + const server = mockServerPool.createServer(); + const client = new SeedApiClient({ maxRetries: 0, environment: server.baseUrl }); + const rawRequestBody = { + name: "Venus Flytrap", + species: "Dionaea muscipula", + care: { light: "full sun", water: "distilled only", humidity: "high" }, + tags: ["carnivorous", "tropical"], + }; + const rawResponseBody = { id: "plant_123", name: "Venus Flytrap" }; + + server + .mockEndpoint() + .post("/plants") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.createPlant({ + name: "Venus Flytrap", + species: "Dionaea muscipula", + care: { + light: "full sun", + water: "distilled only", + humidity: "high", + }, + tags: ["carnivorous", "tropical"], + }); + expect(response).toEqual({ + id: "plant_123", + name: "Venus Flytrap", + }); + }); + + test("updatePlant", async () => { + const server = mockServerPool.createServer(); + const client = new SeedApiClient({ maxRetries: 0, environment: server.baseUrl }); + const rawRequestBody = { name: "Updated Venus Flytrap", care: { light: "partial shade" } }; + const rawResponseBody = { id: "plant_123", name: "Updated Venus Flytrap" }; + + server + .mockEndpoint() + .put("/plants/plantId") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.updatePlant({ + plantId: "plantId", + body: { + name: "Updated Venus Flytrap", + care: { + light: "partial shade", + }, + }, + }); + expect(response).toEqual({ + id: "plant_123", + name: "Updated Venus Flytrap", + }); + }); + + test("createPlantWithSchema", async () => { + const server = mockServerPool.createServer(); + const client = new SeedApiClient({ maxRetries: 0, environment: server.baseUrl }); + const rawRequestBody = { name: "Sundew", species: "Drosera capensis" }; + const rawResponseBody = { id: "id", name: "name" }; + + server + .mockEndpoint() + .post("/plants/with-schema") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.createPlantWithSchema({ + name: "Sundew", + species: "Drosera capensis", + }); + expect(response).toEqual({ + id: "id", + name: "name", + }); + }); +}); diff --git a/seed/ts-sdk/schemaless-request-body-examples/tsconfig.base.json b/seed/ts-sdk/schemaless-request-body-examples/tsconfig.base.json new file mode 100644 index 000000000000..93a92c0630b5 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tsconfig.base.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "extendedDiagnostics": true, + "strict": true, + "target": "ES6", + "moduleResolution": "node", + "esModuleInterop": true, + "skipLibCheck": true, + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "isolatedModules": true, + "isolatedDeclarations": true + }, + "include": ["src"], + "exclude": [] +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/tsconfig.cjs.json b/seed/ts-sdk/schemaless-request-body-examples/tsconfig.cjs.json new file mode 100644 index 000000000000..5c11446f5984 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tsconfig.cjs.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "CommonJS", + "outDir": "dist/cjs" + }, + "include": ["src"], + "exclude": [] +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/tsconfig.esm.json b/seed/ts-sdk/schemaless-request-body-examples/tsconfig.esm.json new file mode 100644 index 000000000000..6ce909748b2c --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tsconfig.esm.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "esnext", + "outDir": "dist/esm", + "verbatimModuleSyntax": true + }, + "include": ["src"], + "exclude": [] +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/tsconfig.json b/seed/ts-sdk/schemaless-request-body-examples/tsconfig.json new file mode 100644 index 000000000000..d77fdf00d259 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.cjs.json" +} diff --git a/seed/ts-sdk/schemaless-request-body-examples/vitest.config.mts b/seed/ts-sdk/schemaless-request-body-examples/vitest.config.mts new file mode 100644 index 000000000000..0dee5a752d39 --- /dev/null +++ b/seed/ts-sdk/schemaless-request-body-examples/vitest.config.mts @@ -0,0 +1,32 @@ +import { defineConfig } from "vitest/config"; +export default defineConfig({ + test: { + typecheck: { + enabled: true, + tsconfig: "./tests/tsconfig.json", + }, + projects: [ + { + test: { + globals: true, + name: "unit", + environment: "node", + root: "./tests", + include: ["**/*.test.{js,ts,jsx,tsx}"], + exclude: ["wire/**"], + setupFiles: ["./setup.ts"], + }, + }, + { + test: { + globals: true, + name: "wire", + environment: "node", + root: "./tests/wire", + setupFiles: ["../setup.ts", "../mock-server/setup.ts"], + }, + }, + ], + passWithNoTests: true, + }, +}); From 1a7d3dd0f31d50d5a986daa7358a1f717484cd65 Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 7 Apr 2026 08:59:30 -0400 Subject: [PATCH 04/21] chore(java): update java-model seed (#14706) Co-authored-by: davidkonigsberg <72822263+davidkonigsberg@users.noreply.github.com> --- .../java/com/seed/pagination/model/Link.java | 138 +++++++++++ .../seed/pagination/model/UsernameCursor.java | 94 -------- .../seed/pagination/model/UsernamePage.java | 128 ----------- .../pagination/model/UsersListResponse.java | 216 ++++++++++++++++++ 4 files changed, 354 insertions(+), 222 deletions(-) create mode 100644 seed/java-model/pagination-custom/src/main/java/com/seed/pagination/model/Link.java delete mode 100644 seed/java-model/pagination-custom/src/main/java/com/seed/pagination/model/UsernameCursor.java delete mode 100644 seed/java-model/pagination-custom/src/main/java/com/seed/pagination/model/UsernamePage.java create mode 100644 seed/java-model/pagination-custom/src/main/java/com/seed/pagination/model/UsersListResponse.java diff --git a/seed/java-model/pagination-custom/src/main/java/com/seed/pagination/model/Link.java b/seed/java-model/pagination-custom/src/main/java/com/seed/pagination/model/Link.java new file mode 100644 index 000000000000..356bd3ec521a --- /dev/null +++ b/seed/java-model/pagination-custom/src/main/java/com/seed/pagination/model/Link.java @@ -0,0 +1,138 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.pagination.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.seed.pagination.core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = Link.Builder.class +) +public final class Link { + private final String rel; + + private final String method; + + private final String href; + + private Link(String rel, String method, String href) { + this.rel = rel; + this.method = method; + this.href = href; + } + + @JsonProperty("rel") + public String getRel() { + return rel; + } + + @JsonProperty("method") + public String getMethod() { + return method; + } + + @JsonProperty("href") + public String getHref() { + return href; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof Link && equalTo((Link) other); + } + + private boolean equalTo(Link other) { + return rel.equals(other.rel) && method.equals(other.method) && href.equals(other.href); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.rel, this.method, this.href); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static RelStage builder() { + return new Builder(); + } + + public interface RelStage { + MethodStage rel(String rel); + + Builder from(Link other); + } + + public interface MethodStage { + HrefStage method(String method); + } + + public interface HrefStage { + _FinalStage href(String href); + } + + public interface _FinalStage { + Link build(); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements RelStage, MethodStage, HrefStage, _FinalStage { + private String rel; + + private String method; + + private String href; + + private Builder() { + } + + @java.lang.Override + public Builder from(Link other) { + rel(other.getRel()); + method(other.getMethod()); + href(other.getHref()); + return this; + } + + @java.lang.Override + @JsonSetter("rel") + public MethodStage rel(String rel) { + this.rel = Objects.requireNonNull(rel, "rel must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("method") + public HrefStage method(String method) { + this.method = Objects.requireNonNull(method, "method must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("href") + public _FinalStage href(String href) { + this.href = Objects.requireNonNull(href, "href must not be null"); + return this; + } + + @java.lang.Override + public Link build() { + return new Link(rel, method, href); + } + } +} diff --git a/seed/java-model/pagination-custom/src/main/java/com/seed/pagination/model/UsernameCursor.java b/seed/java-model/pagination-custom/src/main/java/com/seed/pagination/model/UsernameCursor.java deleted file mode 100644 index 73d6ec18857a..000000000000 --- a/seed/java-model/pagination-custom/src/main/java/com/seed/pagination/model/UsernameCursor.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ - -package com.seed.pagination.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSetter; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.seed.pagination.core.ObjectMappers; -import java.lang.Object; -import java.lang.String; -import java.util.Objects; - -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize( - builder = UsernameCursor.Builder.class -) -public final class UsernameCursor { - private final UsernamePage cursor; - - private UsernameCursor(UsernamePage cursor) { - this.cursor = cursor; - } - - @JsonProperty("cursor") - public UsernamePage getCursor() { - return cursor; - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof UsernameCursor && equalTo((UsernameCursor) other); - } - - private boolean equalTo(UsernameCursor other) { - return cursor.equals(other.cursor); - } - - @java.lang.Override - public int hashCode() { - return Objects.hash(this.cursor); - } - - @java.lang.Override - public String toString() { - return ObjectMappers.stringify(this); - } - - public static CursorStage builder() { - return new Builder(); - } - - public interface CursorStage { - _FinalStage cursor(UsernamePage cursor); - - Builder from(UsernameCursor other); - } - - public interface _FinalStage { - UsernameCursor build(); - } - - @JsonIgnoreProperties( - ignoreUnknown = true - ) - public static final class Builder implements CursorStage, _FinalStage { - private UsernamePage cursor; - - private Builder() { - } - - @java.lang.Override - public Builder from(UsernameCursor other) { - cursor(other.getCursor()); - return this; - } - - @java.lang.Override - @JsonSetter("cursor") - public _FinalStage cursor(UsernamePage cursor) { - this.cursor = Objects.requireNonNull(cursor, "cursor must not be null"); - return this; - } - - @java.lang.Override - public UsernameCursor build() { - return new UsernameCursor(cursor); - } - } -} diff --git a/seed/java-model/pagination-custom/src/main/java/com/seed/pagination/model/UsernamePage.java b/seed/java-model/pagination-custom/src/main/java/com/seed/pagination/model/UsernamePage.java deleted file mode 100644 index 8d4f10364753..000000000000 --- a/seed/java-model/pagination-custom/src/main/java/com/seed/pagination/model/UsernamePage.java +++ /dev/null @@ -1,128 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ - -package com.seed.pagination.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSetter; -import com.fasterxml.jackson.annotation.Nulls; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.seed.pagination.core.ObjectMappers; -import java.lang.Object; -import java.lang.String; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize( - builder = UsernamePage.Builder.class -) -public final class UsernamePage { - private final Optional after; - - private final List data; - - private UsernamePage(Optional after, List data) { - this.after = after; - this.data = data; - } - - @JsonProperty("after") - public Optional getAfter() { - return after; - } - - @JsonProperty("data") - public List getData() { - return data; - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof UsernamePage && equalTo((UsernamePage) other); - } - - private boolean equalTo(UsernamePage other) { - return after.equals(other.after) && data.equals(other.data); - } - - @java.lang.Override - public int hashCode() { - return Objects.hash(this.after, this.data); - } - - @java.lang.Override - public String toString() { - return ObjectMappers.stringify(this); - } - - public static Builder builder() { - return new Builder(); - } - - @JsonIgnoreProperties( - ignoreUnknown = true - ) - public static final class Builder { - private Optional after = Optional.empty(); - - private List data = new ArrayList<>(); - - private Builder() { - } - - public Builder from(UsernamePage other) { - after(other.getAfter()); - data(other.getData()); - return this; - } - - @JsonSetter( - value = "after", - nulls = Nulls.SKIP - ) - public Builder after(Optional after) { - this.after = after; - return this; - } - - public Builder after(String after) { - this.after = Optional.ofNullable(after); - return this; - } - - @JsonSetter( - value = "data", - nulls = Nulls.SKIP - ) - public Builder data(List data) { - this.data.clear(); - if (data != null) { - this.data.addAll(data); - } - return this; - } - - public Builder addData(String data) { - this.data.add(data); - return this; - } - - public Builder addAllData(List data) { - if (data != null) { - this.data.addAll(data); - } - return this; - } - - public UsernamePage build() { - return new UsernamePage(after, data); - } - } -} diff --git a/seed/java-model/pagination-custom/src/main/java/com/seed/pagination/model/UsersListResponse.java b/seed/java-model/pagination-custom/src/main/java/com/seed/pagination/model/UsersListResponse.java new file mode 100644 index 000000000000..dc5a272088c3 --- /dev/null +++ b/seed/java-model/pagination-custom/src/main/java/com/seed/pagination/model/UsersListResponse.java @@ -0,0 +1,216 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.pagination.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.seed.pagination.core.ObjectMappers; +import java.lang.Boolean; +import java.lang.Integer; +import java.lang.Object; +import java.lang.String; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = UsersListResponse.Builder.class +) +public final class UsersListResponse { + private final Optional limit; + + private final Optional count; + + private final Optional hasMore; + + private final List links; + + private final List data; + + private UsersListResponse(Optional limit, Optional count, + Optional hasMore, List links, List data) { + this.limit = limit; + this.count = count; + this.hasMore = hasMore; + this.links = links; + this.data = data; + } + + @JsonProperty("limit") + public Optional getLimit() { + return limit; + } + + @JsonProperty("count") + public Optional getCount() { + return count; + } + + @JsonProperty("has_more") + public Optional getHasMore() { + return hasMore; + } + + @JsonProperty("links") + public List getLinks() { + return links; + } + + @JsonProperty("data") + public List getData() { + return data; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof UsersListResponse && equalTo((UsersListResponse) other); + } + + private boolean equalTo(UsersListResponse other) { + return limit.equals(other.limit) && count.equals(other.count) && hasMore.equals(other.hasMore) && links.equals(other.links) && data.equals(other.data); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.limit, this.count, this.hasMore, this.links, this.data); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder { + private Optional limit = Optional.empty(); + + private Optional count = Optional.empty(); + + private Optional hasMore = Optional.empty(); + + private List links = new ArrayList<>(); + + private List data = new ArrayList<>(); + + private Builder() { + } + + public Builder from(UsersListResponse other) { + limit(other.getLimit()); + count(other.getCount()); + hasMore(other.getHasMore()); + links(other.getLinks()); + data(other.getData()); + return this; + } + + @JsonSetter( + value = "limit", + nulls = Nulls.SKIP + ) + public Builder limit(Optional limit) { + this.limit = limit; + return this; + } + + public Builder limit(Integer limit) { + this.limit = Optional.ofNullable(limit); + return this; + } + + @JsonSetter( + value = "count", + nulls = Nulls.SKIP + ) + public Builder count(Optional count) { + this.count = count; + return this; + } + + public Builder count(Integer count) { + this.count = Optional.ofNullable(count); + return this; + } + + @JsonSetter( + value = "has_more", + nulls = Nulls.SKIP + ) + public Builder hasMore(Optional hasMore) { + this.hasMore = hasMore; + return this; + } + + public Builder hasMore(Boolean hasMore) { + this.hasMore = Optional.ofNullable(hasMore); + return this; + } + + @JsonSetter( + value = "links", + nulls = Nulls.SKIP + ) + public Builder links(List links) { + this.links.clear(); + if (links != null) { + this.links.addAll(links); + } + return this; + } + + public Builder addLinks(Link links) { + this.links.add(links); + return this; + } + + public Builder addAllLinks(List links) { + if (links != null) { + this.links.addAll(links); + } + return this; + } + + @JsonSetter( + value = "data", + nulls = Nulls.SKIP + ) + public Builder data(List data) { + this.data.clear(); + if (data != null) { + this.data.addAll(data); + } + return this; + } + + public Builder addData(String data) { + this.data.add(data); + return this; + } + + public Builder addAllData(List data) { + if (data != null) { + this.data.addAll(data); + } + return this; + } + + public UsersListResponse build() { + return new UsersListResponse(limit, count, hasMore, links, data); + } + } +} From 80db72cc68c8b623b5a2739c0bc6c8a552a415f2 Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 7 Apr 2026 08:59:32 -0400 Subject: [PATCH 05/21] chore(openapi): update openapi seed (#14698) Co-authored-by: davidkonigsberg <72822263+davidkonigsberg@users.noreply.github.com> --- seed/openapi/pagination-custom/openapi.yml | 56 +++++++++++++++------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/seed/openapi/pagination-custom/openapi.yml b/seed/openapi/pagination-custom/openapi.yml index 398e55e7ec70..bd4135077268 100644 --- a/seed/openapi/pagination-custom/openapi.yml +++ b/seed/openapi/pagination-custom/openapi.yml @@ -5,15 +5,20 @@ info: paths: /users: get: - operationId: users_listUsernamesCustom + operationId: users_listWithCustomPager tags: - Users parameters: + - name: limit + in: query + description: The maximum number of results to return. + required: false + schema: + type: integer + nullable: true - name: starting_after in: query - description: |- - The cursor used for pagination in order to fetch - the next page of results. + description: The cursor used for pagination. required: false schema: type: string @@ -24,30 +29,47 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UsernameCursor' + $ref: '#/components/schemas/UsersListResponse' components: schemas: - UsernameCursor: - title: UsernameCursor + UsersListResponse: + title: UsersListResponse type: object properties: - cursor: - $ref: '#/components/schemas/UsernamePage' - required: - - cursor - UsernamePage: - title: UsernamePage - type: object - properties: - after: - type: string + limit: + type: integer + nullable: true + count: + type: integer nullable: true + has_more: + type: boolean + nullable: true + links: + type: array + items: + $ref: '#/components/schemas/Link' data: type: array items: type: string required: + - links - data + Link: + title: Link + type: object + properties: + rel: + type: string + method: + type: string + href: + type: string + required: + - rel + - method + - href securitySchemes: Bearer: type: http From 4b43730fb0d01820af2a9afd234ac24606a3f0d6 Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:16:01 -0400 Subject: [PATCH 06/21] chore(php): update php-model seed (#14700) --- seed/php-model/pagination-custom/src/Link.php | 50 ++++++++++++++ .../pagination-custom/src/UsernameCursor.php | 34 ---------- .../pagination-custom/src/UsernamePage.php | 43 ------------ .../src/UsersListResponse.php | 67 +++++++++++++++++++ 4 files changed, 117 insertions(+), 77 deletions(-) create mode 100644 seed/php-model/pagination-custom/src/Link.php delete mode 100644 seed/php-model/pagination-custom/src/UsernameCursor.php delete mode 100644 seed/php-model/pagination-custom/src/UsernamePage.php create mode 100644 seed/php-model/pagination-custom/src/UsersListResponse.php diff --git a/seed/php-model/pagination-custom/src/Link.php b/seed/php-model/pagination-custom/src/Link.php new file mode 100644 index 000000000000..a8f4cf75eb7d --- /dev/null +++ b/seed/php-model/pagination-custom/src/Link.php @@ -0,0 +1,50 @@ +rel = $values['rel']; + $this->method = $values['method']; + $this->href = $values['href']; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->toJson(); + } +} diff --git a/seed/php-model/pagination-custom/src/UsernameCursor.php b/seed/php-model/pagination-custom/src/UsernameCursor.php deleted file mode 100644 index 486a18cd1557..000000000000 --- a/seed/php-model/pagination-custom/src/UsernameCursor.php +++ /dev/null @@ -1,34 +0,0 @@ -cursor = $values['cursor']; - } - - /** - * @return string - */ - public function __toString(): string - { - return $this->toJson(); - } -} diff --git a/seed/php-model/pagination-custom/src/UsernamePage.php b/seed/php-model/pagination-custom/src/UsernamePage.php deleted file mode 100644 index 2b60f8863692..000000000000 --- a/seed/php-model/pagination-custom/src/UsernamePage.php +++ /dev/null @@ -1,43 +0,0 @@ - $data - */ - #[JsonProperty('data'), ArrayType(['string'])] - public array $data; - - /** - * @param array{ - * data: array, - * after?: ?string, - * } $values - */ - public function __construct( - array $values, - ) { - $this->after = $values['after'] ?? null; - $this->data = $values['data']; - } - - /** - * @return string - */ - public function __toString(): string - { - return $this->toJson(); - } -} diff --git a/seed/php-model/pagination-custom/src/UsersListResponse.php b/seed/php-model/pagination-custom/src/UsersListResponse.php new file mode 100644 index 000000000000..73df04a2fe4c --- /dev/null +++ b/seed/php-model/pagination-custom/src/UsersListResponse.php @@ -0,0 +1,67 @@ + $links + */ + #[JsonProperty('links'), ArrayType([Link::class])] + public array $links; + + /** + * @var array $data + */ + #[JsonProperty('data'), ArrayType(['string'])] + public array $data; + + /** + * @param array{ + * links: array, + * data: array, + * limit?: ?int, + * count?: ?int, + * hasMore?: ?bool, + * } $values + */ + public function __construct( + array $values, + ) { + $this->limit = $values['limit'] ?? null; + $this->count = $values['count'] ?? null; + $this->hasMore = $values['hasMore'] ?? null; + $this->links = $values['links']; + $this->data = $values['data']; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->toJson(); + } +} From 50880c9de6636dbbc241a9906e37f2dbf3814d99 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 7 Apr 2026 13:17:29 +0000 Subject: [PATCH 07/21] fix(deps): update vite to 7.3.2 to address path traversal vulnerability (#14692) * [Dependabot Alert #1411] Scaffold PR for vite * fix: update vite to 7.3.2 to address path traversal vulnerability (GHSA-4w7w-66w2-5vf9) Co-Authored-By: unknown <> * fix(deps): update vite to 7.3.2 to address path traversal vulnerability Co-Authored-By: unknown <> * chore(deps): remove lodash from minimumReleaseAgeExclude and dedupe lockfile Co-Authored-By: unknown <> * fix(deps): use ^7.3.2 range for vite override instead of exact pin Co-Authored-By: unknown <> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 1009 +++++++++++++++++++++---------------------- pnpm-workspace.yaml | 3 +- 3 files changed, 485 insertions(+), 529 deletions(-) diff --git a/package.json b/package.json index 53a145432f41..25d48c01cd3c 100644 --- a/package.json +++ b/package.json @@ -157,7 +157,7 @@ "ts-essentials": "^10.1.1", "form-data": "^4.0.4", "@fern-api/ui-core-utils": "0.145.12-b50d999d1", - "vite": "^7.0.0", + "vite": "^7.3.2", "yauzl": "^3.2.1", "brace-expansion": ">=5.0.5", "path-to-regexp@~0.1.12": "0.1.13" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cf7d254eea8a..f66034dc725f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -320,7 +320,7 @@ catalogs: version: 5.0.3 esbuild: specifier: ^0.27.3 - version: 0.27.3 + version: 0.27.7 esbuild-plugin-polyfill-node: specifier: ^0.3.0 version: 0.3.0 @@ -620,7 +620,7 @@ overrides: ts-essentials: ^10.1.1 form-data: ^4.0.4 '@fern-api/ui-core-utils': 0.145.12-b50d999d1 - vite: ^7.0.0 + vite: ^7.3.2 yauzl: ^3.2.1 brace-expansion: '>=5.0.5' path-to-regexp@~0.1.12: 0.1.13 @@ -686,10 +686,10 @@ importers: version: 0.0.1(stylelint@14.16.1) stylelint-config-standard-scss: specifier: 'catalog:' - version: 5.0.0(postcss@8.5.6)(stylelint@14.16.1) + version: 5.0.0(postcss@8.5.8)(stylelint@14.16.1) tsup: specifier: 'catalog:' - version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) tsx: specifier: 'catalog:' version: 4.21.0 @@ -701,7 +701,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.2.1)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.2.1)(vite@7.3.2(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/base: dependencies: @@ -759,7 +759,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/browser-compatible-base: dependencies: @@ -793,7 +793,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/csharp/base: devDependencies: @@ -835,7 +835,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/csharp/codegen: dependencies: @@ -875,7 +875,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/csharp/dynamic-snippets: devDependencies: @@ -911,7 +911,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/csharp/formatter: dependencies: @@ -936,7 +936,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/csharp/model: devDependencies: @@ -972,7 +972,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/csharp/sdk: devDependencies: @@ -1026,7 +1026,7 @@ importers: version: 4.0.1 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/go-v2/ast: devDependencies: @@ -1050,7 +1050,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) zod: specifier: 'catalog:' version: 3.25.76 @@ -1095,7 +1095,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/go-v2/dynamic-snippets: devDependencies: @@ -1128,7 +1128,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/go-v2/formatter: dependencies: @@ -1150,7 +1150,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/go-v2/model: devDependencies: @@ -1183,7 +1183,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/go-v2/sdk: devDependencies: @@ -1234,7 +1234,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/java-v2/ast: dependencies: @@ -1259,7 +1259,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/java-v2/base: devDependencies: @@ -1292,7 +1292,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/java-v2/dynamic-snippets: devDependencies: @@ -1328,7 +1328,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/java-v2/sdk: devDependencies: @@ -1379,7 +1379,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/openapi: devDependencies: @@ -1427,7 +1427,7 @@ importers: version: 4.0.1 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/php/base: devDependencies: @@ -1469,7 +1469,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/php/codegen: dependencies: @@ -1497,7 +1497,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) zod: specifier: 'catalog:' version: 3.25.76 @@ -1536,7 +1536,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/php/model: devDependencies: @@ -1569,7 +1569,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) zod: specifier: 'catalog:' version: 3.25.76 @@ -1632,7 +1632,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) zod: specifier: 'catalog:' version: 3.25.76 @@ -1656,7 +1656,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/python-v2/base: dependencies: @@ -1702,7 +1702,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/python-v2/browser-compatible-base: devDependencies: @@ -1717,7 +1717,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) zod: specifier: 'catalog:' version: 3.25.76 @@ -1759,7 +1759,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/python-v2/formatter: devDependencies: @@ -1780,7 +1780,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/python-v2/pydantic-model: devDependencies: @@ -1816,7 +1816,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) zod: specifier: 'catalog:' version: 3.25.76 @@ -1873,7 +1873,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) zod: specifier: 'catalog:' version: 3.25.76 @@ -1901,7 +1901,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/ruby-v2/base: devDependencies: @@ -1955,7 +1955,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/ruby-v2/dynamic-snippets: devDependencies: @@ -1991,7 +1991,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/ruby-v2/model: devDependencies: @@ -2024,7 +2024,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/ruby-v2/sdk: devDependencies: @@ -2090,7 +2090,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/rust/base: dependencies: @@ -2130,7 +2130,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/rust/codegen: dependencies: @@ -2149,7 +2149,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/rust/dynamic-snippets: dependencies: @@ -2183,7 +2183,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/rust/model: devDependencies: @@ -2222,7 +2222,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) zod: specifier: 'catalog:' version: 3.25.76 @@ -2280,7 +2280,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) zod: specifier: 'catalog:' version: 3.25.76 @@ -2313,7 +2313,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/swift/codegen: dependencies: @@ -2350,7 +2350,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/swift/dynamic-snippets: devDependencies: @@ -2386,7 +2386,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/swift/model: dependencies: @@ -2429,7 +2429,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) zod: specifier: 'catalog:' version: 3.25.76 @@ -2495,7 +2495,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) zod: specifier: 'catalog:' version: 3.25.76 @@ -2523,7 +2523,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript-v2/base: devDependencies: @@ -2538,7 +2538,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.2.1)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.2.1)(vite@7.3.2(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript-v2/browser-compatible-base: devDependencies: @@ -2559,7 +2559,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.2.1)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.2.1)(vite@7.3.2(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript-v2/dynamic-snippets: devDependencies: @@ -2592,7 +2592,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript-v2/formatter: dependencies: @@ -2617,7 +2617,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/model/type-generator: dependencies: @@ -2651,13 +2651,13 @@ importers: version: 18.15.3 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/model/type-reference-converters: dependencies: @@ -2688,13 +2688,13 @@ importers: version: 18.15.3 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/model/type-reference-example-generator: dependencies: @@ -2725,13 +2725,13 @@ importers: version: 18.15.3 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/model/type-schema-generator: dependencies: @@ -2768,13 +2768,13 @@ importers: version: 18.15.3 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/model/union-generator: dependencies: @@ -2808,7 +2808,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/model/union-schema-generator: dependencies: @@ -2842,7 +2842,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/sdk/cli: devDependencies: @@ -2936,13 +2936,13 @@ importers: version: 18.15.3 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/sdk/endpoint-error-union-generator: dependencies: @@ -2979,13 +2979,13 @@ importers: version: 18.15.3 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/sdk/environments-generator: dependencies: @@ -3016,13 +3016,13 @@ importers: version: 18.15.3 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/sdk/generator: dependencies: @@ -3122,13 +3122,13 @@ importers: version: 18.15.3 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/sdk/generic-sdk-error-generators: dependencies: @@ -3156,13 +3156,13 @@ importers: version: 18.15.3 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/sdk/request-wrapper-generator: dependencies: @@ -3196,13 +3196,13 @@ importers: version: 18.15.3 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/sdk/sdk-endpoint-type-schemas-generator: dependencies: @@ -3245,13 +3245,13 @@ importers: version: 18.15.3 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/sdk/sdk-error-generator: dependencies: @@ -3282,13 +3282,13 @@ importers: version: 18.15.3 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/sdk/sdk-error-schema-generator: dependencies: @@ -3322,13 +3322,13 @@ importers: version: 18.15.3 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/sdk/sdk-inlined-request-body-schema-generator: dependencies: @@ -3359,13 +3359,13 @@ importers: version: 18.15.3 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/sdk/test-utils: dependencies: @@ -3396,7 +3396,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/sdk/websocket-type-schema-generator: dependencies: @@ -3427,13 +3427,13 @@ importers: version: 18.15.3 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/utils/abstract-error-class-generator: dependencies: @@ -3455,13 +3455,13 @@ importers: version: 18.15.3 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/utils/abstract-generator-cli: dependencies: @@ -3507,7 +3507,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/utils/abstract-schema-generator: dependencies: @@ -3532,7 +3532,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/utils/commons: dependencies: @@ -3605,7 +3605,7 @@ importers: version: 4.0.1 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) memfs: specifier: 'catalog:' version: 3.6.0 @@ -3617,7 +3617,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/utils/contexts: dependencies: @@ -3654,7 +3654,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) generators/typescript/utils/resolvers: dependencies: @@ -3676,7 +3676,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/ai: dependencies: @@ -3698,7 +3698,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/api-importers/asyncapi-to-ir: dependencies: @@ -3735,7 +3735,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/api-importers/commons: dependencies: @@ -3769,7 +3769,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/api-importers/conjure/conjure-sdk: devDependencies: @@ -3784,7 +3784,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/api-importers/conjure/conjure-to-fern: dependencies: @@ -3821,7 +3821,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/api-importers/conjure/conjure-to-fern-tests: dependencies: @@ -3846,7 +3846,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/api-importers/graphql: dependencies: @@ -3874,7 +3874,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/api-importers/openapi-pruner: dependencies: @@ -3893,7 +3893,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/api-importers/openapi-to-ir: dependencies: @@ -3939,7 +3939,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/api-importers/openapi/openapi-ir: dependencies: @@ -3961,7 +3961,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/api-importers/openapi/openapi-ir-parser: dependencies: @@ -4010,7 +4010,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/api-importers/openapi/openapi-ir-to-fern: dependencies: @@ -4056,7 +4056,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/api-importers/openapi/openapi-ir-to-fern-tests: dependencies: @@ -4096,7 +4096,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/api-importers/openrpc-to-ir: dependencies: @@ -4133,7 +4133,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/api-importers/v3-importer-commons: dependencies: @@ -4197,7 +4197,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/api-importers/v3-importer-tests: dependencies: @@ -4231,7 +4231,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/auth: dependencies: @@ -4271,7 +4271,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/cli: devDependencies: @@ -4556,7 +4556,7 @@ importers: version: 3.0.3 tsup: specifier: 'catalog:' - version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -4568,7 +4568,7 @@ importers: version: 4.0.1 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) ws: specifier: 'catalog:' version: 8.20.0 @@ -4617,7 +4617,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/cli-migrations: dependencies: @@ -4684,7 +4684,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/cli-source-resolver: dependencies: @@ -4715,7 +4715,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/cli-v2: dependencies: @@ -4905,7 +4905,7 @@ importers: version: 7.7.4 tsup: specifier: 'catalog:' - version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -4914,7 +4914,7 @@ importers: version: 9.0.1 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) yaml: specifier: 2.8.3 version: 2.8.3 @@ -4938,7 +4938,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) zod: specifier: ^4.3.6 version: 4.3.6 @@ -4975,7 +4975,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/configuration-loader: dependencies: @@ -5057,7 +5057,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/docs-importers/commons: dependencies: @@ -5091,7 +5091,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/docs-importers/mintlify: dependencies: @@ -5131,7 +5131,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/docs-importers/readme: dependencies: @@ -5207,7 +5207,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/docs-markdown-utils: dependencies: @@ -5283,7 +5283,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) zod: specifier: 'catalog:' version: 3.25.76 @@ -5389,7 +5389,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/docs-resolver: dependencies: @@ -5501,7 +5501,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/ete-tests: dependencies: @@ -5565,7 +5565,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/fern-definition/formatter: dependencies: @@ -5602,7 +5602,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/fern-definition/ir-to-jsonschema: dependencies: @@ -5657,7 +5657,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/fern-definition/schema: dependencies: @@ -5679,7 +5679,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/fern-definition/validator: dependencies: @@ -5749,7 +5749,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/generation/ir-generator: devDependencies: @@ -5812,7 +5812,7 @@ importers: version: 9.0.1 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/generation/ir-generator-tests: dependencies: @@ -5858,7 +5858,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/generation/ir-migrations: dependencies: @@ -5952,7 +5952,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/generation/local-generation/docker-utils: dependencies: @@ -5983,7 +5983,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/generation/local-generation/local-workspace-runner: dependencies: @@ -6128,7 +6128,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/generation/protoc-gen-fern: dependencies: @@ -6174,7 +6174,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/generation/protoc-gen-fern/bin: {} @@ -6318,7 +6318,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) yazl: specifier: 'catalog:' version: 3.3.1 @@ -6343,7 +6343,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/init: dependencies: @@ -6422,7 +6422,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/library-docs-generator: dependencies: @@ -6447,7 +6447,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/logger: dependencies: @@ -6466,7 +6466,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/login: dependencies: @@ -6521,7 +6521,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/mock: dependencies: @@ -6567,7 +6567,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/posthog-manager: dependencies: @@ -6604,7 +6604,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/project-loader: dependencies: @@ -6638,7 +6638,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/register: dependencies: @@ -6723,7 +6723,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/semver-utils: dependencies: @@ -6742,7 +6742,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/source: devDependencies: @@ -6760,7 +6760,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/task-context: dependencies: @@ -6779,7 +6779,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/workspace/api-workspace-validator: dependencies: @@ -6840,7 +6840,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/workspace/browser-compatible-fern-workspace: dependencies: @@ -6901,16 +6901,16 @@ importers: version: 18.15.3 esbuild: specifier: 'catalog:' - version: 0.27.3 + version: 0.27.7 tsup: specifier: 'catalog:' - version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/workspace/lazy-fern-workspace: dependencies: @@ -7058,7 +7058,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/workspace/loader: dependencies: @@ -7113,7 +7113,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/workspace/oss-validator: dependencies: @@ -7153,7 +7153,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/yaml/docs-validator: dependencies: @@ -7295,7 +7295,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/yaml/generators-validator: dependencies: @@ -7347,7 +7347,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/cli/yaml/loader: devDependencies: @@ -7374,7 +7374,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) yaml: specifier: 2.8.3 version: 2.8.3 @@ -7453,7 +7453,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/commons/casings-generator: dependencies: @@ -7484,7 +7484,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/commons/core-utils: dependencies: @@ -7551,7 +7551,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/commons/fs-utils: dependencies: @@ -7588,7 +7588,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/commons/github: dependencies: @@ -7625,7 +7625,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/commons/ir-utils: dependencies: @@ -7662,7 +7662,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/commons/loadable: dependencies: @@ -7681,7 +7681,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/commons/logging-execa: dependencies: @@ -7706,7 +7706,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/commons/mock-utils: dependencies: @@ -7728,7 +7728,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/commons/path-utils: devDependencies: @@ -7743,7 +7743,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/commons/test-utils: devDependencies: @@ -7788,22 +7788,22 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/configs: devDependencies: '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.2.1)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.2.1)(vite@7.3.2(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))) esbuild-plugin-polyfill-node: specifier: 'catalog:' - version: 0.3.0(esbuild@0.27.3) + version: 0.3.0(esbuild@0.27.7) tsup: specifier: 'catalog:' - version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.2.1)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.2.1)(vite@7.3.2(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/core: dependencies: @@ -7834,7 +7834,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/generator-cli: dependencies: @@ -7874,19 +7874,19 @@ importers: version: 17.0.35 esbuild: specifier: 'catalog:' - version: 0.27.3 + version: 0.27.7 execa: specifier: ^9.5.1 version: 9.6.1 tsup: specifier: 'catalog:' - version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.11)(vite@7.3.1(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.11)(vite@7.3.2(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) yargs: specifier: ^17.4.1 version: 17.7.2 @@ -7916,7 +7916,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/ir-sdk: devDependencies: @@ -7931,7 +7931,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/migrations-base: dependencies: @@ -7956,7 +7956,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages/scripts: devDependencies: @@ -7980,7 +7980,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) yaml: specifier: 2.8.3 version: 2.8.3 @@ -8095,13 +8095,13 @@ importers: version: 3.0.3 tsup: specifier: 'catalog:' - version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) yargs: specifier: ^17.4.1 version: 17.7.2 @@ -8143,7 +8143,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) packages: @@ -8683,8 +8683,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.28.6': - resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} '@babel/template@7.28.6': @@ -9130,158 +9130,158 @@ packages: '@epic-web/invariant@1.0.0': resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} - '@esbuild/aix-ppc64@0.27.3': - resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.27.3': - resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.27.3': - resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.27.3': - resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.27.3': - resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.27.3': - resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.27.3': - resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.3': - resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.27.3': - resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.27.3': - resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.27.3': - resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.27.3': - resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.27.3': - resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.27.3': - resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.27.3': - resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.27.3': - resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.27.3': - resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.27.3': - resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.3': - resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.27.3': - resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.3': - resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.27.3': - resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.27.3': - resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.27.3': - resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.27.3': - resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.27.3': - resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -10309,141 +10309,141 @@ packages: '@rolldown/pluginutils@1.0.0-rc.1': resolution: {integrity: sha512-UTBjtTxVOhodhzFVp/ayITaTETRHPUPYZPXQe0WU0wOgxghMojXxYjOiPOauKIYNWJAWS2fd7gJgGQK8GU8vDA==} - '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + '@rollup/rollup-android-arm-eabi@4.60.1': + resolution: {integrity: sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + '@rollup/rollup-android-arm64@4.60.1': + resolution: {integrity: sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + '@rollup/rollup-darwin-arm64@4.60.1': + resolution: {integrity: sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + '@rollup/rollup-darwin-x64@4.60.1': + resolution: {integrity: sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} + '@rollup/rollup-freebsd-arm64@4.60.1': + resolution: {integrity: sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + '@rollup/rollup-freebsd-x64@4.60.1': + resolution: {integrity: sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + '@rollup/rollup-linux-arm-gnueabihf@4.60.1': + resolution: {integrity: sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + '@rollup/rollup-linux-arm-musleabihf@4.60.1': + resolution: {integrity: sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.59.0': - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + '@rollup/rollup-linux-arm64-gnu@4.60.1': + resolution: {integrity: sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + '@rollup/rollup-linux-arm64-musl@4.60.1': + resolution: {integrity: sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + '@rollup/rollup-linux-loong64-gnu@4.60.1': + resolution: {integrity: sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} + '@rollup/rollup-linux-loong64-musl@4.60.1': + resolution: {integrity: sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + '@rollup/rollup-linux-ppc64-gnu@4.60.1': + resolution: {integrity: sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.59.0': - resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} + '@rollup/rollup-linux-ppc64-musl@4.60.1': + resolution: {integrity: sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} + '@rollup/rollup-linux-riscv64-gnu@4.60.1': + resolution: {integrity: sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.59.0': - resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} + '@rollup/rollup-linux-riscv64-musl@4.60.1': + resolution: {integrity: sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.59.0': - resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + '@rollup/rollup-linux-s390x-gnu@4.60.1': + resolution: {integrity: sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.59.0': - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + '@rollup/rollup-linux-x64-gnu@4.60.1': + resolution: {integrity: sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.59.0': - resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + '@rollup/rollup-linux-x64-musl@4.60.1': + resolution: {integrity: sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.59.0': - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} + '@rollup/rollup-openbsd-x64@4.60.1': + resolution: {integrity: sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.59.0': - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + '@rollup/rollup-openharmony-arm64@4.60.1': + resolution: {integrity: sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.59.0': - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + '@rollup/rollup-win32-arm64-msvc@4.60.1': + resolution: {integrity: sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.59.0': - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} + '@rollup/rollup-win32-ia32-msvc@4.60.1': + resolution: {integrity: sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.59.0': - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} + '@rollup/rollup-win32-x64-gnu@4.60.1': + resolution: {integrity: sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.59.0': - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} + '@rollup/rollup-win32-x64-msvc@4.60.1': + resolution: {integrity: sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==} cpu: [x64] os: [win32] @@ -10729,9 +10729,6 @@ packages: '@types/lodash-es@4.17.12': resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} - '@types/lodash@4.17.23': - resolution: {integrity: sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==} - '@types/lodash@4.17.24': resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} @@ -10803,9 +10800,6 @@ packages: '@types/qs@6.15.0': resolution: {integrity: sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==} - '@types/qs@6.9.15': - resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} - '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} @@ -10961,7 +10955,7 @@ packages: resolution: {integrity: sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==} peerDependencies: msw: ^2.4.9 - vite: ^7.0.0 + vite: ^7.3.2 peerDependenciesMeta: msw: optional: true @@ -11618,9 +11612,6 @@ packages: date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} - dayjs@1.11.19: - resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} - dayjs@1.11.20: resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} @@ -11871,8 +11862,8 @@ packages: peerDependencies: esbuild: '*' - esbuild@0.27.3: - resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} engines: {node: '>=18'} hasBin: true @@ -13008,9 +12999,6 @@ packages: mdast-util-find-and-replace@3.0.2: resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} - mdast-util-from-markdown@2.0.2: - resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} - mdast-util-from-markdown@2.0.3: resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} @@ -13662,8 +13650,8 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + postcss@8.5.8: + resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} engines: {node: ^10 || ^12 || >=14} postgres-array@2.0.0: @@ -13972,8 +13960,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.59.0: - resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} + rollup@4.60.1: + resolution: {integrity: sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -14216,10 +14204,6 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.2: - resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} - engines: {node: '>=12'} - strip-ansi@7.2.0: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} @@ -14749,8 +14733,8 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - vite@7.3.1: - resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + vite@7.3.2: + resolution: {integrity: sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -14803,7 +14787,7 @@ packages: '@vitest/ui': 4.1.0 happy-dom: '*' jsdom: '*' - vite: ^7.0.0 + vite: ^7.3.2 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -15702,7 +15686,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/runtime@7.28.6': + '@babel/runtime@7.29.2': optional: true '@babel/template@7.28.6': @@ -15789,7 +15773,7 @@ snapshots: '@blueprintjs/stylelint-plugin@4.1.18(stylelint@14.16.1)': dependencies: '@blueprintjs/colors': 5.1.13 - postcss: 8.5.6 + postcss: 8.5.8 postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 stylelint: 14.16.1 @@ -16118,82 +16102,82 @@ snapshots: '@epic-web/invariant@1.0.0': {} - '@esbuild/aix-ppc64@0.27.3': + '@esbuild/aix-ppc64@0.27.7': optional: true - '@esbuild/android-arm64@0.27.3': + '@esbuild/android-arm64@0.27.7': optional: true - '@esbuild/android-arm@0.27.3': + '@esbuild/android-arm@0.27.7': optional: true - '@esbuild/android-x64@0.27.3': + '@esbuild/android-x64@0.27.7': optional: true - '@esbuild/darwin-arm64@0.27.3': + '@esbuild/darwin-arm64@0.27.7': optional: true - '@esbuild/darwin-x64@0.27.3': + '@esbuild/darwin-x64@0.27.7': optional: true - '@esbuild/freebsd-arm64@0.27.3': + '@esbuild/freebsd-arm64@0.27.7': optional: true - '@esbuild/freebsd-x64@0.27.3': + '@esbuild/freebsd-x64@0.27.7': optional: true - '@esbuild/linux-arm64@0.27.3': + '@esbuild/linux-arm64@0.27.7': optional: true - '@esbuild/linux-arm@0.27.3': + '@esbuild/linux-arm@0.27.7': optional: true - '@esbuild/linux-ia32@0.27.3': + '@esbuild/linux-ia32@0.27.7': optional: true - '@esbuild/linux-loong64@0.27.3': + '@esbuild/linux-loong64@0.27.7': optional: true - '@esbuild/linux-mips64el@0.27.3': + '@esbuild/linux-mips64el@0.27.7': optional: true - '@esbuild/linux-ppc64@0.27.3': + '@esbuild/linux-ppc64@0.27.7': optional: true - '@esbuild/linux-riscv64@0.27.3': + '@esbuild/linux-riscv64@0.27.7': optional: true - '@esbuild/linux-s390x@0.27.3': + '@esbuild/linux-s390x@0.27.7': optional: true - '@esbuild/linux-x64@0.27.3': + '@esbuild/linux-x64@0.27.7': optional: true - '@esbuild/netbsd-arm64@0.27.3': + '@esbuild/netbsd-arm64@0.27.7': optional: true - '@esbuild/netbsd-x64@0.27.3': + '@esbuild/netbsd-x64@0.27.7': optional: true - '@esbuild/openbsd-arm64@0.27.3': + '@esbuild/openbsd-arm64@0.27.7': optional: true - '@esbuild/openbsd-x64@0.27.3': + '@esbuild/openbsd-x64@0.27.7': optional: true - '@esbuild/openharmony-arm64@0.27.3': + '@esbuild/openharmony-arm64@0.27.7': optional: true - '@esbuild/sunos-x64@0.27.3': + '@esbuild/sunos-x64@0.27.7': optional: true - '@esbuild/win32-arm64@0.27.3': + '@esbuild/win32-arm64@0.27.7': optional: true - '@esbuild/win32-ia32@0.27.3': + '@esbuild/win32-ia32@0.27.7': optional: true - '@esbuild/win32-x64@0.27.3': + '@esbuild/win32-x64@0.27.7': optional: true '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@2.6.1))': @@ -16283,7 +16267,7 @@ snapshots: '@fern-api/ui-core-utils': 0.145.12-b50d999d1 '@types/readable-stream': 4.0.23 '@ungap/structured-clone': 1.3.0 - dayjs: 1.11.19 + dayjs: 1.11.20 es-toolkit: 1.45.1 escape-string-regexp: 5.0.0 fast-deep-equal: 3.1.3 @@ -16327,7 +16311,7 @@ snapshots: dependencies: date-fns: 4.1.0 date-fns-tz: 3.2.0(date-fns@4.1.0) - strip-ansi: 7.1.2 + strip-ansi: 7.2.0 title: 3.5.3 ua-parser-js: 1.0.41 @@ -16598,7 +16582,7 @@ snapshots: dependencies: string-width: 5.1.2 string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.2 + strip-ansi: 7.2.0 strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 @@ -17372,79 +17356,79 @@ snapshots: '@rolldown/pluginutils@1.0.0-rc.1': {} - '@rollup/rollup-android-arm-eabi@4.59.0': + '@rollup/rollup-android-arm-eabi@4.60.1': optional: true - '@rollup/rollup-android-arm64@4.59.0': + '@rollup/rollup-android-arm64@4.60.1': optional: true - '@rollup/rollup-darwin-arm64@4.59.0': + '@rollup/rollup-darwin-arm64@4.60.1': optional: true - '@rollup/rollup-darwin-x64@4.59.0': + '@rollup/rollup-darwin-x64@4.60.1': optional: true - '@rollup/rollup-freebsd-arm64@4.59.0': + '@rollup/rollup-freebsd-arm64@4.60.1': optional: true - '@rollup/rollup-freebsd-x64@4.59.0': + '@rollup/rollup-freebsd-x64@4.60.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + '@rollup/rollup-linux-arm-gnueabihf@4.60.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.59.0': + '@rollup/rollup-linux-arm-musleabihf@4.60.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.59.0': + '@rollup/rollup-linux-arm64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.59.0': + '@rollup/rollup-linux-arm64-musl@4.60.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.59.0': + '@rollup/rollup-linux-loong64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-loong64-musl@4.59.0': + '@rollup/rollup-linux-loong64-musl@4.60.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.59.0': + '@rollup/rollup-linux-ppc64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-ppc64-musl@4.59.0': + '@rollup/rollup-linux-ppc64-musl@4.60.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.59.0': + '@rollup/rollup-linux-riscv64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.59.0': + '@rollup/rollup-linux-riscv64-musl@4.60.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.59.0': + '@rollup/rollup-linux-s390x-gnu@4.60.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.59.0': + '@rollup/rollup-linux-x64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-x64-musl@4.59.0': + '@rollup/rollup-linux-x64-musl@4.60.1': optional: true - '@rollup/rollup-openbsd-x64@4.59.0': + '@rollup/rollup-openbsd-x64@4.60.1': optional: true - '@rollup/rollup-openharmony-arm64@4.59.0': + '@rollup/rollup-openharmony-arm64@4.60.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.59.0': + '@rollup/rollup-win32-arm64-msvc@4.60.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.59.0': + '@rollup/rollup-win32-ia32-msvc@4.60.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.59.0': + '@rollup/rollup-win32-x64-gnu@4.60.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.59.0': + '@rollup/rollup-win32-x64-msvc@4.60.1': optional: true '@scarf/scarf@1.4.0': {} @@ -17667,7 +17651,7 @@ snapshots: '@types/express-serve-static-core@4.19.8': dependencies: '@types/node': 22.19.11 - '@types/qs': 6.9.15 + '@types/qs': 6.15.0 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -17675,7 +17659,7 @@ snapshots: dependencies: '@types/body-parser': 1.19.6 '@types/express-serve-static-core': 4.19.8 - '@types/qs': 6.9.15 + '@types/qs': 6.15.0 '@types/serve-static': 1.15.10 '@types/fast-levenshtein@0.0.4': {} @@ -17748,9 +17732,7 @@ snapshots: '@types/lodash-es@4.17.12': dependencies: - '@types/lodash': 4.17.23 - - '@types/lodash@4.17.23': {} + '@types/lodash': 4.17.24 '@types/lodash@4.17.24': {} @@ -17821,8 +17803,6 @@ snapshots: '@types/qs@6.15.0': {} - '@types/qs@6.9.15': {} - '@types/range-parser@1.2.7': {} '@types/react@19.2.13': @@ -17985,7 +17965,7 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vitest/coverage-v8@4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)))': + '@vitest/coverage-v8@4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)))': dependencies: '@bcoe/v8-coverage': 1.0.2 '@vitest/utils': 4.1.0 @@ -17997,9 +17977,9 @@ snapshots: obug: 2.1.1 std-env: 4.0.0 tinyrainbow: 3.1.0 - vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/coverage-v8@4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.2.1)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)))': + '@vitest/coverage-v8@4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.2.1)(vite@7.3.2(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)))': dependencies: '@bcoe/v8-coverage': 1.0.2 '@vitest/utils': 4.1.0 @@ -18011,7 +17991,7 @@ snapshots: obug: 2.1.1 std-env: 4.0.0 tinyrainbow: 3.1.0 - vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.2.1)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.2.1)(vite@7.3.2(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) '@vitest/expect@4.1.0': dependencies: @@ -18022,29 +18002,29 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.0(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))': + '@vitest/mocker@4.1.0(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@vitest/spy': 4.1.0 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) - '@vitest/mocker@4.1.0(vite@7.3.1(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))': + '@vitest/mocker@4.1.0(vite@7.3.2(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@vitest/spy': 4.1.0 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 7.3.2(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) - '@vitest/mocker@4.1.0(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))': + '@vitest/mocker@4.1.0(vite@7.3.2(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@vitest/spy': 4.1.0 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 7.3.2(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) '@vitest/pretty-format@4.1.0': dependencies: @@ -18202,7 +18182,7 @@ snapshots: babel-plugin-macros@3.1.0: dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 cosmiconfig: 7.1.0 resolve: 1.22.11 optional: true @@ -18374,9 +18354,9 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 - bundle-require@5.1.0(esbuild@0.27.3): + bundle-require@5.1.0(esbuild@0.27.7): dependencies: - esbuild: 0.27.3 + esbuild: 0.27.7 load-tsconfig: 0.2.5 bytes@3.1.2: {} @@ -18721,8 +18701,6 @@ snapshots: date-fns@4.1.0: {} - dayjs@1.11.19: {} - dayjs@1.11.20: {} debug@2.6.9: @@ -18945,40 +18923,40 @@ snapshots: esast-util-from-estree: 2.0.0 vfile-message: 4.0.3 - esbuild-plugin-polyfill-node@0.3.0(esbuild@0.27.3): + esbuild-plugin-polyfill-node@0.3.0(esbuild@0.27.7): dependencies: '@jspm/core': 2.1.0 - esbuild: 0.27.3 + esbuild: 0.27.7 import-meta-resolve: 3.1.1 - esbuild@0.27.3: + esbuild@0.27.7: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.3 - '@esbuild/android-arm': 0.27.3 - '@esbuild/android-arm64': 0.27.3 - '@esbuild/android-x64': 0.27.3 - '@esbuild/darwin-arm64': 0.27.3 - '@esbuild/darwin-x64': 0.27.3 - '@esbuild/freebsd-arm64': 0.27.3 - '@esbuild/freebsd-x64': 0.27.3 - '@esbuild/linux-arm': 0.27.3 - '@esbuild/linux-arm64': 0.27.3 - '@esbuild/linux-ia32': 0.27.3 - '@esbuild/linux-loong64': 0.27.3 - '@esbuild/linux-mips64el': 0.27.3 - '@esbuild/linux-ppc64': 0.27.3 - '@esbuild/linux-riscv64': 0.27.3 - '@esbuild/linux-s390x': 0.27.3 - '@esbuild/linux-x64': 0.27.3 - '@esbuild/netbsd-arm64': 0.27.3 - '@esbuild/netbsd-x64': 0.27.3 - '@esbuild/openbsd-arm64': 0.27.3 - '@esbuild/openbsd-x64': 0.27.3 - '@esbuild/openharmony-arm64': 0.27.3 - '@esbuild/sunos-x64': 0.27.3 - '@esbuild/win32-arm64': 0.27.3 - '@esbuild/win32-ia32': 0.27.3 - '@esbuild/win32-x64': 0.27.3 + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 escalade@3.2.0: {} @@ -19328,7 +19306,7 @@ snapshots: dependencies: magic-string: 0.30.21 mlly: 1.8.0 - rollup: 4.59.0 + rollup: 4.60.1 flat-cache@3.2.0: dependencies: @@ -20254,23 +20232,6 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 - mdast-util-from-markdown@2.0.2: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - decode-named-character-reference: 1.3.0 - devlop: 1.1.0 - mdast-util-to-string: 4.0.0 - micromark: 4.0.2 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-decode-string: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - unist-util-stringify-position: 4.0.0 - transitivePeerDependencies: - - supports-color - mdast-util-from-markdown@2.0.3: dependencies: '@types/mdast': 4.0.4 @@ -20376,7 +20337,7 @@ snapshots: '@types/unist': 3.0.3 ccount: 2.0.1 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 parse-entities: 4.0.2 stringify-entities: 4.0.4 @@ -20985,7 +20946,7 @@ snapshots: log-symbols: 5.1.0 stdin-discarder: 0.1.0 string-width: 6.1.0 - strip-ansi: 7.1.2 + strip-ansi: 7.2.0 oxc-resolver@11.18.0: optionalDependencies: @@ -21177,12 +21138,12 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(yaml@2.8.3): + postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(yaml@2.8.3): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 2.6.1 - postcss: 8.5.6 + postcss: 8.5.8 tsx: 4.21.0 yaml: 2.8.3 @@ -21190,17 +21151,17 @@ snapshots: postcss-resolve-nested-selector@0.1.6: {} - postcss-safe-parser@6.0.0(postcss@8.5.6): + postcss-safe-parser@6.0.0(postcss@8.5.8): dependencies: - postcss: 8.5.6 + postcss: 8.5.8 - postcss-safe-parser@7.0.1(postcss@8.5.6): + postcss-safe-parser@7.0.1(postcss@8.5.8): dependencies: - postcss: 8.5.6 + postcss: 8.5.8 - postcss-scss@4.0.9(postcss@8.5.6): + postcss-scss@4.0.9(postcss@8.5.8): dependencies: - postcss: 8.5.6 + postcss: 8.5.8 postcss-selector-parser@6.1.2: dependencies: @@ -21214,7 +21175,7 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss@8.5.6: + postcss@8.5.8: dependencies: nanoid: 3.3.8 picocolors: 1.1.1 @@ -21530,7 +21491,7 @@ snapshots: remark-parse@11.0.0: dependencies: '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.3 micromark-util-types: 2.0.2 unified: 11.0.5 transitivePeerDependencies: @@ -21624,35 +21585,35 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.1 '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.1 - rollup@4.59.0: + rollup@4.60.1: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.59.0 - '@rollup/rollup-android-arm64': 4.59.0 - '@rollup/rollup-darwin-arm64': 4.59.0 - '@rollup/rollup-darwin-x64': 4.59.0 - '@rollup/rollup-freebsd-arm64': 4.59.0 - '@rollup/rollup-freebsd-x64': 4.59.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 - '@rollup/rollup-linux-arm-musleabihf': 4.59.0 - '@rollup/rollup-linux-arm64-gnu': 4.59.0 - '@rollup/rollup-linux-arm64-musl': 4.59.0 - '@rollup/rollup-linux-loong64-gnu': 4.59.0 - '@rollup/rollup-linux-loong64-musl': 4.59.0 - '@rollup/rollup-linux-ppc64-gnu': 4.59.0 - '@rollup/rollup-linux-ppc64-musl': 4.59.0 - '@rollup/rollup-linux-riscv64-gnu': 4.59.0 - '@rollup/rollup-linux-riscv64-musl': 4.59.0 - '@rollup/rollup-linux-s390x-gnu': 4.59.0 - '@rollup/rollup-linux-x64-gnu': 4.59.0 - '@rollup/rollup-linux-x64-musl': 4.59.0 - '@rollup/rollup-openbsd-x64': 4.59.0 - '@rollup/rollup-openharmony-arm64': 4.59.0 - '@rollup/rollup-win32-arm64-msvc': 4.59.0 - '@rollup/rollup-win32-ia32-msvc': 4.59.0 - '@rollup/rollup-win32-x64-gnu': 4.59.0 - '@rollup/rollup-win32-x64-msvc': 4.59.0 + '@rollup/rollup-android-arm-eabi': 4.60.1 + '@rollup/rollup-android-arm64': 4.60.1 + '@rollup/rollup-darwin-arm64': 4.60.1 + '@rollup/rollup-darwin-x64': 4.60.1 + '@rollup/rollup-freebsd-arm64': 4.60.1 + '@rollup/rollup-freebsd-x64': 4.60.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.1 + '@rollup/rollup-linux-arm-musleabihf': 4.60.1 + '@rollup/rollup-linux-arm64-gnu': 4.60.1 + '@rollup/rollup-linux-arm64-musl': 4.60.1 + '@rollup/rollup-linux-loong64-gnu': 4.60.1 + '@rollup/rollup-linux-loong64-musl': 4.60.1 + '@rollup/rollup-linux-ppc64-gnu': 4.60.1 + '@rollup/rollup-linux-ppc64-musl': 4.60.1 + '@rollup/rollup-linux-riscv64-gnu': 4.60.1 + '@rollup/rollup-linux-riscv64-musl': 4.60.1 + '@rollup/rollup-linux-s390x-gnu': 4.60.1 + '@rollup/rollup-linux-x64-gnu': 4.60.1 + '@rollup/rollup-linux-x64-musl': 4.60.1 + '@rollup/rollup-openbsd-x64': 4.60.1 + '@rollup/rollup-openharmony-arm64': 4.60.1 + '@rollup/rollup-win32-arm64-msvc': 4.60.1 + '@rollup/rollup-win32-ia32-msvc': 4.60.1 + '@rollup/rollup-win32-x64-gnu': 4.60.1 + '@rollup/rollup-win32-x64-msvc': 4.60.1 fsevents: 2.3.3 run-async@3.0.0: {} @@ -21907,13 +21868,13 @@ snapshots: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.1.2 + strip-ansi: 7.2.0 string-width@6.1.0: dependencies: eastasianwidth: 0.2.0 emoji-regex: 10.6.0 - strip-ansi: 7.1.2 + strip-ansi: 7.2.0 string_decoder@1.1.1: dependencies: @@ -21938,10 +21899,6 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.2: - dependencies: - ansi-regex: 6.2.2 - strip-ansi@7.2.0: dependencies: ansi-regex: 6.2.2 @@ -21993,9 +21950,9 @@ snapshots: dependencies: stylelint: 14.16.1 - stylelint-config-recommended-scss@7.0.0(postcss@8.5.6)(stylelint@14.16.1): + stylelint-config-recommended-scss@7.0.0(postcss@8.5.8)(stylelint@14.16.1): dependencies: - postcss-scss: 4.0.9(postcss@8.5.6) + postcss-scss: 4.0.9(postcss@8.5.8) stylelint: 14.16.1 stylelint-config-recommended: 8.0.0(stylelint@14.16.1) stylelint-scss: 4.7.0(stylelint@14.16.1) @@ -22006,10 +21963,10 @@ snapshots: dependencies: stylelint: 14.16.1 - stylelint-config-standard-scss@5.0.0(postcss@8.5.6)(stylelint@14.16.1): + stylelint-config-standard-scss@5.0.0(postcss@8.5.8)(stylelint@14.16.1): dependencies: stylelint: 14.16.1 - stylelint-config-recommended-scss: 7.0.0(postcss@8.5.6)(stylelint@14.16.1) + stylelint-config-recommended-scss: 7.0.0(postcss@8.5.8)(stylelint@14.16.1) stylelint-config-standard: 26.0.0(stylelint@14.16.1) transitivePeerDependencies: - postcss @@ -22052,10 +22009,10 @@ snapshots: micromatch: 4.0.8 normalize-path: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.6 + postcss: 8.5.8 postcss-media-query-parser: 0.2.3 postcss-resolve-nested-selector: 0.1.6 - postcss-safe-parser: 6.0.0(postcss@8.5.6) + postcss-safe-parser: 6.0.0(postcss@8.5.8) postcss-selector-parser: 6.1.2 postcss-value-parser: 4.2.0 resolve-from: 5.0.0 @@ -22100,9 +22057,9 @@ snapshots: micromatch: 4.0.8 normalize-path: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.6 + postcss: 8.5.8 postcss-resolve-nested-selector: 0.1.6 - postcss-safe-parser: 7.0.1(postcss@8.5.6) + postcss-safe-parser: 7.0.1(postcss@8.5.8) postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 resolve-from: 5.0.0 @@ -22349,27 +22306,27 @@ snapshots: tslib@2.8.1: {} - tsup@8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3): + tsup@8.5.1(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3): dependencies: - bundle-require: 5.1.0(esbuild@0.27.3) + bundle-require: 5.1.0(esbuild@0.27.7) cac: 6.7.14 chokidar: 4.0.3 consola: 3.4.2 debug: 4.4.3 - esbuild: 0.27.3 + esbuild: 0.27.7 fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(yaml@2.8.3) + postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(yaml@2.8.3) resolve-from: 5.0.0 - rollup: 4.59.0 + rollup: 4.60.1 source-map: 0.7.6 sucrase: 3.35.1 tinyexec: 0.3.2 tinyglobby: 0.2.15 tree-kill: 1.2.2 optionalDependencies: - postcss: 8.5.6 + postcss: 8.5.8 typescript: 5.9.3 transitivePeerDependencies: - jiti @@ -22379,7 +22336,7 @@ snapshots: tsx@4.21.0: dependencies: - esbuild: 0.27.3 + esbuild: 0.27.7 get-tsconfig: 4.13.1 optionalDependencies: fsevents: 2.3.3 @@ -22571,13 +22528,13 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3): + vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3): dependencies: - esbuild: 0.27.3 + esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.6 - rollup: 4.59.0 + postcss: 8.5.8 + rollup: 4.60.1 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 18.15.3 @@ -22586,13 +22543,13 @@ snapshots: tsx: 4.21.0 yaml: 2.8.3 - vite@7.3.1(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3): + vite@7.3.2(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3): dependencies: - esbuild: 0.27.3 + esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.6 - rollup: 4.59.0 + postcss: 8.5.8 + rollup: 4.60.1 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 22.19.11 @@ -22601,13 +22558,13 @@ snapshots: tsx: 4.21.0 yaml: 2.8.3 - vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3): + vite@7.3.2(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3): dependencies: - esbuild: 0.27.3 + esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.6 - rollup: 4.59.0 + postcss: 8.5.8 + rollup: 4.60.1 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 25.2.1 @@ -22616,10 +22573,10 @@ snapshots: tsx: 4.21.0 yaml: 2.8.3 - vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)): + vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@18.15.3)(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/mocker': 4.1.0(vite@7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) '@vitest/pretty-format': 4.1.0 '@vitest/runner': 4.1.0 '@vitest/snapshot': 4.1.0 @@ -22636,7 +22593,7 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.1.0 - vite: 7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 7.3.2(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 @@ -22644,10 +22601,10 @@ snapshots: transitivePeerDependencies: - msw - vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.11)(vite@7.3.1(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)): + vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.11)(vite@7.3.2(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@7.3.1(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/mocker': 4.1.0(vite@7.3.2(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) '@vitest/pretty-format': 4.1.0 '@vitest/runner': 4.1.0 '@vitest/snapshot': 4.1.0 @@ -22664,7 +22621,7 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.1.0 - vite: 7.3.1(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 7.3.2(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 @@ -22672,10 +22629,10 @@ snapshots: transitivePeerDependencies: - msw - vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.2.1)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)): + vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.2.1)(vite@7.3.2(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/mocker': 4.1.0(vite@7.3.2(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) '@vitest/pretty-format': 4.1.0 '@vitest/runner': 4.1.0 '@vitest/snapshot': 4.1.0 @@ -22692,7 +22649,7 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.1.0 - vite: 7.3.1(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 7.3.2(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 @@ -22776,7 +22733,7 @@ snapshots: dependencies: ansi-styles: 6.2.3 string-width: 5.1.2 - strip-ansi: 7.1.2 + strip-ansi: 7.2.0 wrappy@1.0.2: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 27e63e1c53b3..d57c666a61be 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -28,8 +28,7 @@ minimumReleaseAge: 1440 minimumReleaseAgeExclude: - "@fern-api/*" - "@fern-fern/*" - - "lodash-es" - - "lodash" + - "vite" # Prevent trust level downgrades (e.g. a previously trusted-publisher package # losing its attestation). Catches compromised maintainer accounts. From 95fdcc0babbea144f575fe81ff11e48b83e7e54c Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:18:33 -0400 Subject: [PATCH 08/21] chore(csharp): update csharp-sdk seed (#14696) Co-authored-by: davidkonigsberg <72822263+davidkonigsberg@users.noreply.github.com> --- .../basic-auth-pw-omitted/.fern/metadata.json | 2 +- .../.fern/metadata.json | 2 +- seed/csharp-sdk/pagination-custom/README.md | 22 +++++++++---------- .../csharp-sdk/pagination-custom/reference.md | 8 +++---- .../csharp-sdk/pagination-custom/snippet.json | 4 ++-- .../src/SeedApi.DynamicSnippets/Example0.cs | 5 +++-- .../Types/{UsernamePage.cs => Link.cs} | 13 ++++++----- ...UsernameCursor.cs => UsersListResponse.cs} | 18 ++++++++++++--- .../src/SeedPagination/Users/IUsersClient.cs | 4 ++-- ...ustom.cs => ListWithCustomPagerRequest.cs} | 11 +++++++--- .../src/SeedPagination/Users/UsersClient.cs | 11 +++++----- 11 files changed, 61 insertions(+), 39 deletions(-) rename seed/csharp-sdk/pagination-custom/src/SeedPagination/Types/{UsernamePage.cs => Link.cs} (69%) rename seed/csharp-sdk/pagination-custom/src/SeedPagination/Types/{UsernameCursor.cs => UsersListResponse.cs} (57%) rename seed/csharp-sdk/pagination-custom/src/SeedPagination/Users/Requests/{ListUsernamesRequestCustom.cs => ListWithCustomPagerRequest.cs} (60%) diff --git a/seed/csharp-sdk/basic-auth-pw-omitted/.fern/metadata.json b/seed/csharp-sdk/basic-auth-pw-omitted/.fern/metadata.json index 91d0855bee07..d119d1639273 100644 --- a/seed/csharp-sdk/basic-auth-pw-omitted/.fern/metadata.json +++ b/seed/csharp-sdk/basic-auth-pw-omitted/.fern/metadata.json @@ -1,7 +1,7 @@ { "cliVersion": "DUMMY", "generatorName": "fernapi/fern-csharp-sdk", - "generatorVersion": "latest", + "generatorVersion": "local", "generatorConfig": {}, "originGitCommit": "DUMMY", "sdkVersion": "0.0.1" diff --git a/seed/csharp-sdk/imdb/extra-dependencies-override/.fern/metadata.json b/seed/csharp-sdk/imdb/extra-dependencies-override/.fern/metadata.json index 08afb88395fc..90961ce15d2d 100644 --- a/seed/csharp-sdk/imdb/extra-dependencies-override/.fern/metadata.json +++ b/seed/csharp-sdk/imdb/extra-dependencies-override/.fern/metadata.json @@ -1,7 +1,7 @@ { "cliVersion": "DUMMY", "generatorName": "fernapi/fern-csharp-sdk", - "generatorVersion": "latest", + "generatorVersion": "local", "generatorConfig": { "extra-dependencies": { "PolySharp": "1.14.0" diff --git a/seed/csharp-sdk/pagination-custom/README.md b/seed/csharp-sdk/pagination-custom/README.md index c871d0ae9c32..4ccd750eee97 100644 --- a/seed/csharp-sdk/pagination-custom/README.md +++ b/seed/csharp-sdk/pagination-custom/README.md @@ -43,8 +43,8 @@ Instantiate and use the client with the following: using SeedPagination; var client = new SeedPaginationClient("TOKEN"); -var items = await client.Users.ListUsernamesCustomAsync( - new ListUsernamesRequestCustom { StartingAfter = "starting_after" } +var items = await client.Users.ListWithCustomPagerAsync( + new ListWithCustomPagerRequest { Limit = 1, StartingAfter = "starting_after" } ); await foreach (var item in items) @@ -62,7 +62,7 @@ will be thrown. using SeedPagination; try { - var response = await client.Users.ListUsernamesCustomAsync(...); + var response = await client.Users.ListWithCustomPagerAsync(...); } catch (SeedPaginationApiException e) { System.Console.WriteLine(e.Body); System.Console.WriteLine(e.StatusCode); @@ -77,8 +77,8 @@ List endpoints are paginated. The SDK provides an async enumerable so that you c using SeedPagination; var client = new SeedPaginationClient("TOKEN"); -var items = await client.Users.ListUsernamesCustomAsync( - new ListUsernamesRequestCustom { StartingAfter = "starting_after" } +var items = await client.Users.ListWithCustomPagerAsync( + new ListWithCustomPagerRequest { Limit = 1, StartingAfter = "starting_after" } ); await foreach (var item in items) @@ -104,7 +104,7 @@ A request is deemed retryable when any of the following HTTP status codes is ret Use the `MaxRetries` request option to configure this behavior. ```csharp -var response = await client.Users.ListUsernamesCustomAsync( +var response = await client.Users.ListWithCustomPagerAsync( ..., new RequestOptions { MaxRetries: 0 // Override MaxRetries at the request level @@ -117,7 +117,7 @@ var response = await client.Users.ListUsernamesCustomAsync( The SDK defaults to a 30 second timeout. Use the `Timeout` option to configure this behavior. ```csharp -var response = await client.Users.ListUsernamesCustomAsync( +var response = await client.Users.ListWithCustomPagerAsync( ..., new RequestOptions { Timeout: TimeSpan.FromSeconds(3) // Override timeout to 3s @@ -133,7 +133,7 @@ Access raw HTTP response data (status code, headers, URL) alongside parsed respo using SeedPagination; // Access raw response data (status code, headers, etc.) alongside the parsed response -var result = await client.Users.ListUsernamesCustomAsync(...).WithRawResponse(); +var result = await client.Users.ListWithCustomPagerAsync(...).WithRawResponse(); // Access the parsed data var data = result.Data; @@ -150,7 +150,7 @@ if (headers.TryGetValue("X-Request-Id", out var requestId)) } // For the default behavior, simply await without .WithRawResponse() -var data = await client.Users.ListUsernamesCustomAsync(...); +var data = await client.Users.ListWithCustomPagerAsync(...); ``` ### Additional Headers @@ -158,7 +158,7 @@ var data = await client.Users.ListUsernamesCustomAsync(...); If you would like to send additional headers as part of the request, use the `AdditionalHeaders` request option. ```csharp -var response = await client.Users.ListUsernamesCustomAsync( +var response = await client.Users.ListWithCustomPagerAsync( ..., new RequestOptions { AdditionalHeaders = new Dictionary @@ -174,7 +174,7 @@ var response = await client.Users.ListUsernamesCustomAsync( If you would like to send additional query parameters as part of the request, use the `AdditionalQueryParameters` request option. ```csharp -var response = await client.Users.ListUsernamesCustomAsync( +var response = await client.Users.ListWithCustomPagerAsync( ..., new RequestOptions { AdditionalQueryParameters = new Dictionary diff --git a/seed/csharp-sdk/pagination-custom/reference.md b/seed/csharp-sdk/pagination-custom/reference.md index 060fd111cf64..e7cb038a665c 100644 --- a/seed/csharp-sdk/pagination-custom/reference.md +++ b/seed/csharp-sdk/pagination-custom/reference.md @@ -1,6 +1,6 @@ # Reference ## Users -
client.Users.ListUsernamesCustomAsync(ListUsernamesRequestCustom { ... }) -> SeedPaginationPager<string> +
client.Users.ListWithCustomPagerAsync(ListWithCustomPagerRequest { ... }) -> SeedPaginationPager<string>
@@ -13,8 +13,8 @@
```csharp -await client.Users.ListUsernamesCustomAsync( - new ListUsernamesRequestCustom { StartingAfter = "starting_after" } +await client.Users.ListWithCustomPagerAsync( + new ListWithCustomPagerRequest { Limit = 1, StartingAfter = "starting_after" } ); ```
@@ -30,7 +30,7 @@ await client.Users.ListUsernamesCustomAsync(
-**request:** `ListUsernamesRequestCustom` +**request:** `ListWithCustomPagerRequest`
diff --git a/seed/csharp-sdk/pagination-custom/snippet.json b/seed/csharp-sdk/pagination-custom/snippet.json index 65baf3c36d9f..669fb1c6f13a 100644 --- a/seed/csharp-sdk/pagination-custom/snippet.json +++ b/seed/csharp-sdk/pagination-custom/snippet.json @@ -6,11 +6,11 @@ "id": { "path": "/users", "method": "GET", - "identifier_override": "endpoint_users.listUsernamesCustom" + "identifier_override": "endpoint_users.listWithCustomPager" }, "snippet": { "type": "csharp", - "client": "using SeedPagination;\n\nvar client = new SeedPaginationClient(\"TOKEN\");\nvar items = await client.Users.ListUsernamesCustomAsync(\n new ListUsernamesRequestCustom { StartingAfter = \"starting_after\" }\n);\n\nawait foreach (var item in items)\n{\n // do something with item\n}\n" + "client": "using SeedPagination;\n\nvar client = new SeedPaginationClient(\"TOKEN\");\nvar items = await client.Users.ListWithCustomPagerAsync(\n new ListWithCustomPagerRequest { Limit = 1, StartingAfter = \"starting_after\" }\n);\n\nawait foreach (var item in items)\n{\n // do something with item\n}\n" } } ] diff --git a/seed/csharp-sdk/pagination-custom/src/SeedApi.DynamicSnippets/Example0.cs b/seed/csharp-sdk/pagination-custom/src/SeedApi.DynamicSnippets/Example0.cs index e41a5132e7b6..180f0b9b0932 100644 --- a/seed/csharp-sdk/pagination-custom/src/SeedApi.DynamicSnippets/Example0.cs +++ b/seed/csharp-sdk/pagination-custom/src/SeedApi.DynamicSnippets/Example0.cs @@ -12,8 +12,9 @@ public async Task Do() { } ); - await client.Users.ListUsernamesCustomAsync( - new ListUsernamesRequestCustom { + await client.Users.ListWithCustomPagerAsync( + new ListWithCustomPagerRequest { + Limit = 1, StartingAfter = "starting_after" } ); diff --git a/seed/csharp-sdk/pagination-custom/src/SeedPagination/Types/UsernamePage.cs b/seed/csharp-sdk/pagination-custom/src/SeedPagination/Types/Link.cs similarity index 69% rename from seed/csharp-sdk/pagination-custom/src/SeedPagination/Types/UsernamePage.cs rename to seed/csharp-sdk/pagination-custom/src/SeedPagination/Types/Link.cs index 77fd0e85bd38..4b40998adcb7 100644 --- a/seed/csharp-sdk/pagination-custom/src/SeedPagination/Types/UsernamePage.cs +++ b/seed/csharp-sdk/pagination-custom/src/SeedPagination/Types/Link.cs @@ -5,17 +5,20 @@ namespace SeedPagination; [Serializable] -public record UsernamePage : IJsonOnDeserialized +public record Link : IJsonOnDeserialized { [JsonExtensionData] private readonly IDictionary _extensionData = new Dictionary(); - [JsonPropertyName("after")] - public string? After { get; set; } + [JsonPropertyName("rel")] + public required string Rel { get; set; } - [JsonPropertyName("data")] - public IEnumerable Data { get; set; } = new List(); + [JsonPropertyName("method")] + public required string Method { get; set; } + + [JsonPropertyName("href")] + public required string Href { get; set; } [JsonIgnore] public ReadOnlyAdditionalProperties AdditionalProperties { get; private set; } = new(); diff --git a/seed/csharp-sdk/pagination-custom/src/SeedPagination/Types/UsernameCursor.cs b/seed/csharp-sdk/pagination-custom/src/SeedPagination/Types/UsersListResponse.cs similarity index 57% rename from seed/csharp-sdk/pagination-custom/src/SeedPagination/Types/UsernameCursor.cs rename to seed/csharp-sdk/pagination-custom/src/SeedPagination/Types/UsersListResponse.cs index 47fe5e02478d..cc2cea48e41f 100644 --- a/seed/csharp-sdk/pagination-custom/src/SeedPagination/Types/UsernameCursor.cs +++ b/seed/csharp-sdk/pagination-custom/src/SeedPagination/Types/UsersListResponse.cs @@ -5,14 +5,26 @@ namespace SeedPagination; [Serializable] -public record UsernameCursor : IJsonOnDeserialized +public record UsersListResponse : IJsonOnDeserialized { [JsonExtensionData] private readonly IDictionary _extensionData = new Dictionary(); - [JsonPropertyName("cursor")] - public required UsernamePage Cursor { get; set; } + [JsonPropertyName("limit")] + public int? Limit { get; set; } + + [JsonPropertyName("count")] + public int? Count { get; set; } + + [JsonPropertyName("has_more")] + public bool? HasMore { get; set; } + + [JsonPropertyName("links")] + public IEnumerable Links { get; set; } = new List(); + + [JsonPropertyName("data")] + public IEnumerable Data { get; set; } = new List(); [JsonIgnore] public ReadOnlyAdditionalProperties AdditionalProperties { get; private set; } = new(); diff --git a/seed/csharp-sdk/pagination-custom/src/SeedPagination/Users/IUsersClient.cs b/seed/csharp-sdk/pagination-custom/src/SeedPagination/Users/IUsersClient.cs index cfa2c99bf728..acdc6133f681 100644 --- a/seed/csharp-sdk/pagination-custom/src/SeedPagination/Users/IUsersClient.cs +++ b/seed/csharp-sdk/pagination-custom/src/SeedPagination/Users/IUsersClient.cs @@ -4,8 +4,8 @@ namespace SeedPagination; public partial interface IUsersClient { - Task> ListUsernamesCustomAsync( - ListUsernamesRequestCustom request, + Task> ListWithCustomPagerAsync( + ListWithCustomPagerRequest request, RequestOptions? options = null, CancellationToken cancellationToken = default ); diff --git a/seed/csharp-sdk/pagination-custom/src/SeedPagination/Users/Requests/ListUsernamesRequestCustom.cs b/seed/csharp-sdk/pagination-custom/src/SeedPagination/Users/Requests/ListWithCustomPagerRequest.cs similarity index 60% rename from seed/csharp-sdk/pagination-custom/src/SeedPagination/Users/Requests/ListUsernamesRequestCustom.cs rename to seed/csharp-sdk/pagination-custom/src/SeedPagination/Users/Requests/ListWithCustomPagerRequest.cs index f934b28937c9..feb8087e5644 100644 --- a/seed/csharp-sdk/pagination-custom/src/SeedPagination/Users/Requests/ListUsernamesRequestCustom.cs +++ b/seed/csharp-sdk/pagination-custom/src/SeedPagination/Users/Requests/ListWithCustomPagerRequest.cs @@ -4,11 +4,16 @@ namespace SeedPagination; [Serializable] -public record ListUsernamesRequestCustom +public record ListWithCustomPagerRequest { /// - /// The cursor used for pagination in order to fetch - /// the next page of results. + /// The maximum number of results to return. + /// + [JsonIgnore] + public int? Limit { get; set; } + + /// + /// The cursor used for pagination. /// [JsonIgnore] public string? StartingAfter { get; set; } diff --git a/seed/csharp-sdk/pagination-custom/src/SeedPagination/Users/UsersClient.cs b/seed/csharp-sdk/pagination-custom/src/SeedPagination/Users/UsersClient.cs index 6b55e62f99f9..5139ef654095 100644 --- a/seed/csharp-sdk/pagination-custom/src/SeedPagination/Users/UsersClient.cs +++ b/seed/csharp-sdk/pagination-custom/src/SeedPagination/Users/UsersClient.cs @@ -12,17 +12,18 @@ internal UsersClient(RawClient client) } /// - /// await client.Users.ListUsernamesCustomAsync( - /// new ListUsernamesRequestCustom { StartingAfter = "starting_after" } + /// await client.Users.ListWithCustomPagerAsync( + /// new ListWithCustomPagerRequest { Limit = 1, StartingAfter = "starting_after" } /// ); /// - public async Task> ListUsernamesCustomAsync( - ListUsernamesRequestCustom request, + public async Task> ListWithCustomPagerAsync( + ListWithCustomPagerRequest request, RequestOptions? options = null, CancellationToken cancellationToken = default ) { - var _queryString = new SeedPagination.Core.QueryStringBuilder.Builder(capacity: 1) + var _queryString = new SeedPagination.Core.QueryStringBuilder.Builder(capacity: 2) + .Add("limit", request.Limit) .Add("starting_after", request.StartingAfter) .MergeAdditional(options?.AdditionalQueryParameters) .Build(); From 39d8b8789ce4c91ba1467d0d7e46bc6347fa5d36 Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:20:13 -0400 Subject: [PATCH 09/21] chore(rust): update rust-sdk seed (#14697) Co-authored-by: davidkonigsberg <72822263+davidkonigsberg@users.noreply.github.com> --- seed/rust-sdk/alias-extends/README.md | 2 +- seed/rust-sdk/any-auth/README.md | 2 +- seed/rust-sdk/audiences/README.md | 4 +- seed/rust-sdk/client-side-params/README.md | 2 +- seed/rust-sdk/content-type/README.md | 2 +- .../cross-package-type-names/README.md | 2 +- .../rust-sdk/endpoint-security-auth/README.md | 2 +- seed/rust-sdk/enum/README.md | 2 +- .../examples/no-custom-config/README.md | 2 +- .../rust-sdk/examples/readme-config/README.md | 2 +- seed/rust-sdk/exhaustive/README.md | 2 +- seed/rust-sdk/extends/README.md | 2 +- seed/rust-sdk/extra-properties/README.md | 2 +- seed/rust-sdk/idempotency-headers/README.md | 2 +- seed/rust-sdk/imdb/imdb/src/config.rs | 2 +- .../rust-sdk/inferred-auth-explicit/README.md | 2 +- .../README.md | 2 +- .../rust-sdk/inferred-auth-implicit/README.md | 2 +- seed/rust-sdk/literal/README.md | 2 +- seed/rust-sdk/multi-line-docs/README.md | 2 +- .../README.md | 4 +- seed/rust-sdk/multi-url-environment/README.md | 4 +- .../multiple-request-bodies/README.md | 4 +- seed/rust-sdk/no-content-response/README.md | 2 +- seed/rust-sdk/null-type/README.md | 2 +- .../rust-sdk/nullable-allof-extends/README.md | 2 +- seed/rust-sdk/nullable-optional/README.md | 2 +- seed/rust-sdk/nullable/README.md | 2 +- .../oauth-client-credentials-custom/README.md | 2 +- .../README.md | 2 +- .../README.md | 2 +- .../README.md | 2 +- .../README.md | 2 +- .../README.md | 2 +- .../README.md | 2 +- .../oauth-client-credentials/README.md | 2 +- seed/rust-sdk/pagination-custom/README.md | 15 ++-- .../dynamic-snippets/example0.rs | 5 +- seed/rust-sdk/pagination-custom/reference.md | 18 +++-- .../src/api/resources/users/users.rs | 7 +- .../pagination-custom/src/api/types/link.rs | 57 ++++++++++++++ .../list_usernames_custom_query_request.rs | 36 --------- .../list_with_custom_pager_query_request.rs | 45 +++++++++++ .../pagination-custom/src/api/types/mod.rs | 12 +-- .../src/api/types/username_cursor.rs | 37 ---------- .../src/api/types/username_page.rs | 44 ----------- .../src/api/types/users_list_response.rs | 74 +++++++++++++++++++ seed/rust-sdk/pagination-custom/src/lib.rs | 5 +- seed/rust-sdk/pagination/README.md | 2 +- .../query-param-name-conflict/README.md | 2 +- seed/rust-sdk/request-parameters/README.md | 2 +- seed/rust-sdk/required-nullable/README.md | 2 +- .../README.md | 2 +- .../with-wire-tests/README.md | 2 +- .../with-wire-tests/README.md | 2 +- seed/rust-sdk/server-url-templating/README.md | 4 +- .../simple-api/basic-custom-config/README.md | 2 +- seed/rust-sdk/simple-api/basic/README.md | 2 +- .../basic/README.md | 2 +- .../custom-environment/README.md | 2 +- .../full-custom/README.md | 2 +- .../README.md | 2 +- seed/rust-sdk/streaming-parameter/README.md | 2 +- seed/rust-sdk/streaming/README.md | 2 +- seed/rust-sdk/trace/README.md | 4 +- .../rust-sdk/undiscriminated-unions/README.md | 2 +- seed/rust-sdk/url-form-encoded/README.md | 2 +- seed/rust-sdk/validation/README.md | 2 +- .../websocket-inferred-auth/README.md | 2 +- seed/rust-sdk/websocket/src/api/mod.rs | 2 +- .../websocket/src/api/websocket/realtime.rs | 2 +- seed/rust-sdk/websocket/src/lib.rs | 8 +- 72 files changed, 281 insertions(+), 212 deletions(-) create mode 100644 seed/rust-sdk/pagination-custom/src/api/types/link.rs delete mode 100644 seed/rust-sdk/pagination-custom/src/api/types/list_usernames_custom_query_request.rs create mode 100644 seed/rust-sdk/pagination-custom/src/api/types/list_with_custom_pager_query_request.rs delete mode 100644 seed/rust-sdk/pagination-custom/src/api/types/username_cursor.rs delete mode 100644 seed/rust-sdk/pagination-custom/src/api/types/username_page.rs create mode 100644 seed/rust-sdk/pagination-custom/src/api/types/users_list_response.rs diff --git a/seed/rust-sdk/alias-extends/README.md b/seed/rust-sdk/alias-extends/README.md index e9f3b76c03f7..19ab6aa8f6d4 100644 --- a/seed/rust-sdk/alias-extends/README.md +++ b/seed/rust-sdk/alias-extends/README.md @@ -86,7 +86,7 @@ match client.extended_inline_request_body(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_alias_extends::prelude::*; +use seed_alias_extends::prelude::{*}; let request = InlinedChildRequest { ... diff --git a/seed/rust-sdk/any-auth/README.md b/seed/rust-sdk/any-auth/README.md index 493b588c2832..b4d2c5036f63 100644 --- a/seed/rust-sdk/any-auth/README.md +++ b/seed/rust-sdk/any-auth/README.md @@ -90,7 +90,7 @@ match client.auth.get_token(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_any_auth::prelude::*; +use seed_any_auth::prelude::{*}; let request = GetTokenRequest { ... diff --git a/seed/rust-sdk/audiences/README.md b/seed/rust-sdk/audiences/README.md index ce48e438b3be..99680ce2207d 100644 --- a/seed/rust-sdk/audiences/README.md +++ b/seed/rust-sdk/audiences/README.md @@ -71,7 +71,7 @@ async fn main() { This SDK allows you to configure different environments for API requests. ```rust -use seed_audiences::prelude::*; +use seed_audiences::prelude::{*}; let config = ClientConfig { base_url: Environment::EnvironmentA.url().to_string(), @@ -103,7 +103,7 @@ match client.foo.find(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_audiences::prelude::*; +use seed_audiences::prelude::{*}; let request = FindRequest { ... diff --git a/seed/rust-sdk/client-side-params/README.md b/seed/rust-sdk/client-side-params/README.md index a62e5b079dc3..286ffea4d72d 100644 --- a/seed/rust-sdk/client-side-params/README.md +++ b/seed/rust-sdk/client-side-params/README.md @@ -93,7 +93,7 @@ match client.service.search_resources(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_client_side_params::prelude::*; +use seed_client_side_params::prelude::{*}; let request = SearchResourcesRequest { ... diff --git a/seed/rust-sdk/content-type/README.md b/seed/rust-sdk/content-type/README.md index bf773b4f0d18..7b0663262d93 100644 --- a/seed/rust-sdk/content-type/README.md +++ b/seed/rust-sdk/content-type/README.md @@ -88,7 +88,7 @@ match client.service.patch(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_content_types::prelude::*; +use seed_content_types::prelude::{*}; let request = PatchProxyRequest { ... diff --git a/seed/rust-sdk/cross-package-type-names/README.md b/seed/rust-sdk/cross-package-type-names/README.md index a1ea9c422f70..91b48de9bc81 100644 --- a/seed/rust-sdk/cross-package-type-names/README.md +++ b/seed/rust-sdk/cross-package-type-names/README.md @@ -88,7 +88,7 @@ match client.foo.find(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_cross_package_type_names::prelude::*; +use seed_cross_package_type_names::prelude::{*}; let request = FindRequest { ... diff --git a/seed/rust-sdk/endpoint-security-auth/README.md b/seed/rust-sdk/endpoint-security-auth/README.md index e433da717781..9480fd0ccdfe 100644 --- a/seed/rust-sdk/endpoint-security-auth/README.md +++ b/seed/rust-sdk/endpoint-security-auth/README.md @@ -90,7 +90,7 @@ match client.auth.get_token(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_endpoint_security_auth::prelude::*; +use seed_endpoint_security_auth::prelude::{*}; let request = GetTokenRequest { ... diff --git a/seed/rust-sdk/enum/README.md b/seed/rust-sdk/enum/README.md index 4e3f9101b922..30e64db90c60 100644 --- a/seed/rust-sdk/enum/README.md +++ b/seed/rust-sdk/enum/README.md @@ -86,7 +86,7 @@ match client.headers.send(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_enum::prelude::*; +use seed_enum::prelude::{*}; let request = SendEnumInlinedRequest { ... diff --git a/seed/rust-sdk/examples/no-custom-config/README.md b/seed/rust-sdk/examples/no-custom-config/README.md index 833f0337d860..f3d88100a220 100644 --- a/seed/rust-sdk/examples/no-custom-config/README.md +++ b/seed/rust-sdk/examples/no-custom-config/README.md @@ -63,7 +63,7 @@ async fn main() { This SDK allows you to configure different environments for API requests. ```rust -use seed_examples::prelude::*; +use seed_examples::prelude::{*}; let config = ClientConfig { base_url: Environment::Production.url().to_string(), diff --git a/seed/rust-sdk/examples/readme-config/README.md b/seed/rust-sdk/examples/readme-config/README.md index e22cf369e61c..cefc1de1cdf3 100644 --- a/seed/rust-sdk/examples/readme-config/README.md +++ b/seed/rust-sdk/examples/readme-config/README.md @@ -110,7 +110,7 @@ async fn main() { This SDK allows you to configure different environments for API requests. ```rust -use seed_examples::prelude::*; +use seed_examples::prelude::{*}; let config = ClientConfig { base_url: Environment::Production.url().to_string(), diff --git a/seed/rust-sdk/exhaustive/README.md b/seed/rust-sdk/exhaustive/README.md index 4234ff7e9682..cc249d29dad2 100644 --- a/seed/rust-sdk/exhaustive/README.md +++ b/seed/rust-sdk/exhaustive/README.md @@ -83,7 +83,7 @@ match client.endpoints.container.get_and_return_list_of_primitives(None)?.await The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_exhaustive::prelude::*; +use seed_exhaustive::prelude::{*}; let request = PostWithObjectBody { ... diff --git a/seed/rust-sdk/extends/README.md b/seed/rust-sdk/extends/README.md index ec4f76af9b70..64a91efae375 100644 --- a/seed/rust-sdk/extends/README.md +++ b/seed/rust-sdk/extends/README.md @@ -87,7 +87,7 @@ match client.extended_inline_request_body(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_extends::prelude::*; +use seed_extends::prelude::{*}; let request = Inlined { ... diff --git a/seed/rust-sdk/extra-properties/README.md b/seed/rust-sdk/extra-properties/README.md index fe3ed5712701..57c2ada1e784 100644 --- a/seed/rust-sdk/extra-properties/README.md +++ b/seed/rust-sdk/extra-properties/README.md @@ -88,7 +88,7 @@ match client.user.create_user(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_extra_properties::prelude::*; +use seed_extra_properties::prelude::{*}; let request = CreateUserRequest { ... diff --git a/seed/rust-sdk/idempotency-headers/README.md b/seed/rust-sdk/idempotency-headers/README.md index 9a3aa12f9fed..5defd0301dca 100644 --- a/seed/rust-sdk/idempotency-headers/README.md +++ b/seed/rust-sdk/idempotency-headers/README.md @@ -88,7 +88,7 @@ match client.payment.create(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_idempotency_headers::prelude::*; +use seed_idempotency_headers::prelude::{*}; let request = CreatePaymentRequest { ... diff --git a/seed/rust-sdk/imdb/imdb/src/config.rs b/seed/rust-sdk/imdb/imdb/src/config.rs index fb215ac8e2c1..014f33fa50a3 100644 --- a/seed/rust-sdk/imdb/imdb/src/config.rs +++ b/seed/rust-sdk/imdb/imdb/src/config.rs @@ -26,7 +26,7 @@ impl Default for ClientConfig { client_id: None, client_secret: None, timeout: Duration::from_secs(60), - max_retries: 3, + max_retries: 5, custom_headers: HashMap::from([ ("X-Fern-Language".to_string(), "Rust".to_string()), ("X-Fern-SDK-Name".to_string(), "seed_api".to_string()), diff --git a/seed/rust-sdk/inferred-auth-explicit/README.md b/seed/rust-sdk/inferred-auth-explicit/README.md index 7e9db8729580..301db3f60b4b 100644 --- a/seed/rust-sdk/inferred-auth-explicit/README.md +++ b/seed/rust-sdk/inferred-auth-explicit/README.md @@ -90,7 +90,7 @@ match client.auth.get_token_with_client_credentials(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_inferred_auth_explicit::prelude::*; +use seed_inferred_auth_explicit::prelude::{*}; let request = GetTokenRequest { ... diff --git a/seed/rust-sdk/inferred-auth-implicit-no-expiry/README.md b/seed/rust-sdk/inferred-auth-implicit-no-expiry/README.md index 06d47027bb40..a802b4498328 100644 --- a/seed/rust-sdk/inferred-auth-implicit-no-expiry/README.md +++ b/seed/rust-sdk/inferred-auth-implicit-no-expiry/README.md @@ -90,7 +90,7 @@ match client.auth.get_token_with_client_credentials(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_inferred_auth_implicit_no_expiry::prelude::*; +use seed_inferred_auth_implicit_no_expiry::prelude::{*}; let request = GetTokenRequest { ... diff --git a/seed/rust-sdk/inferred-auth-implicit/README.md b/seed/rust-sdk/inferred-auth-implicit/README.md index f93de0feed5c..df4e91f3f848 100644 --- a/seed/rust-sdk/inferred-auth-implicit/README.md +++ b/seed/rust-sdk/inferred-auth-implicit/README.md @@ -90,7 +90,7 @@ match client.auth.get_token_with_client_credentials(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_inferred_auth_implicit::prelude::*; +use seed_inferred_auth_implicit::prelude::{*}; let request = GetTokenRequest { ... diff --git a/seed/rust-sdk/literal/README.md b/seed/rust-sdk/literal/README.md index 85b10298c053..e3817b717f4b 100644 --- a/seed/rust-sdk/literal/README.md +++ b/seed/rust-sdk/literal/README.md @@ -90,7 +90,7 @@ match client.headers.send(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_literal::prelude::*; +use seed_literal::prelude::{*}; let request = SendLiteralsInHeadersRequest { ... diff --git a/seed/rust-sdk/multi-line-docs/README.md b/seed/rust-sdk/multi-line-docs/README.md index b376e6920040..87c03b179784 100644 --- a/seed/rust-sdk/multi-line-docs/README.md +++ b/seed/rust-sdk/multi-line-docs/README.md @@ -87,7 +87,7 @@ match client.user.create_user(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_multi_line_docs::prelude::*; +use seed_multi_line_docs::prelude::{*}; let request = CreateUserRequest { ... diff --git a/seed/rust-sdk/multi-url-environment-no-default/README.md b/seed/rust-sdk/multi-url-environment-no-default/README.md index 5ed2064b5843..330c42fdfeb1 100644 --- a/seed/rust-sdk/multi-url-environment-no-default/README.md +++ b/seed/rust-sdk/multi-url-environment-no-default/README.md @@ -70,7 +70,7 @@ async fn main() { This SDK allows you to configure different environments for API requests. ```rust -use seed_multi_url_environment_no_default::prelude::*; +use seed_multi_url_environment_no_default::prelude::{*}; let config = ClientConfig { base_url: Environment::Production.url().to_string(), @@ -102,7 +102,7 @@ match client.ec_2.boot_instance(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_multi_url_environment_no_default::prelude::*; +use seed_multi_url_environment_no_default::prelude::{*}; let request = BootInstanceRequest { ... diff --git a/seed/rust-sdk/multi-url-environment/README.md b/seed/rust-sdk/multi-url-environment/README.md index 74f2aecdbf4c..0eaf39fb7eec 100644 --- a/seed/rust-sdk/multi-url-environment/README.md +++ b/seed/rust-sdk/multi-url-environment/README.md @@ -70,7 +70,7 @@ async fn main() { This SDK allows you to configure different environments for API requests. ```rust -use seed_multi_url_environment::prelude::*; +use seed_multi_url_environment::prelude::{*}; let config = ClientConfig { base_url: Environment::Production.url().to_string(), @@ -102,7 +102,7 @@ match client.ec_2.boot_instance(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_multi_url_environment::prelude::*; +use seed_multi_url_environment::prelude::{*}; let request = BootInstanceRequest { ... diff --git a/seed/rust-sdk/multiple-request-bodies/README.md b/seed/rust-sdk/multiple-request-bodies/README.md index af52ca0f2785..926401edb0fe 100644 --- a/seed/rust-sdk/multiple-request-bodies/README.md +++ b/seed/rust-sdk/multiple-request-bodies/README.md @@ -69,7 +69,7 @@ async fn main() { This SDK allows you to configure different environments for API requests. ```rust -use seed_api::prelude::*; +use seed_api::prelude::{*}; let config = ClientConfig { base_url: Environment::Default.url().to_string(), @@ -101,7 +101,7 @@ match client.upload_json_document(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_api::prelude::*; +use seed_api::prelude::{*}; let request = UploadDocumentRequest { ... diff --git a/seed/rust-sdk/no-content-response/README.md b/seed/rust-sdk/no-content-response/README.md index f3e66f677590..9f3cad31ce31 100644 --- a/seed/rust-sdk/no-content-response/README.md +++ b/seed/rust-sdk/no-content-response/README.md @@ -87,7 +87,7 @@ match client.contacts.create(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_api::prelude::*; +use seed_api::prelude::{*}; let request = CreateContactRequest { ... diff --git a/seed/rust-sdk/null-type/README.md b/seed/rust-sdk/null-type/README.md index bb1bd0b3f3a1..830c8a63ca4c 100644 --- a/seed/rust-sdk/null-type/README.md +++ b/seed/rust-sdk/null-type/README.md @@ -87,7 +87,7 @@ match client.conversations.outbound_call(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_api::prelude::*; +use seed_api::prelude::{*}; let request = OutboundCallConversationsRequest { ... diff --git a/seed/rust-sdk/nullable-allof-extends/README.md b/seed/rust-sdk/nullable-allof-extends/README.md index 31c11ad4d1c0..8f59f0ab4a02 100644 --- a/seed/rust-sdk/nullable-allof-extends/README.md +++ b/seed/rust-sdk/nullable-allof-extends/README.md @@ -70,7 +70,7 @@ async fn main() { This SDK allows you to configure different environments for API requests. ```rust -use seed_api::prelude::*; +use seed_api::prelude::{*}; let config = ClientConfig { base_url: Environment::Default.url().to_string(), diff --git a/seed/rust-sdk/nullable-optional/README.md b/seed/rust-sdk/nullable-optional/README.md index 0be1d79f5704..51324ac7bc97 100644 --- a/seed/rust-sdk/nullable-optional/README.md +++ b/seed/rust-sdk/nullable-optional/README.md @@ -99,7 +99,7 @@ match client.nullable_optional.create_user(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_nullable_optional::prelude::*; +use seed_nullable_optional::prelude::{*}; let request = UpdateComplexProfileRequest { ... diff --git a/seed/rust-sdk/nullable/README.md b/seed/rust-sdk/nullable/README.md index f5ae96e2f36e..77cca5dc63ab 100644 --- a/seed/rust-sdk/nullable/README.md +++ b/seed/rust-sdk/nullable/README.md @@ -99,7 +99,7 @@ match client.nullable.create_user(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_nullable::prelude::*; +use seed_nullable::prelude::{*}; let request = CreateUserRequest { ... diff --git a/seed/rust-sdk/oauth-client-credentials-custom/README.md b/seed/rust-sdk/oauth-client-credentials-custom/README.md index 35f9cd660e58..8316d1fbaa1c 100644 --- a/seed/rust-sdk/oauth-client-credentials-custom/README.md +++ b/seed/rust-sdk/oauth-client-credentials-custom/README.md @@ -92,7 +92,7 @@ match client.auth.get_token_with_client_credentials(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_oauth_client_credentials::prelude::*; +use seed_oauth_client_credentials::prelude::{*}; let request = GetTokenRequest { ... diff --git a/seed/rust-sdk/oauth-client-credentials-default/README.md b/seed/rust-sdk/oauth-client-credentials-default/README.md index 1d79dd6f4f73..7508c5393183 100644 --- a/seed/rust-sdk/oauth-client-credentials-default/README.md +++ b/seed/rust-sdk/oauth-client-credentials-default/README.md @@ -88,7 +88,7 @@ match client.auth.get_token(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_oauth_client_credentials_default::prelude::*; +use seed_oauth_client_credentials_default::prelude::{*}; let request = GetTokenRequest { ... diff --git a/seed/rust-sdk/oauth-client-credentials-environment-variables/README.md b/seed/rust-sdk/oauth-client-credentials-environment-variables/README.md index 0de7958807d4..6e38b58c17ad 100644 --- a/seed/rust-sdk/oauth-client-credentials-environment-variables/README.md +++ b/seed/rust-sdk/oauth-client-credentials-environment-variables/README.md @@ -91,7 +91,7 @@ match client.auth.get_token_with_client_credentials(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_oauth_client_credentials_environment_variables::prelude::*; +use seed_oauth_client_credentials_environment_variables::prelude::{*}; let request = GetTokenRequest { ... diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/README.md b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/README.md index 89a01aafa2c1..b5041e75dacc 100644 --- a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/README.md +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/README.md @@ -91,7 +91,7 @@ match client.auth.get_token_with_client_credentials(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_oauth_client_credentials_mandatory_auth::prelude::*; +use seed_oauth_client_credentials_mandatory_auth::prelude::{*}; let request = GetTokenRequest { ... diff --git a/seed/rust-sdk/oauth-client-credentials-nested-root/README.md b/seed/rust-sdk/oauth-client-credentials-nested-root/README.md index 720424db834f..4587373992dc 100644 --- a/seed/rust-sdk/oauth-client-credentials-nested-root/README.md +++ b/seed/rust-sdk/oauth-client-credentials-nested-root/README.md @@ -90,7 +90,7 @@ match client.auth.get_token(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_oauth_client_credentials::prelude::*; +use seed_oauth_client_credentials::prelude::{*}; let request = GetTokenRequest { ... diff --git a/seed/rust-sdk/oauth-client-credentials-openapi/README.md b/seed/rust-sdk/oauth-client-credentials-openapi/README.md index 41d1bdecb7d9..2052a5de5515 100644 --- a/seed/rust-sdk/oauth-client-credentials-openapi/README.md +++ b/seed/rust-sdk/oauth-client-credentials-openapi/README.md @@ -87,7 +87,7 @@ match client.identity.get_token(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_api::prelude::*; +use seed_api::prelude::{*}; let request = GetTokenIdentityRequest { ... diff --git a/seed/rust-sdk/oauth-client-credentials-with-variables/README.md b/seed/rust-sdk/oauth-client-credentials-with-variables/README.md index 8e3b6e1b8bcd..e22e3fc61513 100644 --- a/seed/rust-sdk/oauth-client-credentials-with-variables/README.md +++ b/seed/rust-sdk/oauth-client-credentials-with-variables/README.md @@ -91,7 +91,7 @@ match client.auth.get_token_with_client_credentials(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_oauth_client_credentials_with_variables::prelude::*; +use seed_oauth_client_credentials_with_variables::prelude::{*}; let request = GetTokenRequest { ... diff --git a/seed/rust-sdk/oauth-client-credentials/README.md b/seed/rust-sdk/oauth-client-credentials/README.md index e8ac435a1432..176ab6acfe53 100644 --- a/seed/rust-sdk/oauth-client-credentials/README.md +++ b/seed/rust-sdk/oauth-client-credentials/README.md @@ -90,7 +90,7 @@ match client.auth.get_token_with_client_credentials(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_oauth_client_credentials::prelude::*; +use seed_oauth_client_credentials::prelude::{*}; let request = GetTokenRequest { ... diff --git a/seed/rust-sdk/pagination-custom/README.md b/seed/rust-sdk/pagination-custom/README.md index 276299fd06dc..6add79460acf 100644 --- a/seed/rust-sdk/pagination-custom/README.md +++ b/seed/rust-sdk/pagination-custom/README.md @@ -53,8 +53,9 @@ async fn main() { let client = PaginationClient::new(config).expect("Failed to build client"); client .users - .list_usernames_custom( - &ListUsernamesCustomQueryRequest { + .list_with_custom_pager( + &ListWithCustomPagerQueryRequest { + limit: Some(1), starting_after: Some("starting_after".to_string()), ..Default::default() }, @@ -69,7 +70,7 @@ async fn main() { When the API returns a non-success status code (4xx or 5xx response), an error will be returned. ```rust -match client.users.list_usernames_custom(None)?.await { +match client.users.list_with_custom_pager(None)?.await { Ok(response) => { println!("Success: {:?}", response); }, @@ -99,7 +100,7 @@ A request is deemed retryable when any of the following HTTP status codes is ret Use the `max_retries` method to configure this behavior. ```rust -let response = client.users.list_usernames_custom( +let response = client.users.list_with_custom_pager( Some(RequestOptions::new().max_retries(3)) )?.await; ``` @@ -109,7 +110,7 @@ let response = client.users.list_usernames_custom( The SDK defaults to a 30 second timeout. Use the `timeout` method to configure this behavior. ```rust -let response = client.users.list_usernames_custom( +let response = client.users.list_with_custom_pager( Some(RequestOptions::new().timeout_seconds(30)) )?.await; ``` @@ -119,7 +120,7 @@ let response = client.users.list_usernames_custom( You can add custom headers to requests using `RequestOptions`. ```rust -let response = client.users.list_usernames_custom( +let response = client.users.list_with_custom_pager( Some( RequestOptions::new() .additional_header("X-Custom-Header", "custom-value") @@ -134,7 +135,7 @@ let response = client.users.list_usernames_custom( You can add custom query parameters to requests using `RequestOptions`. ```rust -let response = client.users.list_usernames_custom( +let response = client.users.list_with_custom_pager( Some( RequestOptions::new() .additional_query_param("filter", "active") diff --git a/seed/rust-sdk/pagination-custom/dynamic-snippets/example0.rs b/seed/rust-sdk/pagination-custom/dynamic-snippets/example0.rs index 17d548ce3beb..f2acddf7f83f 100644 --- a/seed/rust-sdk/pagination-custom/dynamic-snippets/example0.rs +++ b/seed/rust-sdk/pagination-custom/dynamic-snippets/example0.rs @@ -10,8 +10,9 @@ async fn main() { let client = PaginationClient::new(config).expect("Failed to build client"); client .users - .list_usernames_custom( - &ListUsernamesCustomQueryRequest { + .list_with_custom_pager( + &ListWithCustomPagerQueryRequest { + limit: Some(1), starting_after: Some("starting_after".to_string()), ..Default::default() }, diff --git a/seed/rust-sdk/pagination-custom/reference.md b/seed/rust-sdk/pagination-custom/reference.md index cf47393e235a..25fdaedab071 100644 --- a/seed/rust-sdk/pagination-custom/reference.md +++ b/seed/rust-sdk/pagination-custom/reference.md @@ -1,6 +1,6 @@ # Reference ## Users -
client.users.list_usernames_custom(starting_after: Option<Option<String>>) -> Result<UsernameCursor, ApiError> +
client.users.list_with_custom_pager(limit: Option<Option<i64>>, starting_after: Option<Option<String>>) -> Result<UsersListResponse, ApiError>
@@ -24,8 +24,9 @@ async fn main() { let client = PaginationClient::new(config).expect("Failed to build client"); client .users - .list_usernames_custom( - &ListUsernamesCustomQueryRequest { + .list_with_custom_pager( + &ListWithCustomPagerQueryRequest { + limit: Some(1), starting_after: Some("starting_after".to_string()), ..Default::default() }, @@ -47,10 +48,15 @@ async fn main() {
-**starting_after:** `Option` +**limit:** `Option` — The maximum number of results to return. + +
+
+ +
+
-The cursor used for pagination in order to fetch -the next page of results. +**starting_after:** `Option` — The cursor used for pagination.
diff --git a/seed/rust-sdk/pagination-custom/src/api/resources/users/users.rs b/seed/rust-sdk/pagination-custom/src/api/resources/users/users.rs index c860bff02fba..c8dd43482824 100644 --- a/seed/rust-sdk/pagination-custom/src/api/resources/users/users.rs +++ b/seed/rust-sdk/pagination-custom/src/api/resources/users/users.rs @@ -13,17 +13,18 @@ impl UsersClient { }) } - pub async fn list_usernames_custom( + pub async fn list_with_custom_pager( &self, - request: &ListUsernamesCustomQueryRequest, + request: &ListWithCustomPagerQueryRequest, options: Option, - ) -> Result { + ) -> Result { self.http_client .execute_request( Method::GET, "/users", None, QueryBuilder::new() + .int("limit", request.limit.clone()) .string("starting_after", request.starting_after.clone()) .build(), options, diff --git a/seed/rust-sdk/pagination-custom/src/api/types/link.rs b/seed/rust-sdk/pagination-custom/src/api/types/link.rs new file mode 100644 index 000000000000..d3e5b08e63e9 --- /dev/null +++ b/seed/rust-sdk/pagination-custom/src/api/types/link.rs @@ -0,0 +1,57 @@ +pub use crate::prelude::*; + +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] +pub struct Link { + #[serde(default)] + pub rel: String, + #[serde(default)] + pub method: String, + #[serde(default)] + pub href: String, +} + +impl Link { + pub fn builder() -> LinkBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct LinkBuilder { + rel: Option, + method: Option, + href: Option, +} + +impl LinkBuilder { + pub fn rel(mut self, value: impl Into) -> Self { + self.rel = Some(value.into()); + self + } + + pub fn method(mut self, value: impl Into) -> Self { + self.method = Some(value.into()); + self + } + + pub fn href(mut self, value: impl Into) -> Self { + self.href = Some(value.into()); + self + } + + /// Consumes the builder and constructs a [`Link`]. + /// This method will fail if any of the following fields are not set: + /// - [`rel`](LinkBuilder::rel) + /// - [`method`](LinkBuilder::method) + /// - [`href`](LinkBuilder::href) + pub fn build(self) -> Result { + Ok(Link { + rel: self.rel.ok_or_else(|| BuildError::missing_field("rel"))?, + method: self + .method + .ok_or_else(|| BuildError::missing_field("method"))?, + href: self.href.ok_or_else(|| BuildError::missing_field("href"))?, + }) + } +} diff --git a/seed/rust-sdk/pagination-custom/src/api/types/list_usernames_custom_query_request.rs b/seed/rust-sdk/pagination-custom/src/api/types/list_usernames_custom_query_request.rs deleted file mode 100644 index 57a338739bbb..000000000000 --- a/seed/rust-sdk/pagination-custom/src/api/types/list_usernames_custom_query_request.rs +++ /dev/null @@ -1,36 +0,0 @@ -pub use crate::prelude::*; - -/// Query parameters for listUsernamesCustom -#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] -pub struct ListUsernamesCustomQueryRequest { - /// The cursor used for pagination in order to fetch - /// the next page of results. - #[serde(skip_serializing_if = "Option::is_none")] - pub starting_after: Option, -} - -impl ListUsernamesCustomQueryRequest { - pub fn builder() -> ListUsernamesCustomQueryRequestBuilder { - ::default() - } -} - -#[derive(Clone, PartialEq, Default, Debug)] -#[non_exhaustive] -pub struct ListUsernamesCustomQueryRequestBuilder { - starting_after: Option, -} - -impl ListUsernamesCustomQueryRequestBuilder { - pub fn starting_after(mut self, value: impl Into) -> Self { - self.starting_after = Some(value.into()); - self - } - - /// Consumes the builder and constructs a [`ListUsernamesCustomQueryRequest`]. - pub fn build(self) -> Result { - Ok(ListUsernamesCustomQueryRequest { - starting_after: self.starting_after, - }) - } -} diff --git a/seed/rust-sdk/pagination-custom/src/api/types/list_with_custom_pager_query_request.rs b/seed/rust-sdk/pagination-custom/src/api/types/list_with_custom_pager_query_request.rs new file mode 100644 index 000000000000..833469340a73 --- /dev/null +++ b/seed/rust-sdk/pagination-custom/src/api/types/list_with_custom_pager_query_request.rs @@ -0,0 +1,45 @@ +pub use crate::prelude::*; + +/// Query parameters for listWithCustomPager +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] +pub struct ListWithCustomPagerQueryRequest { + /// The maximum number of results to return. + #[serde(skip_serializing_if = "Option::is_none")] + pub limit: Option, + /// The cursor used for pagination. + #[serde(skip_serializing_if = "Option::is_none")] + pub starting_after: Option, +} + +impl ListWithCustomPagerQueryRequest { + pub fn builder() -> ListWithCustomPagerQueryRequestBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct ListWithCustomPagerQueryRequestBuilder { + limit: Option, + starting_after: Option, +} + +impl ListWithCustomPagerQueryRequestBuilder { + pub fn limit(mut self, value: i64) -> Self { + self.limit = Some(value); + self + } + + pub fn starting_after(mut self, value: impl Into) -> Self { + self.starting_after = Some(value.into()); + self + } + + /// Consumes the builder and constructs a [`ListWithCustomPagerQueryRequest`]. + pub fn build(self) -> Result { + Ok(ListWithCustomPagerQueryRequest { + limit: self.limit, + starting_after: self.starting_after, + }) + } +} diff --git a/seed/rust-sdk/pagination-custom/src/api/types/mod.rs b/seed/rust-sdk/pagination-custom/src/api/types/mod.rs index 6417815ba193..c5cf54898919 100644 --- a/seed/rust-sdk/pagination-custom/src/api/types/mod.rs +++ b/seed/rust-sdk/pagination-custom/src/api/types/mod.rs @@ -1,7 +1,7 @@ -pub mod list_usernames_custom_query_request; -pub mod username_cursor; -pub mod username_page; +pub mod link; +pub mod list_with_custom_pager_query_request; +pub mod users_list_response; -pub use list_usernames_custom_query_request::ListUsernamesCustomQueryRequest; -pub use username_cursor::UsernameCursor; -pub use username_page::UsernamePage; +pub use link::Link; +pub use list_with_custom_pager_query_request::ListWithCustomPagerQueryRequest; +pub use users_list_response::UsersListResponse; diff --git a/seed/rust-sdk/pagination-custom/src/api/types/username_cursor.rs b/seed/rust-sdk/pagination-custom/src/api/types/username_cursor.rs deleted file mode 100644 index 864ff1062ce4..000000000000 --- a/seed/rust-sdk/pagination-custom/src/api/types/username_cursor.rs +++ /dev/null @@ -1,37 +0,0 @@ -pub use crate::prelude::*; - -#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] -pub struct UsernameCursor { - #[serde(default)] - pub cursor: UsernamePage, -} - -impl UsernameCursor { - pub fn builder() -> UsernameCursorBuilder { - ::default() - } -} - -#[derive(Clone, PartialEq, Default, Debug)] -#[non_exhaustive] -pub struct UsernameCursorBuilder { - cursor: Option, -} - -impl UsernameCursorBuilder { - pub fn cursor(mut self, value: UsernamePage) -> Self { - self.cursor = Some(value); - self - } - - /// Consumes the builder and constructs a [`UsernameCursor`]. - /// This method will fail if any of the following fields are not set: - /// - [`cursor`](UsernameCursorBuilder::cursor) - pub fn build(self) -> Result { - Ok(UsernameCursor { - cursor: self - .cursor - .ok_or_else(|| BuildError::missing_field("cursor"))?, - }) - } -} diff --git a/seed/rust-sdk/pagination-custom/src/api/types/username_page.rs b/seed/rust-sdk/pagination-custom/src/api/types/username_page.rs deleted file mode 100644 index 050b288129d5..000000000000 --- a/seed/rust-sdk/pagination-custom/src/api/types/username_page.rs +++ /dev/null @@ -1,44 +0,0 @@ -pub use crate::prelude::*; - -#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] -pub struct UsernamePage { - #[serde(skip_serializing_if = "Option::is_none")] - pub after: Option, - #[serde(default)] - pub data: Vec, -} - -impl UsernamePage { - pub fn builder() -> UsernamePageBuilder { - ::default() - } -} - -#[derive(Clone, PartialEq, Default, Debug)] -#[non_exhaustive] -pub struct UsernamePageBuilder { - after: Option, - data: Option>, -} - -impl UsernamePageBuilder { - pub fn after(mut self, value: impl Into) -> Self { - self.after = Some(value.into()); - self - } - - pub fn data(mut self, value: Vec) -> Self { - self.data = Some(value); - self - } - - /// Consumes the builder and constructs a [`UsernamePage`]. - /// This method will fail if any of the following fields are not set: - /// - [`data`](UsernamePageBuilder::data) - pub fn build(self) -> Result { - Ok(UsernamePage { - after: self.after, - data: self.data.ok_or_else(|| BuildError::missing_field("data"))?, - }) - } -} diff --git a/seed/rust-sdk/pagination-custom/src/api/types/users_list_response.rs b/seed/rust-sdk/pagination-custom/src/api/types/users_list_response.rs new file mode 100644 index 000000000000..3c1d0495304a --- /dev/null +++ b/seed/rust-sdk/pagination-custom/src/api/types/users_list_response.rs @@ -0,0 +1,74 @@ +pub use crate::prelude::*; + +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] +pub struct UsersListResponse { + #[serde(skip_serializing_if = "Option::is_none")] + pub limit: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub count: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub has_more: Option, + #[serde(default)] + pub links: Vec, + #[serde(default)] + pub data: Vec, +} + +impl UsersListResponse { + pub fn builder() -> UsersListResponseBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct UsersListResponseBuilder { + limit: Option, + count: Option, + has_more: Option, + links: Option>, + data: Option>, +} + +impl UsersListResponseBuilder { + pub fn limit(mut self, value: i64) -> Self { + self.limit = Some(value); + self + } + + pub fn count(mut self, value: i64) -> Self { + self.count = Some(value); + self + } + + pub fn has_more(mut self, value: bool) -> Self { + self.has_more = Some(value); + self + } + + pub fn links(mut self, value: Vec) -> Self { + self.links = Some(value); + self + } + + pub fn data(mut self, value: Vec) -> Self { + self.data = Some(value); + self + } + + /// Consumes the builder and constructs a [`UsersListResponse`]. + /// This method will fail if any of the following fields are not set: + /// - [`links`](UsersListResponseBuilder::links) + /// - [`data`](UsersListResponseBuilder::data) + pub fn build(self) -> Result { + Ok(UsersListResponse { + limit: self.limit, + count: self.count, + has_more: self.has_more, + links: self + .links + .ok_or_else(|| BuildError::missing_field("links"))?, + data: self.data.ok_or_else(|| BuildError::missing_field("data"))?, + }) + } +} diff --git a/seed/rust-sdk/pagination-custom/src/lib.rs b/seed/rust-sdk/pagination-custom/src/lib.rs index b9c0377c17b0..49faf53bd479 100644 --- a/seed/rust-sdk/pagination-custom/src/lib.rs +++ b/seed/rust-sdk/pagination-custom/src/lib.rs @@ -16,8 +16,9 @@ //! let client = PaginationClient::new(config).expect("Failed to build client"); //! client //! .users -//! .list_usernames_custom( -//! &ListUsernamesCustomQueryRequest { +//! .list_with_custom_pager( +//! &ListWithCustomPagerQueryRequest { +//! limit: Some(1), //! starting_after: Some("starting_after".to_string()), //! ..Default::default() //! }, diff --git a/seed/rust-sdk/pagination/README.md b/seed/rust-sdk/pagination/README.md index 7372e3a77a28..904ba0a61393 100644 --- a/seed/rust-sdk/pagination/README.md +++ b/seed/rust-sdk/pagination/README.md @@ -98,7 +98,7 @@ match client.complex.search(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_pagination::prelude::*; +use seed_pagination::prelude::{*}; let request = ListUsersBodyCursorPaginationRequest { ... diff --git a/seed/rust-sdk/query-param-name-conflict/README.md b/seed/rust-sdk/query-param-name-conflict/README.md index 33d56b48eca7..e00ee985f4b8 100644 --- a/seed/rust-sdk/query-param-name-conflict/README.md +++ b/seed/rust-sdk/query-param-name-conflict/README.md @@ -85,7 +85,7 @@ match client.bulk_update_tasks(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_api::prelude::*; +use seed_api::prelude::{*}; let request = BulkUpdateTasksRequest { ... diff --git a/seed/rust-sdk/request-parameters/README.md b/seed/rust-sdk/request-parameters/README.md index c6193dada55c..4a4fdb0ccec1 100644 --- a/seed/rust-sdk/request-parameters/README.md +++ b/seed/rust-sdk/request-parameters/README.md @@ -89,7 +89,7 @@ match client.user.create_username(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_request_parameters::prelude::*; +use seed_request_parameters::prelude::{*}; let request = CreateUsernameRequest { ... diff --git a/seed/rust-sdk/required-nullable/README.md b/seed/rust-sdk/required-nullable/README.md index f43488f89f70..c07936dafbf8 100644 --- a/seed/rust-sdk/required-nullable/README.md +++ b/seed/rust-sdk/required-nullable/README.md @@ -88,7 +88,7 @@ match client.get_foo(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_api::prelude::*; +use seed_api::prelude::{*}; let request = UpdateFooRequest { ... diff --git a/seed/rust-sdk/schemaless-request-body-examples/README.md b/seed/rust-sdk/schemaless-request-body-examples/README.md index fb4665de4a70..d729d6b8118c 100644 --- a/seed/rust-sdk/schemaless-request-body-examples/README.md +++ b/seed/rust-sdk/schemaless-request-body-examples/README.md @@ -78,7 +78,7 @@ match client.create_plant(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_api::prelude::*; +use seed_api::prelude::{*}; let request = CreatePlantWithSchemaRequest { ... diff --git a/seed/rust-sdk/server-sent-event-examples/with-wire-tests/README.md b/seed/rust-sdk/server-sent-event-examples/with-wire-tests/README.md index 6ee713b28a8b..70ed49465c4a 100644 --- a/seed/rust-sdk/server-sent-event-examples/with-wire-tests/README.md +++ b/seed/rust-sdk/server-sent-event-examples/with-wire-tests/README.md @@ -86,7 +86,7 @@ match client.completions.stream(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_server_sent_events::prelude::*; +use seed_server_sent_events::prelude::{*}; let request = StreamCompletionRequest { ... diff --git a/seed/rust-sdk/server-sent-events/with-wire-tests/README.md b/seed/rust-sdk/server-sent-events/with-wire-tests/README.md index 6ee713b28a8b..70ed49465c4a 100644 --- a/seed/rust-sdk/server-sent-events/with-wire-tests/README.md +++ b/seed/rust-sdk/server-sent-events/with-wire-tests/README.md @@ -86,7 +86,7 @@ match client.completions.stream(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_server_sent_events::prelude::*; +use seed_server_sent_events::prelude::{*}; let request = StreamCompletionRequest { ... diff --git a/seed/rust-sdk/server-url-templating/README.md b/seed/rust-sdk/server-url-templating/README.md index 603c1ae630c0..5e5c00289ba9 100644 --- a/seed/rust-sdk/server-url-templating/README.md +++ b/seed/rust-sdk/server-url-templating/README.md @@ -69,7 +69,7 @@ async fn main() { This SDK allows you to configure different environments for API requests. ```rust -use seed_api::prelude::*; +use seed_api::prelude::{*}; let config = ClientConfig { base_url: Environment::RegionalApiServer.url().to_string(), @@ -101,7 +101,7 @@ match client.get_token(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_api::prelude::*; +use seed_api::prelude::{*}; let request = TokenRequest { ... diff --git a/seed/rust-sdk/simple-api/basic-custom-config/README.md b/seed/rust-sdk/simple-api/basic-custom-config/README.md index c90f0dd0d841..50b2879fb0a3 100644 --- a/seed/rust-sdk/simple-api/basic-custom-config/README.md +++ b/seed/rust-sdk/simple-api/basic-custom-config/README.md @@ -61,7 +61,7 @@ async fn main() { This SDK allows you to configure different environments for API requests. ```rust -use custom_simple_sdk::prelude::*; +use custom_simple_sdk::prelude::{*}; let config = ClientConfig { base_url: Environment::Production.url().to_string(), diff --git a/seed/rust-sdk/simple-api/basic/README.md b/seed/rust-sdk/simple-api/basic/README.md index e05d3e8a6499..976cdefacc10 100644 --- a/seed/rust-sdk/simple-api/basic/README.md +++ b/seed/rust-sdk/simple-api/basic/README.md @@ -61,7 +61,7 @@ async fn main() { This SDK allows you to configure different environments for API requests. ```rust -use seed_simple_api::prelude::*; +use seed_simple_api::prelude::{*}; let config = ClientConfig { base_url: Environment::Production.url().to_string(), diff --git a/seed/rust-sdk/single-url-environment-default/basic/README.md b/seed/rust-sdk/single-url-environment-default/basic/README.md index 165bfd0665ab..c4866d8058c1 100644 --- a/seed/rust-sdk/single-url-environment-default/basic/README.md +++ b/seed/rust-sdk/single-url-environment-default/basic/README.md @@ -61,7 +61,7 @@ async fn main() { This SDK allows you to configure different environments for API requests. ```rust -use seed_single_url_environment_default::prelude::*; +use seed_single_url_environment_default::prelude::{*}; let config = ClientConfig { base_url: Environment::Production.url().to_string(), diff --git a/seed/rust-sdk/single-url-environment-default/custom-environment/README.md b/seed/rust-sdk/single-url-environment-default/custom-environment/README.md index 7037906b63ab..e2b33ba85d96 100644 --- a/seed/rust-sdk/single-url-environment-default/custom-environment/README.md +++ b/seed/rust-sdk/single-url-environment-default/custom-environment/README.md @@ -61,7 +61,7 @@ async fn main() { This SDK allows you to configure different environments for API requests. ```rust -use environment_test_sdk::prelude::*; +use environment_test_sdk::prelude::{*}; let config = ClientConfig { base_url: CustomApiEnvironment::Production.url().to_string(), diff --git a/seed/rust-sdk/single-url-environment-default/full-custom/README.md b/seed/rust-sdk/single-url-environment-default/full-custom/README.md index 90c0bc43632a..a422ae2e7239 100644 --- a/seed/rust-sdk/single-url-environment-default/full-custom/README.md +++ b/seed/rust-sdk/single-url-environment-default/full-custom/README.md @@ -75,7 +75,7 @@ async fn main() { This SDK allows you to configure different environments for API requests. ```rust -use full_custom_sdk::prelude::*; +use full_custom_sdk::prelude::{*}; let config = ClientConfig { base_url: MyCustomEnvironment::Production.url().to_string(), diff --git a/seed/rust-sdk/single-url-environment-no-default/README.md b/seed/rust-sdk/single-url-environment-no-default/README.md index 699a8470d247..dc686171858e 100644 --- a/seed/rust-sdk/single-url-environment-no-default/README.md +++ b/seed/rust-sdk/single-url-environment-no-default/README.md @@ -61,7 +61,7 @@ async fn main() { This SDK allows you to configure different environments for API requests. ```rust -use seed_single_url_environment_no_default::prelude::*; +use seed_single_url_environment_no_default::prelude::{*}; let config = ClientConfig { base_url: Environment::Production.url().to_string(), diff --git a/seed/rust-sdk/streaming-parameter/README.md b/seed/rust-sdk/streaming-parameter/README.md index f133a722ccf7..9ec3c6a63e85 100644 --- a/seed/rust-sdk/streaming-parameter/README.md +++ b/seed/rust-sdk/streaming-parameter/README.md @@ -87,7 +87,7 @@ match client.dummy.generate(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_streaming::prelude::*; +use seed_streaming::prelude::{*}; let request = GenerateRequest { ... diff --git a/seed/rust-sdk/streaming/README.md b/seed/rust-sdk/streaming/README.md index 0e0c0ac68450..3babd57ddc18 100644 --- a/seed/rust-sdk/streaming/README.md +++ b/seed/rust-sdk/streaming/README.md @@ -87,7 +87,7 @@ match client.dummy.generate_stream(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_streaming::prelude::*; +use seed_streaming::prelude::{*}; let request = GenerateStreamRequest { ... diff --git a/seed/rust-sdk/trace/README.md b/seed/rust-sdk/trace/README.md index e46c7ae2e9b2..89176cae5f43 100644 --- a/seed/rust-sdk/trace/README.md +++ b/seed/rust-sdk/trace/README.md @@ -69,7 +69,7 @@ async fn main() { This SDK allows you to configure different environments for API requests. ```rust -use seed_trace::prelude::*; +use seed_trace::prelude::{*}; let config = ClientConfig { base_url: Environment::Prod.url().to_string(), @@ -101,7 +101,7 @@ match client.admin.update_test_submission_status(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_trace::prelude::*; +use seed_trace::prelude::{*}; let request = StoreTracedTestCaseRequest { ... diff --git a/seed/rust-sdk/undiscriminated-unions/README.md b/seed/rust-sdk/undiscriminated-unions/README.md index ef8458e1d7dc..223d443afc04 100644 --- a/seed/rust-sdk/undiscriminated-unions/README.md +++ b/seed/rust-sdk/undiscriminated-unions/README.md @@ -81,7 +81,7 @@ match client.union_.get(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_undiscriminated_unions::prelude::*; +use seed_undiscriminated_unions::prelude::{*}; let request = PaymentRequest { ... diff --git a/seed/rust-sdk/url-form-encoded/README.md b/seed/rust-sdk/url-form-encoded/README.md index 9651433ff14c..eff21a8d8124 100644 --- a/seed/rust-sdk/url-form-encoded/README.md +++ b/seed/rust-sdk/url-form-encoded/README.md @@ -86,7 +86,7 @@ match client.submit_form_data(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_api::prelude::*; +use seed_api::prelude::{*}; let request = PostSubmitRequest { ... diff --git a/seed/rust-sdk/validation/README.md b/seed/rust-sdk/validation/README.md index 66eab5b46829..6a296286af3c 100644 --- a/seed/rust-sdk/validation/README.md +++ b/seed/rust-sdk/validation/README.md @@ -88,7 +88,7 @@ match client.create(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_validation::prelude::*; +use seed_validation::prelude::{*}; let request = CreateRequest { ... diff --git a/seed/rust-sdk/websocket-inferred-auth/README.md b/seed/rust-sdk/websocket-inferred-auth/README.md index b355e4e93a94..836bd2c79bfd 100644 --- a/seed/rust-sdk/websocket-inferred-auth/README.md +++ b/seed/rust-sdk/websocket-inferred-auth/README.md @@ -91,7 +91,7 @@ match client.auth.get_token_with_client_credentials(None)?.await { The SDK exports all request types as Rust structs. Simply import them from the crate to access them: ```rust -use seed_websocket_auth::prelude::*; +use seed_websocket_auth::prelude::{*}; let request = GetTokenRequest { ... diff --git a/seed/rust-sdk/websocket/src/api/mod.rs b/seed/rust-sdk/websocket/src/api/mod.rs index 1641e4a8125a..abae3255cca5 100644 --- a/seed/rust-sdk/websocket/src/api/mod.rs +++ b/seed/rust-sdk/websocket/src/api/mod.rs @@ -14,5 +14,5 @@ pub mod types; pub mod websocket; pub use resources::{EmptyClient, WebsocketClient}; -pub use types::*; +pub use types::{*}; diff --git a/seed/rust-sdk/websocket/src/api/websocket/realtime.rs b/seed/rust-sdk/websocket/src/api/websocket/realtime.rs index 10d0c48aab5d..4ce381a318fd 100644 --- a/seed/rust-sdk/websocket/src/api/websocket/realtime.rs +++ b/seed/rust-sdk/websocket/src/api/websocket/realtime.rs @@ -1,6 +1,6 @@ use crate::{ApiError, WebSocketClient, WebSocketMessage, WebSocketOptions, QueryBuilder}; use tokio::sync::{mpsc}; -use crate::prelude::*; +use crate::prelude::{*}; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Default, Serialize, Deserialize)] diff --git a/seed/rust-sdk/websocket/src/lib.rs b/seed/rust-sdk/websocket/src/lib.rs index 2fbb0fe7809d..86b38ea6f698 100644 --- a/seed/rust-sdk/websocket/src/lib.rs +++ b/seed/rust-sdk/websocket/src/lib.rs @@ -21,8 +21,8 @@ pub mod client; pub mod prelude; pub use error::{ApiError, BuildError}; -pub use api::*; -pub use core::*; -pub use config::*; -pub use client::*; +pub use api::{*}; +pub use core::{*}; +pub use config::{*}; +pub use client::{*}; From 4a2d2de405984f83a58bcfd0adb8202744a463a7 Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:20:44 -0400 Subject: [PATCH 10/21] chore(swift): update swift-sdk seed (#14699) Co-authored-by: davidkonigsberg <72822263+davidkonigsberg@users.noreply.github.com> --- seed/swift-sdk/pagination-custom/README.md | 13 ++-- .../pagination-custom/Snippets/Example0.swift | 5 +- .../Sources/Resources/Users/UsersClient.swift | 5 +- .../{UsernameCursor.swift => Link.swift} | 26 +++++--- ...namePage.swift => UsersListResponse.swift} | 32 +++++++--- .../Tests/Core/ClientErrorTests.swift | 21 ++++--- .../Tests/Core/ClientRetryTests.swift | 39 ++++++++---- .../Users/UsersClientWireTests.swift | 59 +++++++++++++------ seed/swift-sdk/pagination-custom/reference.md | 18 ++++-- 9 files changed, 154 insertions(+), 64 deletions(-) rename seed/swift-sdk/pagination-custom/Sources/Schemas/{UsernameCursor.swift => Link.swift} (54%) rename seed/swift-sdk/pagination-custom/Sources/Schemas/{UsernamePage.swift => UsersListResponse.swift} (50%) diff --git a/seed/swift-sdk/pagination-custom/README.md b/seed/swift-sdk/pagination-custom/README.md index dc25dc330de7..43fef957dcce 100644 --- a/seed/swift-sdk/pagination-custom/README.md +++ b/seed/swift-sdk/pagination-custom/README.md @@ -53,7 +53,10 @@ import Pagination private func main() async throws { let client = PaginationClient(token: "") - _ = try await client.users.listUsernamesCustom(startingAfter: "starting_after") + _ = try await client.users.listWithCustomPager( + limit: 1, + startingAfter: "starting_after" + ) } try await main() @@ -69,7 +72,7 @@ import Pagination let client = PaginationClient(...) do { - let response = try await client.users.listUsernamesCustom(...) + let response = try await client.users.listWithCustomPager(...) // Handle successful response } catch let error as PaginationError { switch error { @@ -96,7 +99,7 @@ do { If you would like to send additional headers as part of the request, use the `additionalHeaders` request option. ```swift -try await client.users.listUsernamesCustom(..., requestOptions: .init( +try await client.users.listWithCustomPager(..., requestOptions: .init( additionalHeaders: [ "X-Custom-Header": "custom value" ] @@ -108,7 +111,7 @@ try await client.users.listUsernamesCustom(..., requestOptions: .init( If you would like to send additional query string parameters as part of the request, use the `additionalQueryParameters` request option. ```swift -try await client.users.listUsernamesCustom(..., requestOptions: .init( +try await client.users.listWithCustomPager(..., requestOptions: .init( additionalQueryParameters: [ "custom_query_param_key": "custom_query_param_value" ] @@ -120,7 +123,7 @@ try await client.users.listUsernamesCustom(..., requestOptions: .init( The SDK defaults to a 60-second timeout. Use the `timeout` option to configure this behavior. ```swift -try await client.users.listUsernamesCustom(..., requestOptions: .init( +try await client.users.listWithCustomPager(..., requestOptions: .init( timeout: 30 )) ``` diff --git a/seed/swift-sdk/pagination-custom/Snippets/Example0.swift b/seed/swift-sdk/pagination-custom/Snippets/Example0.swift index c2088f6b870d..9767d164fac3 100644 --- a/seed/swift-sdk/pagination-custom/Snippets/Example0.swift +++ b/seed/swift-sdk/pagination-custom/Snippets/Example0.swift @@ -7,7 +7,10 @@ private func main() async throws { token: "" ) - _ = try await client.users.listUsernamesCustom(startingAfter: "starting_after") + _ = try await client.users.listWithCustomPager( + limit: 1, + startingAfter: "starting_after" + ) } try await main() diff --git a/seed/swift-sdk/pagination-custom/Sources/Resources/Users/UsersClient.swift b/seed/swift-sdk/pagination-custom/Sources/Resources/Users/UsersClient.swift index 6b352eaf3c9d..3cb542786aaf 100644 --- a/seed/swift-sdk/pagination-custom/Sources/Resources/Users/UsersClient.swift +++ b/seed/swift-sdk/pagination-custom/Sources/Resources/Users/UsersClient.swift @@ -7,15 +7,16 @@ public final class UsersClient: Sendable { self.httpClient = HTTPClient(config: config) } - public func listUsernamesCustom(startingAfter: String? = nil, requestOptions: RequestOptions? = nil) async throws -> UsernameCursor { + public func listWithCustomPager(limit: Int? = nil, startingAfter: String? = nil, requestOptions: RequestOptions? = nil) async throws -> UsersListResponse { return try await httpClient.performRequest( method: .get, path: "/users", queryParams: [ + "limit": limit.map { .int($0) }, "starting_after": startingAfter.map { .string($0) } ], requestOptions: requestOptions, - responseType: UsernameCursor.self + responseType: UsersListResponse.self ) } } \ No newline at end of file diff --git a/seed/swift-sdk/pagination-custom/Sources/Schemas/UsernameCursor.swift b/seed/swift-sdk/pagination-custom/Sources/Schemas/Link.swift similarity index 54% rename from seed/swift-sdk/pagination-custom/Sources/Schemas/UsernameCursor.swift rename to seed/swift-sdk/pagination-custom/Sources/Schemas/Link.swift index 5ecbf8d6c639..95d7c5848df6 100644 --- a/seed/swift-sdk/pagination-custom/Sources/Schemas/UsernameCursor.swift +++ b/seed/swift-sdk/pagination-custom/Sources/Schemas/Link.swift @@ -1,32 +1,44 @@ import Foundation -public struct UsernameCursor: Codable, Hashable, Sendable { - public let cursor: UsernamePage +public struct Link: Codable, Hashable, Sendable { + public let rel: String + public let method: String + public let href: String /// Additional properties that are not explicitly defined in the schema public let additionalProperties: [String: JSONValue] public init( - cursor: UsernamePage, + rel: String, + method: String, + href: String, additionalProperties: [String: JSONValue] = .init() ) { - self.cursor = cursor + self.rel = rel + self.method = method + self.href = href self.additionalProperties = additionalProperties } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - self.cursor = try container.decode(UsernamePage.self, forKey: .cursor) + self.rel = try container.decode(String.self, forKey: .rel) + self.method = try container.decode(String.self, forKey: .method) + self.href = try container.decode(String.self, forKey: .href) self.additionalProperties = try decoder.decodeAdditionalProperties(using: CodingKeys.self) } public func encode(to encoder: Encoder) throws -> Void { var container = encoder.container(keyedBy: CodingKeys.self) try encoder.encodeAdditionalProperties(self.additionalProperties) - try container.encode(self.cursor, forKey: .cursor) + try container.encode(self.rel, forKey: .rel) + try container.encode(self.method, forKey: .method) + try container.encode(self.href, forKey: .href) } /// Keys for encoding/decoding struct properties. enum CodingKeys: String, CodingKey, CaseIterable { - case cursor + case rel + case method + case href } } \ No newline at end of file diff --git a/seed/swift-sdk/pagination-custom/Sources/Schemas/UsernamePage.swift b/seed/swift-sdk/pagination-custom/Sources/Schemas/UsersListResponse.swift similarity index 50% rename from seed/swift-sdk/pagination-custom/Sources/Schemas/UsernamePage.swift rename to seed/swift-sdk/pagination-custom/Sources/Schemas/UsersListResponse.swift index 04c1af39bf8e..6437def2f593 100644 --- a/seed/swift-sdk/pagination-custom/Sources/Schemas/UsernamePage.swift +++ b/seed/swift-sdk/pagination-custom/Sources/Schemas/UsersListResponse.swift @@ -1,24 +1,36 @@ import Foundation -public struct UsernamePage: Codable, Hashable, Sendable { - public let after: String? +public struct UsersListResponse: Codable, Hashable, Sendable { + public let limit: Int? + public let count: Int? + public let hasMore: Bool? + public let links: [Link] public let data: [String] /// Additional properties that are not explicitly defined in the schema public let additionalProperties: [String: JSONValue] public init( - after: String? = nil, + limit: Int? = nil, + count: Int? = nil, + hasMore: Bool? = nil, + links: [Link], data: [String], additionalProperties: [String: JSONValue] = .init() ) { - self.after = after + self.limit = limit + self.count = count + self.hasMore = hasMore + self.links = links self.data = data self.additionalProperties = additionalProperties } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - self.after = try container.decodeIfPresent(String.self, forKey: .after) + self.limit = try container.decodeIfPresent(Int.self, forKey: .limit) + self.count = try container.decodeIfPresent(Int.self, forKey: .count) + self.hasMore = try container.decodeIfPresent(Bool.self, forKey: .hasMore) + self.links = try container.decode([Link].self, forKey: .links) self.data = try container.decode([String].self, forKey: .data) self.additionalProperties = try decoder.decodeAdditionalProperties(using: CodingKeys.self) } @@ -26,13 +38,19 @@ public struct UsernamePage: Codable, Hashable, Sendable { public func encode(to encoder: Encoder) throws -> Void { var container = encoder.container(keyedBy: CodingKeys.self) try encoder.encodeAdditionalProperties(self.additionalProperties) - try container.encodeIfPresent(self.after, forKey: .after) + try container.encodeIfPresent(self.limit, forKey: .limit) + try container.encodeIfPresent(self.count, forKey: .count) + try container.encodeIfPresent(self.hasMore, forKey: .hasMore) + try container.encode(self.links, forKey: .links) try container.encode(self.data, forKey: .data) } /// Keys for encoding/decoding struct properties. enum CodingKeys: String, CodingKey, CaseIterable { - case after + case limit + case count + case hasMore = "has_more" + case links case data } } \ No newline at end of file diff --git a/seed/swift-sdk/pagination-custom/Tests/Core/ClientErrorTests.swift b/seed/swift-sdk/pagination-custom/Tests/Core/ClientErrorTests.swift index 8f6640ccde47..5ee4093a5fc1 100644 --- a/seed/swift-sdk/pagination-custom/Tests/Core/ClientErrorTests.swift +++ b/seed/swift-sdk/pagination-custom/Tests/Core/ClientErrorTests.swift @@ -20,7 +20,8 @@ import Testing ) do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -54,7 +55,8 @@ import Testing ) do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -88,7 +90,8 @@ import Testing ) do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -124,7 +127,8 @@ import Testing ) do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -158,7 +162,8 @@ import Testing ) do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -194,7 +199,8 @@ import Testing ) do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -228,7 +234,8 @@ import Testing ) do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) diff --git a/seed/swift-sdk/pagination-custom/Tests/Core/ClientRetryTests.swift b/seed/swift-sdk/pagination-custom/Tests/Core/ClientRetryTests.swift index 84a857c32b78..301f6902205b 100644 --- a/seed/swift-sdk/pagination-custom/Tests/Core/ClientRetryTests.swift +++ b/seed/swift-sdk/pagination-custom/Tests/Core/ClientRetryTests.swift @@ -21,7 +21,8 @@ import Testing ) do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -49,7 +50,8 @@ import Testing ) do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -77,7 +79,8 @@ import Testing ) do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -104,7 +107,8 @@ import Testing ) do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -130,7 +134,8 @@ import Testing ) do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -157,7 +162,8 @@ import Testing ) do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -184,7 +190,8 @@ import Testing ) do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -216,7 +223,8 @@ import Testing let startTime = Date() do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -258,7 +266,8 @@ import Testing let startTime = Date() do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -296,7 +305,8 @@ import Testing let startTime = Date() do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -345,7 +355,8 @@ import Testing ) do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(maxRetries: 5, additionalHeaders: stub.headers) ) @@ -368,7 +379,8 @@ import Testing ) do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(maxRetries: 0, additionalHeaders: stub.headers) ) @@ -395,7 +407,8 @@ import Testing ) do { - _ = try await client.users.listUsernamesCustom( + _ = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) diff --git a/seed/swift-sdk/pagination-custom/Tests/Wire/Resources/Users/UsersClientWireTests.swift b/seed/swift-sdk/pagination-custom/Tests/Wire/Resources/Users/UsersClientWireTests.swift index c1e3bb2d8797..75831bf9d458 100644 --- a/seed/swift-sdk/pagination-custom/Tests/Wire/Resources/Users/UsersClientWireTests.swift +++ b/seed/swift-sdk/pagination-custom/Tests/Wire/Resources/Users/UsersClientWireTests.swift @@ -3,19 +3,31 @@ import Testing import Pagination @Suite("UsersClient Wire Tests") struct UsersClientWireTests { - @Test func listUsernamesCustom1() async throws -> Void { + @Test func listWithCustomPager1() async throws -> Void { let stub = HTTPStub() stub.setResponse( body: Data( """ { - "cursor": { - "after": "after", - "data": [ - "data", - "data" - ] - } + "limit": 1, + "count": 1, + "has_more": true, + "links": [ + { + "rel": "rel", + "method": "method", + "href": "href" + }, + { + "rel": "rel", + "method": "method", + "href": "href" + } + ], + "data": [ + "data", + "data" + ] } """.utf8 ) @@ -25,16 +37,29 @@ import Pagination token: "", urlSession: stub.urlSession ) - let expectedResponse = UsernameCursor( - cursor: UsernamePage( - after: Optional("after"), - data: [ - "data", - "data" - ] - ) + let expectedResponse = UsersListResponse( + limit: Optional(1), + count: Optional(1), + hasMore: Optional(true), + links: [ + Link( + rel: "rel", + method: "method", + href: "href" + ), + Link( + rel: "rel", + method: "method", + href: "href" + ) + ], + data: [ + "data", + "data" + ] ) - let response = try await client.users.listUsernamesCustom( + let response = try await client.users.listWithCustomPager( + limit: 1, startingAfter: "starting_after", requestOptions: RequestOptions(additionalHeaders: stub.headers) ) diff --git a/seed/swift-sdk/pagination-custom/reference.md b/seed/swift-sdk/pagination-custom/reference.md index ed855ef66af3..74747fdc5a8a 100644 --- a/seed/swift-sdk/pagination-custom/reference.md +++ b/seed/swift-sdk/pagination-custom/reference.md @@ -1,6 +1,6 @@ # Reference ## Users -
client.users.listUsernamesCustom(startingAfter: String?, requestOptions: RequestOptions?) -> UsernameCursor +
client.users.listWithCustomPager(limit: Int?, startingAfter: String?, requestOptions: RequestOptions?) -> UsersListResponse
@@ -19,7 +19,10 @@ import Pagination private func main() async throws { let client = PaginationClient(token: "") - _ = try await client.users.listUsernamesCustom(startingAfter: "starting_after") + _ = try await client.users.listWithCustomPager( + limit: 1, + startingAfter: "starting_after" + ) } try await main() @@ -37,10 +40,15 @@ try await main()
-**startingAfter:** `String?` +**limit:** `Int?` — The maximum number of results to return. + +
+
+ +
+
-The cursor used for pagination in order to fetch -the next page of results. +**startingAfter:** `String?` — The cursor used for pagination.
From ecd4a89a88d20afd849b4c8c5129e9a4ef169904 Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:24:13 -0400 Subject: [PATCH 11/21] chore(ruby): update ruby-sdk-v2 seed (#14703) Co-authored-by: davidkonigsberg <72822263+davidkonigsberg@users.noreply.github.com> --- seed/ruby-sdk-v2/any-auth/seed.gemspec | 1 - .../seed.gemspec | 1 - .../basic-auth-pw-omitted/seed.gemspec | 1 - .../basic-auth/wire-tests/seed.gemspec | 1 - seed/ruby-sdk-v2/empty-clients/Gemfile | 8 ++------ .../endpoint-security-auth/seed.gemspec | 1 - .../examples/no-custom-config/Gemfile | 8 ++------ .../examples/omit-fern-headers/Gemfile | 8 ++------ .../ruby-sdk-v2/examples/readme-config/Gemfile | 8 ++------ .../ruby-sdk-v2/examples/require-paths/Gemfile | 8 ++------ seed/ruby-sdk-v2/examples/wire-tests/Gemfile | 8 ++------ seed/ruby-sdk-v2/folders/Gemfile | 8 ++------ .../ruby-sdk-v2/inferred-auth-explicit/Gemfile | 8 ++------ .../inferred-auth-implicit-no-expiry/Gemfile | 8 ++------ .../inferred-auth-implicit-reference/Gemfile | 8 ++------ seed/ruby-sdk-v2/mixed-file-directory/Gemfile | 8 ++------ seed/ruby-sdk-v2/no-content-response/Gemfile | 8 ++------ seed/ruby-sdk-v2/null-type/Gemfile | 8 ++------ .../ruby-sdk-v2/nullable-allof-extends/Gemfile | 8 ++------ seed/ruby-sdk-v2/nullable-request-body/Gemfile | 8 ++------ .../oauth-client-credentials-custom/Gemfile | 8 ++------ .../oauth-client-credentials-default/Gemfile | 8 ++------ .../Gemfile | 8 ++------ .../Gemfile | 8 ++------ .../Gemfile | 8 ++------ .../oauth-client-credentials-openapi/Gemfile | 8 ++------ .../Gemfile | 8 ++------ .../oauth-client-credentials/Gemfile | 8 ++------ seed/ruby-sdk-v2/objects-with-imports/Gemfile | 8 ++------ seed/ruby-sdk-v2/pagination-custom/README.md | 17 ++++++++++------- .../dynamic-snippets/example0/snippet.rb | 5 ++++- seed/ruby-sdk-v2/pagination-custom/lib/seed.rb | 6 +++--- .../pagination-custom/lib/seed/types/link.rb | 11 +++++++++++ .../lib/seed/types/username_cursor.rb | 9 --------- .../lib/seed/types/username_page.rb | 10 ---------- .../lib/seed/types/users_list_response.rb | 13 +++++++++++++ .../pagination-custom/lib/seed/users/client.rb | 10 ++++++---- ...om.rb => list_with_custom_pager_request.rb} | 3 ++- .../ruby-sdk-v2/pagination-custom/reference.md | 18 +++++++++++++----- seed/ruby-sdk-v2/pagination/Gemfile | 8 ++------ .../query-param-name-conflict/Gemfile | 8 ++------ .../Gemfile | 8 ++------ .../query-parameters-openapi/Gemfile | 8 ++------ .../schemaless-request-body-examples/Gemfile | 8 ++------ .../server-sent-events-openapi/Gemfile | 8 ++------ seed/ruby-sdk-v2/server-url-templating/Gemfile | 8 ++------ seed/ruby-sdk-v2/trace/Gemfile | 8 ++------ seed/ruby-sdk-v2/url-form-encoded/Gemfile | 8 ++------ seed/ruby-sdk-v2/webhook-audience/Gemfile | 8 ++------ seed/ruby-sdk-v2/websocket-multi-url/Gemfile | 8 ++------ seed/ruby-sdk-v2/websocket/Gemfile | 8 ++------ seed/ruby-sdk-v2/x-fern-default/Gemfile | 8 ++------ 52 files changed, 136 insertions(+), 267 deletions(-) create mode 100644 seed/ruby-sdk-v2/pagination-custom/lib/seed/types/link.rb delete mode 100644 seed/ruby-sdk-v2/pagination-custom/lib/seed/types/username_cursor.rb delete mode 100644 seed/ruby-sdk-v2/pagination-custom/lib/seed/types/username_page.rb create mode 100644 seed/ruby-sdk-v2/pagination-custom/lib/seed/types/users_list_response.rb rename seed/ruby-sdk-v2/pagination-custom/lib/seed/users/types/{list_usernames_request_custom.rb => list_with_custom_pager_request.rb} (57%) diff --git a/seed/ruby-sdk-v2/any-auth/seed.gemspec b/seed/ruby-sdk-v2/any-auth/seed.gemspec index 058687df2754..ae219da82f93 100644 --- a/seed/ruby-sdk-v2/any-auth/seed.gemspec +++ b/seed/ruby-sdk-v2/any-auth/seed.gemspec @@ -26,7 +26,6 @@ Gem::Specification.new do |spec| spec.bindir = "exe" spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.add_dependency "base64" # For more information and examples about making a new gem, check out our # guide at: https://bundler.io/guides/creating_gem.html diff --git a/seed/ruby-sdk-v2/basic-auth-environment-variables/seed.gemspec b/seed/ruby-sdk-v2/basic-auth-environment-variables/seed.gemspec index d10a0c35d40b..2bd9fe5d1d8d 100644 --- a/seed/ruby-sdk-v2/basic-auth-environment-variables/seed.gemspec +++ b/seed/ruby-sdk-v2/basic-auth-environment-variables/seed.gemspec @@ -26,7 +26,6 @@ Gem::Specification.new do |spec| spec.bindir = "exe" spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.add_dependency "base64" # For more information and examples about making a new gem, check out our # guide at: https://bundler.io/guides/creating_gem.html diff --git a/seed/ruby-sdk-v2/basic-auth-pw-omitted/seed.gemspec b/seed/ruby-sdk-v2/basic-auth-pw-omitted/seed.gemspec index 2897e62684cc..6fdd2f7d837d 100644 --- a/seed/ruby-sdk-v2/basic-auth-pw-omitted/seed.gemspec +++ b/seed/ruby-sdk-v2/basic-auth-pw-omitted/seed.gemspec @@ -26,7 +26,6 @@ Gem::Specification.new do |spec| spec.bindir = "exe" spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.add_dependency "base64" # For more information and examples about making a new gem, check out our # guide at: https://bundler.io/guides/creating_gem.html diff --git a/seed/ruby-sdk-v2/basic-auth/wire-tests/seed.gemspec b/seed/ruby-sdk-v2/basic-auth/wire-tests/seed.gemspec index 8a6bc34c67ff..1b3bd323ed32 100644 --- a/seed/ruby-sdk-v2/basic-auth/wire-tests/seed.gemspec +++ b/seed/ruby-sdk-v2/basic-auth/wire-tests/seed.gemspec @@ -26,7 +26,6 @@ Gem::Specification.new do |spec| spec.bindir = "exe" spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.add_dependency "base64" # For more information and examples about making a new gem, check out our # guide at: https://bundler.io/guides/creating_gem.html diff --git a/seed/ruby-sdk-v2/empty-clients/Gemfile b/seed/ruby-sdk-v2/empty-clients/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/empty-clients/Gemfile +++ b/seed/ruby-sdk-v2/empty-clients/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/endpoint-security-auth/seed.gemspec b/seed/ruby-sdk-v2/endpoint-security-auth/seed.gemspec index 777a527c0a1f..2a49500dfa95 100644 --- a/seed/ruby-sdk-v2/endpoint-security-auth/seed.gemspec +++ b/seed/ruby-sdk-v2/endpoint-security-auth/seed.gemspec @@ -26,7 +26,6 @@ Gem::Specification.new do |spec| spec.bindir = "exe" spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.add_dependency "base64" # For more information and examples about making a new gem, check out our # guide at: https://bundler.io/guides/creating_gem.html diff --git a/seed/ruby-sdk-v2/examples/no-custom-config/Gemfile b/seed/ruby-sdk-v2/examples/no-custom-config/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/examples/no-custom-config/Gemfile +++ b/seed/ruby-sdk-v2/examples/no-custom-config/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/examples/omit-fern-headers/Gemfile b/seed/ruby-sdk-v2/examples/omit-fern-headers/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/examples/omit-fern-headers/Gemfile +++ b/seed/ruby-sdk-v2/examples/omit-fern-headers/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/examples/readme-config/Gemfile b/seed/ruby-sdk-v2/examples/readme-config/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/examples/readme-config/Gemfile +++ b/seed/ruby-sdk-v2/examples/readme-config/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/examples/require-paths/Gemfile b/seed/ruby-sdk-v2/examples/require-paths/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/examples/require-paths/Gemfile +++ b/seed/ruby-sdk-v2/examples/require-paths/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/examples/wire-tests/Gemfile b/seed/ruby-sdk-v2/examples/wire-tests/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/examples/wire-tests/Gemfile +++ b/seed/ruby-sdk-v2/examples/wire-tests/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/folders/Gemfile b/seed/ruby-sdk-v2/folders/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/folders/Gemfile +++ b/seed/ruby-sdk-v2/folders/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/inferred-auth-explicit/Gemfile b/seed/ruby-sdk-v2/inferred-auth-explicit/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/inferred-auth-explicit/Gemfile +++ b/seed/ruby-sdk-v2/inferred-auth-explicit/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-no-expiry/Gemfile b/seed/ruby-sdk-v2/inferred-auth-implicit-no-expiry/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/inferred-auth-implicit-no-expiry/Gemfile +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-no-expiry/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/Gemfile b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/Gemfile +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/mixed-file-directory/Gemfile b/seed/ruby-sdk-v2/mixed-file-directory/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/mixed-file-directory/Gemfile +++ b/seed/ruby-sdk-v2/mixed-file-directory/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/no-content-response/Gemfile b/seed/ruby-sdk-v2/no-content-response/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/no-content-response/Gemfile +++ b/seed/ruby-sdk-v2/no-content-response/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/null-type/Gemfile b/seed/ruby-sdk-v2/null-type/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/null-type/Gemfile +++ b/seed/ruby-sdk-v2/null-type/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/nullable-allof-extends/Gemfile b/seed/ruby-sdk-v2/nullable-allof-extends/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/nullable-allof-extends/Gemfile +++ b/seed/ruby-sdk-v2/nullable-allof-extends/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/nullable-request-body/Gemfile b/seed/ruby-sdk-v2/nullable-request-body/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/nullable-request-body/Gemfile +++ b/seed/ruby-sdk-v2/nullable-request-body/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/oauth-client-credentials-custom/Gemfile b/seed/ruby-sdk-v2/oauth-client-credentials-custom/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/oauth-client-credentials-custom/Gemfile +++ b/seed/ruby-sdk-v2/oauth-client-credentials-custom/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/oauth-client-credentials-default/Gemfile b/seed/ruby-sdk-v2/oauth-client-credentials-default/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/oauth-client-credentials-default/Gemfile +++ b/seed/ruby-sdk-v2/oauth-client-credentials-default/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/oauth-client-credentials-environment-variables/Gemfile b/seed/ruby-sdk-v2/oauth-client-credentials-environment-variables/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/oauth-client-credentials-environment-variables/Gemfile +++ b/seed/ruby-sdk-v2/oauth-client-credentials-environment-variables/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/oauth-client-credentials-mandatory-auth/Gemfile b/seed/ruby-sdk-v2/oauth-client-credentials-mandatory-auth/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/oauth-client-credentials-mandatory-auth/Gemfile +++ b/seed/ruby-sdk-v2/oauth-client-credentials-mandatory-auth/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/oauth-client-credentials-nested-root/Gemfile b/seed/ruby-sdk-v2/oauth-client-credentials-nested-root/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/oauth-client-credentials-nested-root/Gemfile +++ b/seed/ruby-sdk-v2/oauth-client-credentials-nested-root/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/oauth-client-credentials-openapi/Gemfile b/seed/ruby-sdk-v2/oauth-client-credentials-openapi/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/oauth-client-credentials-openapi/Gemfile +++ b/seed/ruby-sdk-v2/oauth-client-credentials-openapi/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/oauth-client-credentials-with-variables/Gemfile b/seed/ruby-sdk-v2/oauth-client-credentials-with-variables/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/oauth-client-credentials-with-variables/Gemfile +++ b/seed/ruby-sdk-v2/oauth-client-credentials-with-variables/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/oauth-client-credentials/Gemfile b/seed/ruby-sdk-v2/oauth-client-credentials/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/oauth-client-credentials/Gemfile +++ b/seed/ruby-sdk-v2/oauth-client-credentials/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/objects-with-imports/Gemfile b/seed/ruby-sdk-v2/objects-with-imports/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/objects-with-imports/Gemfile +++ b/seed/ruby-sdk-v2/objects-with-imports/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/pagination-custom/README.md b/seed/ruby-sdk-v2/pagination-custom/README.md index f5c59e78e3cd..84bbdd52d388 100644 --- a/seed/ruby-sdk-v2/pagination-custom/README.md +++ b/seed/ruby-sdk-v2/pagination-custom/README.md @@ -31,7 +31,10 @@ require "seed" client = Seed::Client.new(token: "") -client.users.list_usernames_custom(starting_after: "starting_after") +client.users.list_with_custom_pager( + limit: 1, + starting_after: "starting_after" +) ``` ## Environments @@ -55,14 +58,14 @@ List endpoints are paginated. The SDK provides an iterator so that you can simpl require "seed" # For custom pagination, the response is returned directly. -response = client.users.list_usernames_custom( +response = client.users.list_with_custom_pager( ... ) pager = Seed::Internal::FooPager.new( response, has_next_proc: ->(page) { page.has_more }, - get_next_proc: ->(page) { client.users.list_usernames_custom(cursor: page.next_cursor) } + get_next_proc: ->(page) { client.users.list_with_custom_pager(cursor: page.next_cursor) } ) # Iterate over pages @@ -83,7 +86,7 @@ client = Seed::Client.new( ) begin - result = client.users.list_usernames_custom + result = client.users.list_with_custom_pager rescue Seed::Errors::TimeoutError puts "API didn't respond before our timeout elapsed" rescue Seed::Errors::ServiceUnavailableError @@ -128,7 +131,7 @@ The SDK defaults to a 60 second timeout. Use the `timeout` option to configure t ```ruby require "seed" -response = client.users.list_usernames_custom( +response = client.users.list_with_custom_pager( ..., timeout: 30 # 30 second timeout ) @@ -141,7 +144,7 @@ If you would like to send additional headers as part of the request, use the `ad ```ruby require "seed" -response = client.users.list_usernames_custom( +response = client.users.list_with_custom_pager( ..., request_options: { additional_headers: { @@ -158,7 +161,7 @@ If you would like to send additional query parameters as part of the request, us ```ruby require "seed" -response = client.users.list_usernames_custom( +response = client.users.list_with_custom_pager( ..., request_options: { additional_query_parameters: { diff --git a/seed/ruby-sdk-v2/pagination-custom/dynamic-snippets/example0/snippet.rb b/seed/ruby-sdk-v2/pagination-custom/dynamic-snippets/example0/snippet.rb index 44bd45555756..73837fa41988 100644 --- a/seed/ruby-sdk-v2/pagination-custom/dynamic-snippets/example0/snippet.rb +++ b/seed/ruby-sdk-v2/pagination-custom/dynamic-snippets/example0/snippet.rb @@ -5,4 +5,7 @@ base_url: "https://api.fern.com" ) -client.users.list_usernames_custom(starting_after: "starting_after") +client.users.list_with_custom_pager( + limit: 1, + starting_after: "starting_after" +) diff --git a/seed/ruby-sdk-v2/pagination-custom/lib/seed.rb b/seed/ruby-sdk-v2/pagination-custom/lib/seed.rb index 2e8f4db77ef1..d6b9b33354e0 100644 --- a/seed/ruby-sdk-v2/pagination-custom/lib/seed.rb +++ b/seed/ruby-sdk-v2/pagination-custom/lib/seed.rb @@ -36,8 +36,8 @@ require_relative "seed/internal/iterators/cursor_page_iterator" require_relative "seed/internal/iterators/offset_page_iterator" require_relative "seed/internal/iterators/custom_pager" -require_relative "seed/types/username_page" -require_relative "seed/types/username_cursor" +require_relative "seed/types/link" +require_relative "seed/types/users_list_response" require_relative "seed/client" require_relative "seed/users/client" -require_relative "seed/users/types/list_usernames_request_custom" +require_relative "seed/users/types/list_with_custom_pager_request" diff --git a/seed/ruby-sdk-v2/pagination-custom/lib/seed/types/link.rb b/seed/ruby-sdk-v2/pagination-custom/lib/seed/types/link.rb new file mode 100644 index 000000000000..1d734c923219 --- /dev/null +++ b/seed/ruby-sdk-v2/pagination-custom/lib/seed/types/link.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Seed + module Types + class Link < Internal::Types::Model + field :rel, -> { String }, optional: false, nullable: false + field :method_, -> { String }, optional: false, nullable: false, api_name: "method" + field :href, -> { String }, optional: false, nullable: false + end + end +end diff --git a/seed/ruby-sdk-v2/pagination-custom/lib/seed/types/username_cursor.rb b/seed/ruby-sdk-v2/pagination-custom/lib/seed/types/username_cursor.rb deleted file mode 100644 index 4976e7c9e6ab..000000000000 --- a/seed/ruby-sdk-v2/pagination-custom/lib/seed/types/username_cursor.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -module Seed - module Types - class UsernameCursor < Internal::Types::Model - field :cursor, -> { Seed::Types::UsernamePage }, optional: false, nullable: false - end - end -end diff --git a/seed/ruby-sdk-v2/pagination-custom/lib/seed/types/username_page.rb b/seed/ruby-sdk-v2/pagination-custom/lib/seed/types/username_page.rb deleted file mode 100644 index b565b5be4863..000000000000 --- a/seed/ruby-sdk-v2/pagination-custom/lib/seed/types/username_page.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -module Seed - module Types - class UsernamePage < Internal::Types::Model - field :after, -> { String }, optional: true, nullable: false - field :data, -> { Internal::Types::Array[String] }, optional: false, nullable: false - end - end -end diff --git a/seed/ruby-sdk-v2/pagination-custom/lib/seed/types/users_list_response.rb b/seed/ruby-sdk-v2/pagination-custom/lib/seed/types/users_list_response.rb new file mode 100644 index 000000000000..9a446c3319c1 --- /dev/null +++ b/seed/ruby-sdk-v2/pagination-custom/lib/seed/types/users_list_response.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Seed + module Types + class UsersListResponse < Internal::Types::Model + field :limit, -> { Integer }, optional: true, nullable: false + field :count, -> { Integer }, optional: true, nullable: false + field :has_more, -> { Internal::Types::Boolean }, optional: true, nullable: false + field :links, -> { Internal::Types::Array[Seed::Types::Link] }, optional: false, nullable: false + field :data, -> { Internal::Types::Array[String] }, optional: false, nullable: false + end + end +end diff --git a/seed/ruby-sdk-v2/pagination-custom/lib/seed/users/client.rb b/seed/ruby-sdk-v2/pagination-custom/lib/seed/users/client.rb index a8801405d6c6..dce705a56778 100644 --- a/seed/ruby-sdk-v2/pagination-custom/lib/seed/users/client.rb +++ b/seed/ruby-sdk-v2/pagination-custom/lib/seed/users/client.rb @@ -17,13 +17,15 @@ def initialize(client:) # @option request_options [Hash{String => Object}] :additional_query_parameters # @option request_options [Hash{String => Object}] :additional_body_parameters # @option request_options [Integer] :timeout_in_seconds + # @option params [Integer, nil] :limit # @option params [String, nil] :starting_after # - # @return [Seed::Types::UsernameCursor] - def list_usernames_custom(request_options: {}, **params) + # @return [Seed::Types::UsersListResponse] + def list_with_custom_pager(request_options: {}, **params) params = Seed::Internal::Types::Utils.normalize_keys(params) - query_param_names = %i[starting_after] + query_param_names = %i[limit starting_after] query_params = {} + query_params["limit"] = params[:limit] if params.key?(:limit) query_params["starting_after"] = params[:starting_after] if params.key?(:starting_after) params.except(*query_param_names) @@ -41,7 +43,7 @@ def list_usernames_custom(request_options: {}, **params) end code = response.code.to_i if code.between?(200, 299) - parsed_response = Seed::Types::UsernameCursor.load(response.body) + parsed_response = Seed::Types::UsersListResponse.load(response.body) else error_class = Seed::Errors::ResponseError.subclass_for_code(code) raise error_class.new(response.body, code: code) diff --git a/seed/ruby-sdk-v2/pagination-custom/lib/seed/users/types/list_usernames_request_custom.rb b/seed/ruby-sdk-v2/pagination-custom/lib/seed/users/types/list_with_custom_pager_request.rb similarity index 57% rename from seed/ruby-sdk-v2/pagination-custom/lib/seed/users/types/list_usernames_request_custom.rb rename to seed/ruby-sdk-v2/pagination-custom/lib/seed/users/types/list_with_custom_pager_request.rb index 27d6be3ad7e9..008c49a9df7d 100644 --- a/seed/ruby-sdk-v2/pagination-custom/lib/seed/users/types/list_usernames_request_custom.rb +++ b/seed/ruby-sdk-v2/pagination-custom/lib/seed/users/types/list_with_custom_pager_request.rb @@ -3,7 +3,8 @@ module Seed module Users module Types - class ListUsernamesRequestCustom < Internal::Types::Model + class ListWithCustomPagerRequest < Internal::Types::Model + field :limit, -> { Integer }, optional: true, nullable: false field :starting_after, -> { String }, optional: true, nullable: false end end diff --git a/seed/ruby-sdk-v2/pagination-custom/reference.md b/seed/ruby-sdk-v2/pagination-custom/reference.md index fcbeb7bcba9b..57a0806562be 100644 --- a/seed/ruby-sdk-v2/pagination-custom/reference.md +++ b/seed/ruby-sdk-v2/pagination-custom/reference.md @@ -1,6 +1,6 @@ # Reference ## Users -
client.users.list_usernames_custom() -> Seed::Types::UsernameCursor +
client.users.list_with_custom_pager() -> Seed::Types::UsersListResponse
@@ -13,7 +13,10 @@
```ruby -client.users.list_usernames_custom(starting_after: "starting_after") +client.users.list_with_custom_pager( + limit: 1, + starting_after: "starting_after" +) ```
@@ -28,10 +31,15 @@ client.users.list_usernames_custom(starting_after: "starting_after")
-**starting_after:** `String` +**limit:** `Integer` — The maximum number of results to return. + +
+
+ +
+
-The cursor used for pagination in order to fetch -the next page of results. +**starting_after:** `String` — The cursor used for pagination.
diff --git a/seed/ruby-sdk-v2/pagination/Gemfile b/seed/ruby-sdk-v2/pagination/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/pagination/Gemfile +++ b/seed/ruby-sdk-v2/pagination/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/query-param-name-conflict/Gemfile b/seed/ruby-sdk-v2/query-param-name-conflict/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/query-param-name-conflict/Gemfile +++ b/seed/ruby-sdk-v2/query-param-name-conflict/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/query-parameters-openapi-as-objects/Gemfile b/seed/ruby-sdk-v2/query-parameters-openapi-as-objects/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/query-parameters-openapi-as-objects/Gemfile +++ b/seed/ruby-sdk-v2/query-parameters-openapi-as-objects/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/query-parameters-openapi/Gemfile b/seed/ruby-sdk-v2/query-parameters-openapi/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/query-parameters-openapi/Gemfile +++ b/seed/ruby-sdk-v2/query-parameters-openapi/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/schemaless-request-body-examples/Gemfile b/seed/ruby-sdk-v2/schemaless-request-body-examples/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/schemaless-request-body-examples/Gemfile +++ b/seed/ruby-sdk-v2/schemaless-request-body-examples/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/server-sent-events-openapi/Gemfile b/seed/ruby-sdk-v2/server-sent-events-openapi/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/server-sent-events-openapi/Gemfile +++ b/seed/ruby-sdk-v2/server-sent-events-openapi/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/server-url-templating/Gemfile b/seed/ruby-sdk-v2/server-url-templating/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/server-url-templating/Gemfile +++ b/seed/ruby-sdk-v2/server-url-templating/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/trace/Gemfile b/seed/ruby-sdk-v2/trace/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/trace/Gemfile +++ b/seed/ruby-sdk-v2/trace/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/url-form-encoded/Gemfile b/seed/ruby-sdk-v2/url-form-encoded/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/url-form-encoded/Gemfile +++ b/seed/ruby-sdk-v2/url-form-encoded/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/webhook-audience/Gemfile b/seed/ruby-sdk-v2/webhook-audience/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/webhook-audience/Gemfile +++ b/seed/ruby-sdk-v2/webhook-audience/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/websocket-multi-url/Gemfile b/seed/ruby-sdk-v2/websocket-multi-url/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/websocket-multi-url/Gemfile +++ b/seed/ruby-sdk-v2/websocket-multi-url/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/websocket/Gemfile b/seed/ruby-sdk-v2/websocket/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/websocket/Gemfile +++ b/seed/ruby-sdk-v2/websocket/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end diff --git a/seed/ruby-sdk-v2/x-fern-default/Gemfile b/seed/ruby-sdk-v2/x-fern-default/Gemfile index 29b144d77f48..16877f89f300 100644 --- a/seed/ruby-sdk-v2/x-fern-default/Gemfile +++ b/seed/ruby-sdk-v2/x-fern-default/Gemfile @@ -5,16 +5,12 @@ source "https://rubygems.org" gemspec group :test, :development do - gem "rake", "~> 13.0" - gem "minitest", "~> 5.16" gem "minitest-rg" - + gem "pry" + gem "rake", "~> 13.0" gem "rubocop", "~> 1.21" gem "rubocop-minitest" - - gem "pry" - gem "webmock" end From d7a5502b66722fa5db9a494c42eba593f37c5387 Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:24:28 -0400 Subject: [PATCH 12/21] chore(go): update go-model seed (#14701) Co-authored-by: davidkonigsberg <72822263+davidkonigsberg@users.noreply.github.com> --- seed/go-model/pagination-custom/types.go | 108 ++++++++++++++++------- 1 file changed, 74 insertions(+), 34 deletions(-) diff --git a/seed/go-model/pagination-custom/types.go b/seed/go-model/pagination-custom/types.go index 5cfc83ad542a..c868e7c1a8af 100644 --- a/seed/go-model/pagination-custom/types.go +++ b/seed/go-model/pagination-custom/types.go @@ -9,36 +9,68 @@ import ( internal "github.com/pagination-custom/fern/internal" ) -type UsernameCursor struct { - Cursor *UsernamePage `json:"cursor" url:"cursor"` +type UsersListResponse struct { + Limit *int `json:"limit,omitempty" url:"limit,omitempty"` + Count *int `json:"count,omitempty" url:"count,omitempty"` + HasMore *bool `json:"has_more,omitempty" url:"has_more,omitempty"` + Links []*Link `json:"links" url:"links"` + Data []string `json:"data" url:"data"` extraProperties map[string]any rawJSON json.RawMessage } -func (u *UsernameCursor) GetCursor() *UsernamePage { +func (u *UsersListResponse) GetLimit() *int { if u == nil { return nil } - return u.Cursor + return u.Limit } -func (u *UsernameCursor) GetExtraProperties() map[string]any { +func (u *UsersListResponse) GetCount() *int { + if u == nil { + return nil + } + return u.Count +} + +func (u *UsersListResponse) GetHasMore() *bool { + if u == nil { + return nil + } + return u.HasMore +} + +func (u *UsersListResponse) GetLinks() []*Link { + if u == nil { + return nil + } + return u.Links +} + +func (u *UsersListResponse) GetData() []string { + if u == nil { + return nil + } + return u.Data +} + +func (u *UsersListResponse) GetExtraProperties() map[string]any { if u == nil { return nil } return u.extraProperties } -func (u *UsernameCursor) UnmarshalJSON( +func (u *UsersListResponse) UnmarshalJSON( data []byte, ) error { - type unmarshaler UsernameCursor + type unmarshaler UsersListResponse var value unmarshaler if err := json.Unmarshal(data, &value); err != nil { return err } - *u = UsernameCursor(value) + *u = UsersListResponse(value) extraProperties, err := internal.ExtractExtraProperties(data, *u) if err != nil { return err @@ -48,7 +80,7 @@ func (u *UsernameCursor) UnmarshalJSON( return nil } -func (u *UsernameCursor) String() string { +func (u *UsersListResponse) String() string { if len(u.rawJSON) > 0 { if value, err := internal.StringifyJSON(u.rawJSON); err == nil { return value @@ -60,61 +92,69 @@ func (u *UsernameCursor) String() string { return fmt.Sprintf("%#v", u) } -type UsernamePage struct { - After *string `json:"after,omitempty" url:"after,omitempty"` - Data []string `json:"data" url:"data"` +type Link struct { + Rel string `json:"rel" url:"rel"` + Method string `json:"method" url:"method"` + Href string `json:"href" url:"href"` extraProperties map[string]any rawJSON json.RawMessage } -func (u *UsernamePage) GetAfter() *string { - if u == nil { - return nil +func (l *Link) GetRel() string { + if l == nil { + return "" } - return u.After + return l.Rel } -func (u *UsernamePage) GetData() []string { - if u == nil { - return nil +func (l *Link) GetMethod() string { + if l == nil { + return "" } - return u.Data + return l.Method } -func (u *UsernamePage) GetExtraProperties() map[string]any { - if u == nil { +func (l *Link) GetHref() string { + if l == nil { + return "" + } + return l.Href +} + +func (l *Link) GetExtraProperties() map[string]any { + if l == nil { return nil } - return u.extraProperties + return l.extraProperties } -func (u *UsernamePage) UnmarshalJSON( +func (l *Link) UnmarshalJSON( data []byte, ) error { - type unmarshaler UsernamePage + type unmarshaler Link var value unmarshaler if err := json.Unmarshal(data, &value); err != nil { return err } - *u = UsernamePage(value) - extraProperties, err := internal.ExtractExtraProperties(data, *u) + *l = Link(value) + extraProperties, err := internal.ExtractExtraProperties(data, *l) if err != nil { return err } - u.extraProperties = extraProperties - u.rawJSON = json.RawMessage(data) + l.extraProperties = extraProperties + l.rawJSON = json.RawMessage(data) return nil } -func (u *UsernamePage) String() string { - if len(u.rawJSON) > 0 { - if value, err := internal.StringifyJSON(u.rawJSON); err == nil { +func (l *Link) String() string { + if len(l.rawJSON) > 0 { + if value, err := internal.StringifyJSON(l.rawJSON); err == nil { return value } } - if value, err := internal.StringifyJSON(u); err == nil { + if value, err := internal.StringifyJSON(l); err == nil { return value } - return fmt.Sprintf("%#v", u) + return fmt.Sprintf("%#v", l) } From fda0d9cdb39255ac858f018c403dffae7c22dd2c Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:26:41 -0400 Subject: [PATCH 13/21] chore(python): update python-sdk seed (#14702) Co-authored-by: davidkonigsberg <72822263+davidkonigsberg@users.noreply.github.com> --- seed/python-sdk/pagination-custom/README.md | 19 ++++--- .../python-sdk/pagination-custom/reference.md | 16 ++++-- .../python-sdk/pagination-custom/snippet.json | 6 +-- .../pagination-custom/src/seed/__init__.py | 10 ++-- .../src/seed/types/__init__.py | 8 +-- .../seed/types/{username_page.py => link.py} | 7 +-- ...rname_cursor.py => users_list_response.py} | 10 ++-- .../src/seed/users/client.py | 52 ++++++++++++------- .../src/seed/users/raw_client.py | 48 +++++++++++------ 9 files changed, 110 insertions(+), 66 deletions(-) rename seed/python-sdk/pagination-custom/src/seed/types/{username_page.py => link.py} (81%) rename seed/python-sdk/pagination-custom/src/seed/types/{username_cursor.py => users_list_response.py} (65%) diff --git a/seed/python-sdk/pagination-custom/README.md b/seed/python-sdk/pagination-custom/README.md index cbe1641f1eb3..b5e5ac098a7f 100644 --- a/seed/python-sdk/pagination-custom/README.md +++ b/seed/python-sdk/pagination-custom/README.md @@ -42,7 +42,8 @@ client = SeedPagination( base_url="https://yourhost.com/path/to/api", ) -client.users.list_usernames_custom( +client.users.list_with_custom_pager( + limit=1, starting_after="starting_after", ) ``` @@ -63,7 +64,8 @@ client = AsyncSeedPagination( async def main() -> None: - await client.users.list_usernames_custom( + await client.users.list_with_custom_pager( + limit=1, starting_after="starting_after", ) @@ -80,7 +82,7 @@ will be thrown. from seed.core.api_error import ApiError try: - client.users.list_usernames_custom(...) + client.users.list_with_custom_pager(...) except ApiError as e: print(e.status_code) print(e.body) @@ -98,14 +100,15 @@ client = SeedPagination( base_url="https://yourhost.com/path/to/api", ) -client.users.list_usernames_custom( +client.users.list_with_custom_pager( + limit=1, starting_after="starting_after", ) ``` ```python # You can also iterate through pages and access the typed response per page -pager = client.users.list_usernames_custom(...) +pager = client.users.list_with_custom_pager(...) for page in pager.iter_pages(): print(page.response) # access the typed response for each page for item in page: @@ -125,7 +128,7 @@ from seed import SeedPagination client = SeedPagination( ..., ) -pager = client.users.list_usernames_custom(...) +pager = client.users.list_with_custom_pager(...) print(pager.response) # access the typed response for the first page for item in pager: print(item) # access the underlying object(s) @@ -150,7 +153,7 @@ A request is deemed retryable when any of the following HTTP status codes is ret Use the `max_retries` request option to configure this behavior. ```python -client.users.list_usernames_custom(..., request_options={ +client.users.list_with_custom_pager(..., request_options={ "max_retries": 1 }) ``` @@ -165,7 +168,7 @@ from seed import SeedPagination client = SeedPagination(..., timeout=20.0) # Override timeout for a specific method -client.users.list_usernames_custom(..., request_options={ +client.users.list_with_custom_pager(..., request_options={ "timeout_in_seconds": 1 }) ``` diff --git a/seed/python-sdk/pagination-custom/reference.md b/seed/python-sdk/pagination-custom/reference.md index 7f3f995f820a..eb8c17a81933 100644 --- a/seed/python-sdk/pagination-custom/reference.md +++ b/seed/python-sdk/pagination-custom/reference.md @@ -1,6 +1,6 @@ # Reference ## Users -
client.users.list_usernames_custom(...) -> UsernameCursor +
client.users.list_with_custom_pager(...) -> UsersListResponse
@@ -20,7 +20,8 @@ client = SeedPagination( base_url="https://yourhost.com/path/to/api", ) -client.users.list_usernames_custom( +client.users.list_with_custom_pager( + limit=1, starting_after="starting_after", ) @@ -38,10 +39,15 @@ client.users.list_usernames_custom(
-**starting_after:** `typing.Optional[str]` +**limit:** `typing.Optional[int]` — The maximum number of results to return. + +
+
+ +
+
-The cursor used for pagination in order to fetch -the next page of results. +**starting_after:** `typing.Optional[str]` — The cursor used for pagination.
diff --git a/seed/python-sdk/pagination-custom/snippet.json b/seed/python-sdk/pagination-custom/snippet.json index d769ef60d0d4..936a901449b0 100644 --- a/seed/python-sdk/pagination-custom/snippet.json +++ b/seed/python-sdk/pagination-custom/snippet.json @@ -6,11 +6,11 @@ "id": { "path": "/users", "method": "GET", - "identifier_override": "endpoint_users.listUsernamesCustom" + "identifier_override": "endpoint_users.listWithCustomPager" }, "snippet": { - "sync_client": "from seed import SeedPagination\n\nclient = SeedPagination(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nresponse = client.users.list_usernames_custom(\n starting_after=\"starting_after\",\n)\nfor item in response:\n yield item\n# alternatively, you can paginate page-by-page\nfor page in response.iter_pages():\n yield page\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedPagination\n\nclient = AsyncSeedPagination(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n response = await client.users.list_usernames_custom(\n starting_after=\"starting_after\",\n )\n async for item in response:\n yield item\n\n # alternatively, you can paginate page-by-page\n async for page in response.iter_pages():\n yield page\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedPagination\n\nclient = SeedPagination(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nresponse = client.users.list_with_custom_pager(\n limit=1,\n starting_after=\"starting_after\",\n)\nfor item in response:\n yield item\n# alternatively, you can paginate page-by-page\nfor page in response.iter_pages():\n yield page\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedPagination\n\nclient = AsyncSeedPagination(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n response = await client.users.list_with_custom_pager(\n limit=1,\n starting_after=\"starting_after\",\n )\n async for item in response:\n yield item\n\n # alternatively, you can paginate page-by-page\n async for page in response.iter_pages():\n yield page\n\n\nasyncio.run(main())\n", "type": "python" } } diff --git a/seed/python-sdk/pagination-custom/src/seed/__init__.py b/seed/python-sdk/pagination-custom/src/seed/__init__.py index 2aef69b597e4..c4c4f8956bc1 100644 --- a/seed/python-sdk/pagination-custom/src/seed/__init__.py +++ b/seed/python-sdk/pagination-custom/src/seed/__init__.py @@ -6,7 +6,7 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .types import UsernameCursor, UsernamePage + from .types import Link, UsersListResponse from . import users from ._default_clients import DefaultAioHttpClient, DefaultAsyncHttpxClient from .client import AsyncSeedPagination, SeedPagination @@ -15,9 +15,9 @@ "AsyncSeedPagination": ".client", "DefaultAioHttpClient": "._default_clients", "DefaultAsyncHttpxClient": "._default_clients", + "Link": ".types", "SeedPagination": ".client", - "UsernameCursor": ".types", - "UsernamePage": ".types", + "UsersListResponse": ".types", "__version__": ".version", "users": ".users", } @@ -48,9 +48,9 @@ def __dir__(): "AsyncSeedPagination", "DefaultAioHttpClient", "DefaultAsyncHttpxClient", + "Link", "SeedPagination", - "UsernameCursor", - "UsernamePage", + "UsersListResponse", "__version__", "users", ] diff --git a/seed/python-sdk/pagination-custom/src/seed/types/__init__.py b/seed/python-sdk/pagination-custom/src/seed/types/__init__.py index afa2095e86b2..ac35d3dbdb89 100644 --- a/seed/python-sdk/pagination-custom/src/seed/types/__init__.py +++ b/seed/python-sdk/pagination-custom/src/seed/types/__init__.py @@ -6,9 +6,9 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .username_cursor import UsernameCursor - from .username_page import UsernamePage -_dynamic_imports: typing.Dict[str, str] = {"UsernameCursor": ".username_cursor", "UsernamePage": ".username_page"} + from .link import Link + from .users_list_response import UsersListResponse +_dynamic_imports: typing.Dict[str, str] = {"Link": ".link", "UsersListResponse": ".users_list_response"} def __getattr__(attr_name: str) -> typing.Any: @@ -32,4 +32,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["UsernameCursor", "UsernamePage"] +__all__ = ["Link", "UsersListResponse"] diff --git a/seed/python-sdk/pagination-custom/src/seed/types/username_page.py b/seed/python-sdk/pagination-custom/src/seed/types/link.py similarity index 81% rename from seed/python-sdk/pagination-custom/src/seed/types/username_page.py rename to seed/python-sdk/pagination-custom/src/seed/types/link.py index a2f4df6ab2b4..3480af5e2a91 100644 --- a/seed/python-sdk/pagination-custom/src/seed/types/username_page.py +++ b/seed/python-sdk/pagination-custom/src/seed/types/link.py @@ -6,9 +6,10 @@ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -class UsernamePage(UniversalBaseModel): - after: typing.Optional[str] = None - data: typing.List[str] +class Link(UniversalBaseModel): + rel: str + method: str + href: str if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/seed/python-sdk/pagination-custom/src/seed/types/username_cursor.py b/seed/python-sdk/pagination-custom/src/seed/types/users_list_response.py similarity index 65% rename from seed/python-sdk/pagination-custom/src/seed/types/username_cursor.py rename to seed/python-sdk/pagination-custom/src/seed/types/users_list_response.py index 4cc28b14e82c..990588045fdb 100644 --- a/seed/python-sdk/pagination-custom/src/seed/types/username_cursor.py +++ b/seed/python-sdk/pagination-custom/src/seed/types/users_list_response.py @@ -4,11 +4,15 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .username_page import UsernamePage +from .link import Link -class UsernameCursor(UniversalBaseModel): - cursor: UsernamePage +class UsersListResponse(UniversalBaseModel): + limit: typing.Optional[int] = None + count: typing.Optional[int] = None + has_more: typing.Optional[bool] = None + links: typing.List[Link] + data: typing.List[str] if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/seed/python-sdk/pagination-custom/src/seed/users/client.py b/seed/python-sdk/pagination-custom/src/seed/users/client.py index 4a7c230d2378..7d81f775ca1c 100644 --- a/seed/python-sdk/pagination-custom/src/seed/users/client.py +++ b/seed/python-sdk/pagination-custom/src/seed/users/client.py @@ -5,7 +5,7 @@ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.custom_pagination import AsyncCustomPager, SyncCustomPager from ..core.request_options import RequestOptions -from ..types.username_cursor import UsernameCursor +from ..types.users_list_response import UsersListResponse from .raw_client import AsyncRawUsersClient, RawUsersClient @@ -24,22 +24,28 @@ def with_raw_response(self) -> RawUsersClient: """ return self._raw_client - def list_usernames_custom( - self, *, starting_after: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None - ) -> SyncCustomPager[str, UsernameCursor]: + def list_with_custom_pager( + self, + *, + limit: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncCustomPager[str, UsersListResponse]: """ Parameters ---------- + limit : typing.Optional[int] + The maximum number of results to return. + starting_after : typing.Optional[str] - The cursor used for pagination in order to fetch - the next page of results. + The cursor used for pagination. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SyncCustomPager[str, UsernameCursor] + SyncCustomPager[str, UsersListResponse] Examples -------- @@ -49,7 +55,8 @@ def list_usernames_custom( token="YOUR_TOKEN", base_url="https://yourhost.com/path/to/api", ) - response = client.users.list_usernames_custom( + response = client.users.list_with_custom_pager( + limit=1, starting_after="starting_after", ) for item in response: @@ -58,7 +65,9 @@ def list_usernames_custom( for page in response.iter_pages(): yield page """ - return self._raw_client.list_usernames_custom(starting_after=starting_after, request_options=request_options) + return self._raw_client.list_with_custom_pager( + limit=limit, starting_after=starting_after, request_options=request_options + ) class AsyncUsersClient: @@ -76,22 +85,28 @@ def with_raw_response(self) -> AsyncRawUsersClient: """ return self._raw_client - async def list_usernames_custom( - self, *, starting_after: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncCustomPager[str, UsernameCursor]: + async def list_with_custom_pager( + self, + *, + limit: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncCustomPager[str, UsersListResponse]: """ Parameters ---------- + limit : typing.Optional[int] + The maximum number of results to return. + starting_after : typing.Optional[str] - The cursor used for pagination in order to fetch - the next page of results. + The cursor used for pagination. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncCustomPager[str, UsernameCursor] + AsyncCustomPager[str, UsersListResponse] Examples -------- @@ -106,7 +121,8 @@ async def list_usernames_custom( async def main() -> None: - response = await client.users.list_usernames_custom( + response = await client.users.list_with_custom_pager( + limit=1, starting_after="starting_after", ) async for item in response: @@ -119,6 +135,6 @@ async def main() -> None: asyncio.run(main()) """ - return await self._raw_client.list_usernames_custom( - starting_after=starting_after, request_options=request_options + return await self._raw_client.list_with_custom_pager( + limit=limit, starting_after=starting_after, request_options=request_options ) diff --git a/seed/python-sdk/pagination-custom/src/seed/users/raw_client.py b/seed/python-sdk/pagination-custom/src/seed/users/raw_client.py index 5938173106bc..8b2b0f4d735a 100644 --- a/seed/python-sdk/pagination-custom/src/seed/users/raw_client.py +++ b/seed/python-sdk/pagination-custom/src/seed/users/raw_client.py @@ -9,7 +9,7 @@ from ..core.parse_error import ParsingError from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions -from ..types.username_cursor import UsernameCursor +from ..types.users_list_response import UsersListResponse from pydantic import ValidationError @@ -17,27 +17,34 @@ class RawUsersClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def list_usernames_custom( - self, *, starting_after: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None - ) -> SyncCustomPager[str, UsernameCursor]: + def list_with_custom_pager( + self, + *, + limit: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncCustomPager[str, UsersListResponse]: """ Parameters ---------- + limit : typing.Optional[int] + The maximum number of results to return. + starting_after : typing.Optional[str] - The cursor used for pagination in order to fetch - the next page of results. + The cursor used for pagination. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SyncCustomPager[str, UsernameCursor] + SyncCustomPager[str, UsersListResponse] """ _response = self._client_wrapper.httpx_client.request( "users", method="GET", params={ + "limit": limit, "starting_after": starting_after, }, request_options=request_options, @@ -45,9 +52,9 @@ def list_usernames_custom( try: if 200 <= _response.status_code < 300: _parsed_response = typing.cast( - UsernameCursor, + UsersListResponse, parse_obj_as( - type_=UsernameCursor, # type: ignore + type_=UsersListResponse, # type: ignore object_=_response.json(), ), ) @@ -66,27 +73,34 @@ class AsyncRawUsersClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper - async def list_usernames_custom( - self, *, starting_after: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncCustomPager[str, UsernameCursor]: + async def list_with_custom_pager( + self, + *, + limit: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncCustomPager[str, UsersListResponse]: """ Parameters ---------- + limit : typing.Optional[int] + The maximum number of results to return. + starting_after : typing.Optional[str] - The cursor used for pagination in order to fetch - the next page of results. + The cursor used for pagination. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncCustomPager[str, UsernameCursor] + AsyncCustomPager[str, UsersListResponse] """ _response = await self._client_wrapper.httpx_client.request( "users", method="GET", params={ + "limit": limit, "starting_after": starting_after, }, request_options=request_options, @@ -94,9 +108,9 @@ async def list_usernames_custom( try: if 200 <= _response.status_code < 300: _parsed_response = typing.cast( - UsernameCursor, + UsersListResponse, parse_obj_as( - type_=UsernameCursor, # type: ignore + type_=UsersListResponse, # type: ignore object_=_response.json(), ), ) From 3a4f756946e29dd3dd6f8665b42709dd07bc8a55 Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:28:10 -0400 Subject: [PATCH 14/21] chore(java): update java-sdk seed (#14704) --- .../pagination-custom/default/README.md | 15 +- .../pagination-custom/default/reference.md | 18 +- .../pagination-custom/default/snippet.json | 8 +- .../resources/users/AsyncRawUsersClient.java | 26 +- .../resources/users/AsyncUsersClient.java | 22 +- .../resources/users/RawUsersClient.java | 30 ++- .../resources/users/UsersClient.java | 20 +- ...m.java => ListWithCustomPagerRequest.java} | 55 +++-- .../types/{UsernameCursor.java => Link.java} | 96 ++++++-- .../seed/pagination/types/UsernamePage.java | 139 ----------- .../pagination/types/UsersListResponse.java | 225 ++++++++++++++++++ .../src/main/java/com/snippets/Example0.java | 5 +- 12 files changed, 416 insertions(+), 243 deletions(-) rename seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/requests/{ListUsernamesRequestCustom.java => ListWithCustomPagerRequest.java} (62%) rename seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/types/{UsernameCursor.java => Link.java} (50%) delete mode 100644 seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/types/UsernamePage.java create mode 100644 seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/types/UsersListResponse.java diff --git a/seed/java-sdk/pagination-custom/default/README.md b/seed/java-sdk/pagination-custom/default/README.md index 0c51a4f6584f..8d93bbd25d97 100644 --- a/seed/java-sdk/pagination-custom/default/README.md +++ b/seed/java-sdk/pagination-custom/default/README.md @@ -57,7 +57,7 @@ Instantiate and use the client with the following: package com.example.usage; import com.seed.pagination.SeedPaginationClient; -import com.seed.pagination.resources.users.requests.ListUsernamesRequestCustom; +import com.seed.pagination.resources.users.requests.ListWithCustomPagerRequest; public class Example { public static void main(String[] args) { @@ -66,9 +66,10 @@ public class Example { .token("") .build(); - client.users().listUsernamesCustom( - ListUsernamesRequestCustom + client.users().listWithCustomPager( + ListWithCustomPagerRequest .builder() + .limit(1) .startingAfter("starting_after") .build() ); @@ -114,7 +115,7 @@ When the API returns a non-success status code (4xx or 5xx response), an API exc import com.seed.pagination.core.SeedPaginationApiException; try{ - client.users().listUsernamesCustom(...); + client.users().listWithCustomPager(...); } catch (SeedPaginationApiException e){ // Do something with the API exception... } @@ -178,7 +179,7 @@ SeedPaginationClient client = SeedPaginationClient .build(); // Request level -client.users().listUsernamesCustom( +client.users().listWithCustomPager( ..., RequestOptions .builder() @@ -204,7 +205,7 @@ SeedPaginationClient client = SeedPaginationClient ; // Request level -client.users().listUsernamesCustom( +client.users().listWithCustomPager( ..., RequestOptions .builder() @@ -220,7 +221,7 @@ The `withRawResponse()` method returns a raw client that wraps all responses wit (A normal client's `response` is identical to a raw client's `response.body()`.) ```java -SeedPaginationHttpResponse response = client.users().withRawResponse().listUsernamesCustom(...); +SeedPaginationHttpResponse response = client.users().withRawResponse().listWithCustomPager(...); System.out.println(response.body()); System.out.println(response.headers().get("X-My-Header")); diff --git a/seed/java-sdk/pagination-custom/default/reference.md b/seed/java-sdk/pagination-custom/default/reference.md index 200cd11cbde9..06718ee6765f 100644 --- a/seed/java-sdk/pagination-custom/default/reference.md +++ b/seed/java-sdk/pagination-custom/default/reference.md @@ -1,6 +1,6 @@ # Reference ## Users -
client.users.listUsernamesCustom() -> FernCustomPaginator&lt;UsernameCursor&gt; +
client.users.listWithCustomPager() -> FernCustomPaginator&lt;UsersListResponse&gt;
@@ -13,9 +13,10 @@
```java -client.users().listUsernamesCustom( - ListUsernamesRequestCustom +client.users().listWithCustomPager( + ListWithCustomPagerRequest .builder() + .limit(1) .startingAfter("starting_after") .build() ); @@ -33,10 +34,15 @@ client.users().listUsernamesCustom(
-**startingAfter:** `Optional` +**limit:** `Optional` — The maximum number of results to return. + +
+
+ +
+
-The cursor used for pagination in order to fetch -the next page of results. +**startingAfter:** `Optional` — The cursor used for pagination.
diff --git a/seed/java-sdk/pagination-custom/default/snippet.json b/seed/java-sdk/pagination-custom/default/snippet.json index b62124f5d5bc..4c6a547faae9 100644 --- a/seed/java-sdk/pagination-custom/default/snippet.json +++ b/seed/java-sdk/pagination-custom/default/snippet.json @@ -1,16 +1,16 @@ { "endpoints": [ { - "example_identifier": "fdccf19a", + "example_identifier": "372663bc", "id": { "method": "GET", "path": "/users", - "identifier_override": "endpoint_users.listUsernamesCustom" + "identifier_override": "endpoint_users.listWithCustomPager" }, "snippet": { "type": "java", - "sync_client": "package com.example.usage;\n\nimport com.seed.pagination.SeedPaginationClient;\nimport com.seed.pagination.resources.users.requests.ListUsernamesRequestCustom;\n\npublic class Example {\n public static void main(String[] args) {\n SeedPaginationClient client = SeedPaginationClient\n .builder()\n .token(\"\")\n .build();\n\n client.users().listUsernamesCustom(\n ListUsernamesRequestCustom\n .builder()\n .startingAfter(\"starting_after\")\n .build()\n );\n }\n}\n", - "async_client": "package com.example.usage;\n\nimport com.seed.pagination.SeedPaginationClient;\nimport com.seed.pagination.resources.users.requests.ListUsernamesRequestCustom;\n\npublic class Example {\n public static void main(String[] args) {\n SeedPaginationClient client = SeedPaginationClient\n .builder()\n .token(\"\")\n .build();\n\n client.users().listUsernamesCustom(\n ListUsernamesRequestCustom\n .builder()\n .startingAfter(\"starting_after\")\n .build()\n );\n }\n}\n" + "sync_client": "package com.example.usage;\n\nimport com.seed.pagination.SeedPaginationClient;\nimport com.seed.pagination.resources.users.requests.ListWithCustomPagerRequest;\n\npublic class Example {\n public static void main(String[] args) {\n SeedPaginationClient client = SeedPaginationClient\n .builder()\n .token(\"\")\n .build();\n\n client.users().listWithCustomPager(\n ListWithCustomPagerRequest\n .builder()\n .limit(1)\n .startingAfter(\"starting_after\")\n .build()\n );\n }\n}\n", + "async_client": "package com.example.usage;\n\nimport com.seed.pagination.SeedPaginationClient;\nimport com.seed.pagination.resources.users.requests.ListWithCustomPagerRequest;\n\npublic class Example {\n public static void main(String[] args) {\n SeedPaginationClient client = SeedPaginationClient\n .builder()\n .token(\"\")\n .build();\n\n client.users().listWithCustomPager(\n ListWithCustomPagerRequest\n .builder()\n .limit(1)\n .startingAfter(\"starting_after\")\n .build()\n );\n }\n}\n" } } ], diff --git a/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/AsyncRawUsersClient.java b/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/AsyncRawUsersClient.java index 3ca9af4a3462..bd401d76b702 100644 --- a/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/AsyncRawUsersClient.java +++ b/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/AsyncRawUsersClient.java @@ -11,8 +11,8 @@ import com.seed.pagination.core.SeedPaginationException; import com.seed.pagination.core.SeedPaginationHttpResponse; import com.seed.pagination.core.pagination.AsyncFernCustomPaginator; -import com.seed.pagination.resources.users.requests.ListUsernamesRequestCustom; -import com.seed.pagination.types.UsernameCursor; +import com.seed.pagination.resources.users.requests.ListWithCustomPagerRequest; +import com.seed.pagination.types.UsersListResponse; import java.io.IOException; import java.util.concurrent.CompletableFuture; import okhttp3.Call; @@ -33,25 +33,29 @@ public AsyncRawUsersClient(ClientOptions clientOptions) { } public CompletableFuture>>> - listUsernamesCustom() { - return listUsernamesCustom(ListUsernamesRequestCustom.builder().build()); + listWithCustomPager() { + return listWithCustomPager(ListWithCustomPagerRequest.builder().build()); } public CompletableFuture>>> - listUsernamesCustom(RequestOptions requestOptions) { - return listUsernamesCustom(ListUsernamesRequestCustom.builder().build(), requestOptions); + listWithCustomPager(RequestOptions requestOptions) { + return listWithCustomPager(ListWithCustomPagerRequest.builder().build(), requestOptions); } public CompletableFuture>>> - listUsernamesCustom(ListUsernamesRequestCustom request) { - return listUsernamesCustom(request, null); + listWithCustomPager(ListWithCustomPagerRequest request) { + return listWithCustomPager(request, null); } public CompletableFuture>>> - listUsernamesCustom(ListUsernamesRequestCustom request, RequestOptions requestOptions) { + listWithCustomPager(ListWithCustomPagerRequest request, RequestOptions requestOptions) { HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) .newBuilder() .addPathSegments("users"); + if (request.getLimit().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "limit", request.getLimit().get(), false); + } if (request.getStartingAfter().isPresent()) { QueryStringMapper.addQueryParameter( httpUrl, "starting_after", request.getStartingAfter().get(), false); @@ -79,8 +83,8 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO try (ResponseBody responseBody = response.body()) { String responseBodyString = responseBody != null ? responseBody.string() : "{}"; if (response.isSuccessful()) { - UsernameCursor parsedResponse = - ObjectMappers.JSON_MAPPER.readValue(responseBodyString, UsernameCursor.class); + UsersListResponse parsedResponse = + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, UsersListResponse.class); future.complete(new SeedPaginationHttpResponse<>( AsyncFernCustomPaginator.createAsync(parsedResponse, clientOptions, requestOptions), response)); diff --git a/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/AsyncUsersClient.java b/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/AsyncUsersClient.java index e462c91f57aa..87e8ade4c273 100644 --- a/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/AsyncUsersClient.java +++ b/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/AsyncUsersClient.java @@ -6,7 +6,7 @@ import com.seed.pagination.core.ClientOptions; import com.seed.pagination.core.RequestOptions; import com.seed.pagination.core.pagination.AsyncFernCustomPaginator; -import com.seed.pagination.resources.users.requests.ListUsernamesRequestCustom; +import com.seed.pagination.resources.users.requests.ListWithCustomPagerRequest; import java.util.concurrent.CompletableFuture; public class AsyncUsersClient { @@ -26,22 +26,22 @@ public AsyncRawUsersClient withRawResponse() { return this.rawClient; } - public CompletableFuture>> listUsernamesCustom() { - return this.rawClient.listUsernamesCustom().thenApply(response -> response.body()); + public CompletableFuture>> listWithCustomPager() { + return this.rawClient.listWithCustomPager().thenApply(response -> response.body()); } - public CompletableFuture>> listUsernamesCustom( + public CompletableFuture>> listWithCustomPager( RequestOptions requestOptions) { - return this.rawClient.listUsernamesCustom(requestOptions).thenApply(response -> response.body()); + return this.rawClient.listWithCustomPager(requestOptions).thenApply(response -> response.body()); } - public CompletableFuture>> listUsernamesCustom( - ListUsernamesRequestCustom request) { - return this.rawClient.listUsernamesCustom(request).thenApply(response -> response.body()); + public CompletableFuture>> listWithCustomPager( + ListWithCustomPagerRequest request) { + return this.rawClient.listWithCustomPager(request).thenApply(response -> response.body()); } - public CompletableFuture>> listUsernamesCustom( - ListUsernamesRequestCustom request, RequestOptions requestOptions) { - return this.rawClient.listUsernamesCustom(request, requestOptions).thenApply(response -> response.body()); + public CompletableFuture>> listWithCustomPager( + ListWithCustomPagerRequest request, RequestOptions requestOptions) { + return this.rawClient.listWithCustomPager(request, requestOptions).thenApply(response -> response.body()); } } diff --git a/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/RawUsersClient.java b/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/RawUsersClient.java index e28317164990..c5ddf8f1f97d 100644 --- a/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/RawUsersClient.java +++ b/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/RawUsersClient.java @@ -11,8 +11,8 @@ import com.seed.pagination.core.SeedPaginationException; import com.seed.pagination.core.SeedPaginationHttpResponse; import com.seed.pagination.core.pagination.FernCustomPaginator; -import com.seed.pagination.resources.users.requests.ListUsernamesRequestCustom; -import com.seed.pagination.types.UsernameCursor; +import com.seed.pagination.resources.users.requests.ListWithCustomPagerRequest; +import com.seed.pagination.types.UsersListResponse; import java.io.IOException; import okhttp3.Headers; import okhttp3.HttpUrl; @@ -28,24 +28,28 @@ public RawUsersClient(ClientOptions clientOptions) { this.clientOptions = clientOptions; } - public SeedPaginationHttpResponse> listUsernamesCustom() { - return listUsernamesCustom(ListUsernamesRequestCustom.builder().build()); + public SeedPaginationHttpResponse> listWithCustomPager() { + return listWithCustomPager(ListWithCustomPagerRequest.builder().build()); } - public SeedPaginationHttpResponse> listUsernamesCustom(RequestOptions requestOptions) { - return listUsernamesCustom(ListUsernamesRequestCustom.builder().build(), requestOptions); + public SeedPaginationHttpResponse> listWithCustomPager(RequestOptions requestOptions) { + return listWithCustomPager(ListWithCustomPagerRequest.builder().build(), requestOptions); } - public SeedPaginationHttpResponse> listUsernamesCustom( - ListUsernamesRequestCustom request) { - return listUsernamesCustom(request, null); + public SeedPaginationHttpResponse> listWithCustomPager( + ListWithCustomPagerRequest request) { + return listWithCustomPager(request, null); } - public SeedPaginationHttpResponse> listUsernamesCustom( - ListUsernamesRequestCustom request, RequestOptions requestOptions) { + public SeedPaginationHttpResponse> listWithCustomPager( + ListWithCustomPagerRequest request, RequestOptions requestOptions) { HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) .newBuilder() .addPathSegments("users"); + if (request.getLimit().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "limit", request.getLimit().get(), false); + } if (request.getStartingAfter().isPresent()) { QueryStringMapper.addQueryParameter( httpUrl, "starting_after", request.getStartingAfter().get(), false); @@ -69,8 +73,8 @@ public SeedPaginationHttpResponse> listUsernamesCust ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; if (response.isSuccessful()) { - UsernameCursor parsedResponse = - ObjectMappers.JSON_MAPPER.readValue(responseBodyString, UsernameCursor.class); + UsersListResponse parsedResponse = + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, UsersListResponse.class); return new SeedPaginationHttpResponse<>( FernCustomPaginator.create(parsedResponse, clientOptions, requestOptions), response); } diff --git a/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/UsersClient.java b/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/UsersClient.java index 8cb0f0598935..5e1194cac850 100644 --- a/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/UsersClient.java +++ b/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/UsersClient.java @@ -6,7 +6,7 @@ import com.seed.pagination.core.ClientOptions; import com.seed.pagination.core.RequestOptions; import com.seed.pagination.core.pagination.FernCustomPaginator; -import com.seed.pagination.resources.users.requests.ListUsernamesRequestCustom; +import com.seed.pagination.resources.users.requests.ListWithCustomPagerRequest; public class UsersClient { protected final ClientOptions clientOptions; @@ -25,20 +25,20 @@ public RawUsersClient withRawResponse() { return this.rawClient; } - public FernCustomPaginator listUsernamesCustom() { - return this.rawClient.listUsernamesCustom().body(); + public FernCustomPaginator listWithCustomPager() { + return this.rawClient.listWithCustomPager().body(); } - public FernCustomPaginator listUsernamesCustom(RequestOptions requestOptions) { - return this.rawClient.listUsernamesCustom(requestOptions).body(); + public FernCustomPaginator listWithCustomPager(RequestOptions requestOptions) { + return this.rawClient.listWithCustomPager(requestOptions).body(); } - public FernCustomPaginator listUsernamesCustom(ListUsernamesRequestCustom request) { - return this.rawClient.listUsernamesCustom(request).body(); + public FernCustomPaginator listWithCustomPager(ListWithCustomPagerRequest request) { + return this.rawClient.listWithCustomPager(request).body(); } - public FernCustomPaginator listUsernamesCustom( - ListUsernamesRequestCustom request, RequestOptions requestOptions) { - return this.rawClient.listUsernamesCustom(request, requestOptions).body(); + public FernCustomPaginator listWithCustomPager( + ListWithCustomPagerRequest request, RequestOptions requestOptions) { + return this.rawClient.listWithCustomPager(request, requestOptions).body(); } } diff --git a/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/requests/ListUsernamesRequestCustom.java b/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/requests/ListWithCustomPagerRequest.java similarity index 62% rename from seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/requests/ListUsernamesRequestCustom.java rename to seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/requests/ListWithCustomPagerRequest.java index c5d74c292151..a45fcb4483d0 100644 --- a/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/requests/ListUsernamesRequestCustom.java +++ b/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/resources/users/requests/ListWithCustomPagerRequest.java @@ -18,20 +18,31 @@ import java.util.Optional; @JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(builder = ListUsernamesRequestCustom.Builder.class) -public final class ListUsernamesRequestCustom { +@JsonDeserialize(builder = ListWithCustomPagerRequest.Builder.class) +public final class ListWithCustomPagerRequest { + private final Optional limit; + private final Optional startingAfter; private final Map additionalProperties; - private ListUsernamesRequestCustom(Optional startingAfter, Map additionalProperties) { + private ListWithCustomPagerRequest( + Optional limit, Optional startingAfter, Map additionalProperties) { + this.limit = limit; this.startingAfter = startingAfter; this.additionalProperties = additionalProperties; } /** - * @return The cursor used for pagination in order to fetch - * the next page of results. + * @return The maximum number of results to return. + */ + @JsonIgnore + public Optional getLimit() { + return limit; + } + + /** + * @return The cursor used for pagination. */ @JsonIgnore public Optional getStartingAfter() { @@ -41,7 +52,7 @@ public Optional getStartingAfter() { @java.lang.Override public boolean equals(Object other) { if (this == other) return true; - return other instanceof ListUsernamesRequestCustom && equalTo((ListUsernamesRequestCustom) other); + return other instanceof ListWithCustomPagerRequest && equalTo((ListWithCustomPagerRequest) other); } @JsonAnyGetter @@ -49,13 +60,13 @@ public Map getAdditionalProperties() { return this.additionalProperties; } - private boolean equalTo(ListUsernamesRequestCustom other) { - return startingAfter.equals(other.startingAfter); + private boolean equalTo(ListWithCustomPagerRequest other) { + return limit.equals(other.limit) && startingAfter.equals(other.startingAfter); } @java.lang.Override public int hashCode() { - return Objects.hash(this.startingAfter); + return Objects.hash(this.limit, this.startingAfter); } @java.lang.Override @@ -69,6 +80,8 @@ public static Builder builder() { @JsonIgnoreProperties(ignoreUnknown = true) public static final class Builder { + private Optional limit = Optional.empty(); + private Optional startingAfter = Optional.empty(); @JsonAnySetter @@ -76,14 +89,28 @@ public static final class Builder { private Builder() {} - public Builder from(ListUsernamesRequestCustom other) { + public Builder from(ListWithCustomPagerRequest other) { + limit(other.getLimit()); startingAfter(other.getStartingAfter()); return this; } /** - *

The cursor used for pagination in order to fetch - * the next page of results.

+ *

The maximum number of results to return.

+ */ + @JsonSetter(value = "limit", nulls = Nulls.SKIP) + public Builder limit(Optional limit) { + this.limit = limit; + return this; + } + + public Builder limit(Integer limit) { + this.limit = Optional.ofNullable(limit); + return this; + } + + /** + *

The cursor used for pagination.

*/ @JsonSetter(value = "starting_after", nulls = Nulls.SKIP) public Builder startingAfter(Optional startingAfter) { @@ -96,8 +123,8 @@ public Builder startingAfter(String startingAfter) { return this; } - public ListUsernamesRequestCustom build() { - return new ListUsernamesRequestCustom(startingAfter, additionalProperties); + public ListWithCustomPagerRequest build() { + return new ListWithCustomPagerRequest(limit, startingAfter, additionalProperties); } public Builder additionalProperty(String key, Object value) { diff --git a/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/types/UsernameCursor.java b/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/types/Link.java similarity index 50% rename from seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/types/UsernameCursor.java rename to seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/types/Link.java index 8f9c9409c879..778352f16eb8 100644 --- a/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/types/UsernameCursor.java +++ b/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/types/Link.java @@ -17,26 +17,42 @@ import org.jetbrains.annotations.NotNull; @JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(builder = UsernameCursor.Builder.class) -public final class UsernameCursor { - private final UsernamePage cursor; +@JsonDeserialize(builder = Link.Builder.class) +public final class Link { + private final String rel; + + private final String method; + + private final String href; private final Map additionalProperties; - private UsernameCursor(UsernamePage cursor, Map additionalProperties) { - this.cursor = cursor; + private Link(String rel, String method, String href, Map additionalProperties) { + this.rel = rel; + this.method = method; + this.href = href; this.additionalProperties = additionalProperties; } - @JsonProperty("cursor") - public UsernamePage getCursor() { - return cursor; + @JsonProperty("rel") + public String getRel() { + return rel; + } + + @JsonProperty("method") + public String getMethod() { + return method; + } + + @JsonProperty("href") + public String getHref() { + return href; } @java.lang.Override public boolean equals(Object other) { if (this == other) return true; - return other instanceof UsernameCursor && equalTo((UsernameCursor) other); + return other instanceof Link && equalTo((Link) other); } @JsonAnyGetter @@ -44,13 +60,13 @@ public Map getAdditionalProperties() { return this.additionalProperties; } - private boolean equalTo(UsernameCursor other) { - return cursor.equals(other.cursor); + private boolean equalTo(Link other) { + return rel.equals(other.rel) && method.equals(other.method) && href.equals(other.href); } @java.lang.Override public int hashCode() { - return Objects.hash(this.cursor); + return Objects.hash(this.rel, this.method, this.href); } @java.lang.Override @@ -58,18 +74,26 @@ public String toString() { return ObjectMappers.stringify(this); } - public static CursorStage builder() { + public static RelStage builder() { return new Builder(); } - public interface CursorStage { - _FinalStage cursor(@NotNull UsernamePage cursor); + public interface RelStage { + MethodStage rel(@NotNull String rel); + + Builder from(Link other); + } + + public interface MethodStage { + HrefStage method(@NotNull String method); + } - Builder from(UsernameCursor other); + public interface HrefStage { + _FinalStage href(@NotNull String href); } public interface _FinalStage { - UsernameCursor build(); + Link build(); _FinalStage additionalProperty(String key, Object value); @@ -77,8 +101,12 @@ public interface _FinalStage { } @JsonIgnoreProperties(ignoreUnknown = true) - public static final class Builder implements CursorStage, _FinalStage { - private UsernamePage cursor; + public static final class Builder implements RelStage, MethodStage, HrefStage, _FinalStage { + private String rel; + + private String method; + + private String href; @JsonAnySetter private Map additionalProperties = new HashMap<>(); @@ -86,21 +114,37 @@ public static final class Builder implements CursorStage, _FinalStage { private Builder() {} @java.lang.Override - public Builder from(UsernameCursor other) { - cursor(other.getCursor()); + public Builder from(Link other) { + rel(other.getRel()); + method(other.getMethod()); + href(other.getHref()); + return this; + } + + @java.lang.Override + @JsonSetter("rel") + public MethodStage rel(@NotNull String rel) { + this.rel = Objects.requireNonNull(rel, "rel must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("method") + public HrefStage method(@NotNull String method) { + this.method = Objects.requireNonNull(method, "method must not be null"); return this; } @java.lang.Override - @JsonSetter("cursor") - public _FinalStage cursor(@NotNull UsernamePage cursor) { - this.cursor = Objects.requireNonNull(cursor, "cursor must not be null"); + @JsonSetter("href") + public _FinalStage href(@NotNull String href) { + this.href = Objects.requireNonNull(href, "href must not be null"); return this; } @java.lang.Override - public UsernameCursor build() { - return new UsernameCursor(cursor, additionalProperties); + public Link build() { + return new Link(rel, method, href, additionalProperties); } @java.lang.Override diff --git a/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/types/UsernamePage.java b/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/types/UsernamePage.java deleted file mode 100644 index 1724b6c5654b..000000000000 --- a/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/types/UsernamePage.java +++ /dev/null @@ -1,139 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.seed.pagination.types; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSetter; -import com.fasterxml.jackson.annotation.Nulls; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.seed.pagination.core.ObjectMappers; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; - -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(builder = UsernamePage.Builder.class) -public final class UsernamePage { - private final Optional after; - - private final List data; - - private final Map additionalProperties; - - private UsernamePage(Optional after, List data, Map additionalProperties) { - this.after = after; - this.data = data; - this.additionalProperties = additionalProperties; - } - - @JsonProperty("after") - public Optional getAfter() { - return after; - } - - @JsonProperty("data") - public List getData() { - return data; - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof UsernamePage && equalTo((UsernamePage) other); - } - - @JsonAnyGetter - public Map getAdditionalProperties() { - return this.additionalProperties; - } - - private boolean equalTo(UsernamePage other) { - return after.equals(other.after) && data.equals(other.data); - } - - @java.lang.Override - public int hashCode() { - return Objects.hash(this.after, this.data); - } - - @java.lang.Override - public String toString() { - return ObjectMappers.stringify(this); - } - - public static Builder builder() { - return new Builder(); - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static final class Builder { - private Optional after = Optional.empty(); - - private List data = new ArrayList<>(); - - @JsonAnySetter - private Map additionalProperties = new HashMap<>(); - - private Builder() {} - - public Builder from(UsernamePage other) { - after(other.getAfter()); - data(other.getData()); - return this; - } - - @JsonSetter(value = "after", nulls = Nulls.SKIP) - public Builder after(Optional after) { - this.after = after; - return this; - } - - public Builder after(String after) { - this.after = Optional.ofNullable(after); - return this; - } - - @JsonSetter(value = "data", nulls = Nulls.SKIP) - public Builder data(List data) { - this.data.clear(); - if (data != null) { - this.data.addAll(data); - } - return this; - } - - public Builder addData(String data) { - this.data.add(data); - return this; - } - - public Builder addAllData(List data) { - if (data != null) { - this.data.addAll(data); - } - return this; - } - - public UsernamePage build() { - return new UsernamePage(after, data, additionalProperties); - } - - public Builder additionalProperty(String key, Object value) { - this.additionalProperties.put(key, value); - return this; - } - - public Builder additionalProperties(Map additionalProperties) { - this.additionalProperties.putAll(additionalProperties); - return this; - } - } -} diff --git a/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/types/UsersListResponse.java b/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/types/UsersListResponse.java new file mode 100644 index 000000000000..fa8cd3b0aa40 --- /dev/null +++ b/seed/java-sdk/pagination-custom/default/src/main/java/com/seed/pagination/types/UsersListResponse.java @@ -0,0 +1,225 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.pagination.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.seed.pagination.core.ObjectMappers; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = UsersListResponse.Builder.class) +public final class UsersListResponse { + private final Optional limit; + + private final Optional count; + + private final Optional hasMore; + + private final List links; + + private final List data; + + private final Map additionalProperties; + + private UsersListResponse( + Optional limit, + Optional count, + Optional hasMore, + List links, + List data, + Map additionalProperties) { + this.limit = limit; + this.count = count; + this.hasMore = hasMore; + this.links = links; + this.data = data; + this.additionalProperties = additionalProperties; + } + + @JsonProperty("limit") + public Optional getLimit() { + return limit; + } + + @JsonProperty("count") + public Optional getCount() { + return count; + } + + @JsonProperty("has_more") + public Optional getHasMore() { + return hasMore; + } + + @JsonProperty("links") + public List getLinks() { + return links; + } + + @JsonProperty("data") + public List getData() { + return data; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof UsersListResponse && equalTo((UsersListResponse) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(UsersListResponse other) { + return limit.equals(other.limit) + && count.equals(other.count) + && hasMore.equals(other.hasMore) + && links.equals(other.links) + && data.equals(other.data); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.limit, this.count, this.hasMore, this.links, this.data); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional limit = Optional.empty(); + + private Optional count = Optional.empty(); + + private Optional hasMore = Optional.empty(); + + private List links = new ArrayList<>(); + + private List data = new ArrayList<>(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(UsersListResponse other) { + limit(other.getLimit()); + count(other.getCount()); + hasMore(other.getHasMore()); + links(other.getLinks()); + data(other.getData()); + return this; + } + + @JsonSetter(value = "limit", nulls = Nulls.SKIP) + public Builder limit(Optional limit) { + this.limit = limit; + return this; + } + + public Builder limit(Integer limit) { + this.limit = Optional.ofNullable(limit); + return this; + } + + @JsonSetter(value = "count", nulls = Nulls.SKIP) + public Builder count(Optional count) { + this.count = count; + return this; + } + + public Builder count(Integer count) { + this.count = Optional.ofNullable(count); + return this; + } + + @JsonSetter(value = "has_more", nulls = Nulls.SKIP) + public Builder hasMore(Optional hasMore) { + this.hasMore = hasMore; + return this; + } + + public Builder hasMore(Boolean hasMore) { + this.hasMore = Optional.ofNullable(hasMore); + return this; + } + + @JsonSetter(value = "links", nulls = Nulls.SKIP) + public Builder links(List links) { + this.links.clear(); + if (links != null) { + this.links.addAll(links); + } + return this; + } + + public Builder addLinks(Link links) { + this.links.add(links); + return this; + } + + public Builder addAllLinks(List links) { + if (links != null) { + this.links.addAll(links); + } + return this; + } + + @JsonSetter(value = "data", nulls = Nulls.SKIP) + public Builder data(List data) { + this.data.clear(); + if (data != null) { + this.data.addAll(data); + } + return this; + } + + public Builder addData(String data) { + this.data.add(data); + return this; + } + + public Builder addAllData(List data) { + if (data != null) { + this.data.addAll(data); + } + return this; + } + + public UsersListResponse build() { + return new UsersListResponse(limit, count, hasMore, links, data, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/seed/java-sdk/pagination-custom/default/src/main/java/com/snippets/Example0.java b/seed/java-sdk/pagination-custom/default/src/main/java/com/snippets/Example0.java index 1253fca02695..6464805dfb12 100644 --- a/seed/java-sdk/pagination-custom/default/src/main/java/com/snippets/Example0.java +++ b/seed/java-sdk/pagination-custom/default/src/main/java/com/snippets/Example0.java @@ -1,7 +1,7 @@ package com.snippets; import com.seed.pagination.SeedPaginationClient; -import com.seed.pagination.resources.users.requests.ListUsernamesRequestCustom; +import com.seed.pagination.resources.users.requests.ListWithCustomPagerRequest; public class Example0 { public static void main(String[] args) { @@ -11,7 +11,8 @@ public static void main(String[] args) { .build(); client.users() - .listUsernamesCustom(ListUsernamesRequestCustom.builder() + .listWithCustomPager(ListWithCustomPagerRequest.builder() + .limit(1) .startingAfter("starting_after") .build()); } From bcdfd4a5e7515069ac065a86898e85faa9ed0562 Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:50:52 -0400 Subject: [PATCH 15/21] chore(csharp): update csharp-model seed (#14707) --- .../{UsernamePage.cs => Link.cs} | 13 ++++++++----- ...{UsernameCursor.cs => UsersListResponse.cs} | 18 +++++++++++++++--- 2 files changed, 23 insertions(+), 8 deletions(-) rename seed/csharp-model/pagination-custom/src/SeedPagination/{UsernamePage.cs => Link.cs} (69%) rename seed/csharp-model/pagination-custom/src/SeedPagination/{UsernameCursor.cs => UsersListResponse.cs} (57%) diff --git a/seed/csharp-model/pagination-custom/src/SeedPagination/UsernamePage.cs b/seed/csharp-model/pagination-custom/src/SeedPagination/Link.cs similarity index 69% rename from seed/csharp-model/pagination-custom/src/SeedPagination/UsernamePage.cs rename to seed/csharp-model/pagination-custom/src/SeedPagination/Link.cs index 77fd0e85bd38..4b40998adcb7 100644 --- a/seed/csharp-model/pagination-custom/src/SeedPagination/UsernamePage.cs +++ b/seed/csharp-model/pagination-custom/src/SeedPagination/Link.cs @@ -5,17 +5,20 @@ namespace SeedPagination; [Serializable] -public record UsernamePage : IJsonOnDeserialized +public record Link : IJsonOnDeserialized { [JsonExtensionData] private readonly IDictionary _extensionData = new Dictionary(); - [JsonPropertyName("after")] - public string? After { get; set; } + [JsonPropertyName("rel")] + public required string Rel { get; set; } - [JsonPropertyName("data")] - public IEnumerable Data { get; set; } = new List(); + [JsonPropertyName("method")] + public required string Method { get; set; } + + [JsonPropertyName("href")] + public required string Href { get; set; } [JsonIgnore] public ReadOnlyAdditionalProperties AdditionalProperties { get; private set; } = new(); diff --git a/seed/csharp-model/pagination-custom/src/SeedPagination/UsernameCursor.cs b/seed/csharp-model/pagination-custom/src/SeedPagination/UsersListResponse.cs similarity index 57% rename from seed/csharp-model/pagination-custom/src/SeedPagination/UsernameCursor.cs rename to seed/csharp-model/pagination-custom/src/SeedPagination/UsersListResponse.cs index 47fe5e02478d..cc2cea48e41f 100644 --- a/seed/csharp-model/pagination-custom/src/SeedPagination/UsernameCursor.cs +++ b/seed/csharp-model/pagination-custom/src/SeedPagination/UsersListResponse.cs @@ -5,14 +5,26 @@ namespace SeedPagination; [Serializable] -public record UsernameCursor : IJsonOnDeserialized +public record UsersListResponse : IJsonOnDeserialized { [JsonExtensionData] private readonly IDictionary _extensionData = new Dictionary(); - [JsonPropertyName("cursor")] - public required UsernamePage Cursor { get; set; } + [JsonPropertyName("limit")] + public int? Limit { get; set; } + + [JsonPropertyName("count")] + public int? Count { get; set; } + + [JsonPropertyName("has_more")] + public bool? HasMore { get; set; } + + [JsonPropertyName("links")] + public IEnumerable Links { get; set; } = new List(); + + [JsonPropertyName("data")] + public IEnumerable Data { get; set; } = new List(); [JsonIgnore] public ReadOnlyAdditionalProperties AdditionalProperties { get; private set; } = new(); From c243f09d9b99ff64ca9666df433b7c3c25746bfe Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 7 Apr 2026 14:19:30 +0000 Subject: [PATCH 16/21] chore(deps): fix defu prototype pollution vulnerability (CVE-2026-35209) (#14693) * [Dependabot Alert #1408] Scaffold PR for defu * chore(deps): fix defu prototype pollution vulnerability (CVE-2026-35209) - Add pnpm override for defu>=6.1.5 to resolve HIGH severity prototype pollution - defu updated from 6.1.4 to 6.1.6 (transitive dep of tsdown) - Remove dependabot alert scaffold file Co-Authored-By: unknown <> * chore(deps): minimize lockfile diff - surgical defu 6.1.4 -> 6.1.6 update Co-Authored-By: unknown <> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: David Konigsberg <72822263+davidkonigsberg@users.noreply.github.com> --- package.json | 3 ++- pnpm-lock.yaml | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 25d48c01cd3c..3e7ddebf2366 100644 --- a/package.json +++ b/package.json @@ -160,7 +160,8 @@ "vite": "^7.3.2", "yauzl": "^3.2.1", "brace-expansion": ">=5.0.5", - "path-to-regexp@~0.1.12": "0.1.13" + "path-to-regexp@~0.1.12": "0.1.13", + "defu": ">=6.1.5" } }, "dependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f66034dc725f..26254b6351db 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -624,6 +624,7 @@ overrides: yauzl: ^3.2.1 brace-expansion: '>=5.0.5' path-to-regexp@~0.1.12: 0.1.13 + defu: '>=6.1.5' importers: @@ -11689,8 +11690,8 @@ packages: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} - defu@6.1.4: - resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + defu@6.1.6: + resolution: {integrity: sha512-f8mefEW4WIVg4LckePx3mALjQSPQgFlg9U8yaPdlsbdYcHQyj9n2zL2LJEA52smeYxOvmd/nB7TpMtHGMTHcug==} degenerator@5.0.1: resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} @@ -18780,7 +18781,7 @@ snapshots: define-lazy-prop@2.0.0: {} - defu@6.1.4: {} + defu@6.1.6: {} degenerator@5.0.1: dependencies: @@ -22279,7 +22280,7 @@ snapshots: dependencies: ansis: 4.2.0 cac: 6.7.14 - defu: 6.1.4 + defu: 6.1.6 empathic: 2.0.0 hookable: 6.0.1 import-without-cache: 0.2.5 From 7ecb9ee5e065c7515381de6cbf61dd51d56fb537 Mon Sep 17 00:00:00 2001 From: Niels Swimberghe <3382717+Swimburger@users.noreply.github.com> Date: Tue, 7 Apr 2026 12:07:18 -0400 Subject: [PATCH 17/21] chore(ci): add Docker Hub auth and layer caching (#14676) * chore(ci): add Docker Hub auth and layer caching to mitigate rate limits - Add docker/login-action@v3 with DOCKERHUB_USERNAME/DOCKERHUB_TOKEN secrets - Add actions/cache@v4 for Docker layer caching keyed on runner.os and Dockerfiles - Apply to test-ete job in ci.yml - Apply to all 19 seed test jobs in seed.yml via cached-seed composite action - Apply to benchmark job in seed.yml - Apply to all 19 seed update jobs in update-seed.yml via auto-update-seed composite action - Thread Docker credentials through run-seed-process and auto-update-seed composite actions Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * chore(ci): use DOCKER_USERNAME_PUBLIC_READONLY and DOCKER_PASSWORD_PUBLIC_READONLY secrets Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * chore(ci): harden Docker cache against poisoning on self-hosted runners - Clear /tmp/docker-cache before restoring to prevent stale files - Gate docker load on cache-hit output to skip on cache misses - Record pre-existing images and only save newly-pulled images - Add conditional guard on Docker login for missing secrets Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * chore(ci): guard cache save against missing docker-images-before.txt Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * chore(ci): use sort -u to deduplicate image IDs and per-generator cache keys Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * chore(ci): move image recording after cache load to avoid re-saving cached images Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix: remove cache-hit guard so restore-keys partial matches load Docker images Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .github/actions/auto-update-seed/action.yaml | 10 +++ .github/actions/cached-seed/action.yaml | 55 ++++++++++++++ .github/actions/run-seed-process/action.yaml | 10 +++ .github/workflows/ci.yml | 43 +++++++++++ .github/workflows/seed.yml | 75 ++++++++++++++++++++ .github/workflows/update-seed.yml | 32 +++++++++ 6 files changed, 225 insertions(+) diff --git a/.github/actions/auto-update-seed/action.yaml b/.github/actions/auto-update-seed/action.yaml index cd572775d002..466516e80738 100644 --- a/.github/actions/auto-update-seed/action.yaml +++ b/.github/actions/auto-update-seed/action.yaml @@ -25,6 +25,14 @@ inputs: description: The matrix strategy index of the job (to differentiate when parallelizing jobs) required: false default: "0" + dockerhub-username: + description: Docker Hub username for authenticated pulls (avoids rate limits) + required: false + default: "" + dockerhub-token: + description: Docker Hub token for authenticated pulls (avoids rate limits) + required: false + default: "" runs: using: "composite" @@ -40,6 +48,8 @@ runs: allow-unexpected-failures: ${{ inputs.allow-unexpected-failures }} skip-scripts: ${{ inputs.skip-scripts }} skip-git-diff-check: true + dockerhub-username: ${{ inputs.dockerhub-username }} + dockerhub-token: ${{ inputs.dockerhub-token }} # Add changes first to simplify git diff checks, no changes will NOT error here - name: Check for changes diff --git a/.github/actions/cached-seed/action.yaml b/.github/actions/cached-seed/action.yaml index 579c1423b954..6ed00e72e1e0 100644 --- a/.github/actions/cached-seed/action.yaml +++ b/.github/actions/cached-seed/action.yaml @@ -20,6 +20,14 @@ inputs: description: Whether to allow unexpected failures during seed generation required: false default: "false" + dockerhub-username: + description: Docker Hub username for authenticated pulls (avoids rate limits) + required: false + default: "" + dockerhub-token: + description: Docker Hub token for authenticated pulls (avoids rate limits) + required: false + default: "" runs: using: "composite" @@ -27,6 +35,37 @@ runs: - name: Install uses: ./.github/actions/install + - name: Log in to Docker Hub + if: ${{ inputs.dockerhub-username != '' }} + uses: docker/login-action@v3 + with: + username: ${{ inputs.dockerhub-username }} + password: ${{ inputs.dockerhub-token }} + + - name: Clear stale Docker image cache + shell: bash + run: rm -rf /tmp/docker-cache + + - name: Cache Docker images + id: docker-cache + uses: actions/cache@v4 + with: + path: /tmp/docker-cache + key: ${{ runner.os }}-docker-${{ inputs.generator-name }}-${{ hashFiles('**/Dockerfile*') }} + restore-keys: | + ${{ runner.os }}-docker-${{ inputs.generator-name }}- + + - name: Load cached Docker images + shell: bash + run: | + for f in /tmp/docker-cache/*.tar; do + [ -f "$f" ] && docker load -i "$f" || true + done + + - name: Record pre-existing Docker images + shell: bash + run: docker images -q | sort -u > /tmp/docker-images-before.txt + - name: Seed Test shell: bash env: @@ -55,3 +94,19 @@ runs: echo "Seed test failed (attempt $attempt/3), retrying in 10s..." sleep 10 done + + - name: Save Docker images for cache + if: always() + shell: bash + run: | + if [ ! -f /tmp/docker-images-before.txt ]; then + echo "Skipping cache save: docker-images-before.txt not found" + exit 0 + fi + mkdir -p /tmp/docker-cache + comm -13 /tmp/docker-images-before.txt <(docker images -q | sort -u) | while read -r id; do + img=$(docker inspect --format '{{index .RepoTags 0}}' "$id" 2>/dev/null || true) + [ -z "$img" ] && continue + fname=$(echo "$img" | tr '/:' '_') + docker save "$img" -o "/tmp/docker-cache/${fname}.tar" 2>/dev/null || true + done diff --git a/.github/actions/run-seed-process/action.yaml b/.github/actions/run-seed-process/action.yaml index ed385c66fcf6..c88993c355ba 100644 --- a/.github/actions/run-seed-process/action.yaml +++ b/.github/actions/run-seed-process/action.yaml @@ -34,6 +34,14 @@ inputs: description: "Whether to skip the git diff check (used by auto-update-seed where changes are expected)" required: false default: "false" + dockerhub-username: + description: Docker Hub username for authenticated pulls (avoids rate limits) + required: false + default: "" + dockerhub-token: + description: Docker Hub token for authenticated pulls (avoids rate limits) + required: false + default: "" outputs: start_time: @@ -78,6 +86,8 @@ runs: fixtures-to-run: ${{ steps.json-to-string.outputs.as-string }} skip-scripts: ${{ inputs.skip-scripts }} allow-unexpected-failures: ${{ inputs.allow-unexpected-failures }} + dockerhub-username: ${{ inputs.dockerhub-username }} + dockerhub-token: ${{ inputs.dockerhub-token }} - name: Summarize Test Results if: always() && !cancelled() diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ce2193e3caa..399aea780dab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -179,6 +179,34 @@ jobs: - name: Install uses: ./.github/actions/install + - name: Log in to Docker Hub + if: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY != '' }} + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + password: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} + + - name: Clear stale Docker image cache + run: rm -rf /tmp/docker-cache + + - name: Cache Docker images + id: docker-cache + uses: actions/cache@v4 + with: + path: /tmp/docker-cache + key: ${{ runner.os }}-docker-ete-${{ hashFiles('**/Dockerfile*') }} + restore-keys: | + ${{ runner.os }}-docker-ete- + + - name: Load cached Docker images + run: | + for f in /tmp/docker-cache/*.tar; do + [ -f "$f" ] && docker load -i "$f" || true + done + + - name: Record pre-existing Docker images + run: docker images -q | sort -u > /tmp/docker-images-before.txt + - name: Cache Fern bin dependencies uses: actions/cache@v4 with: @@ -232,6 +260,21 @@ jobs: echo "No snapshot changes to commit" fi + - name: Save Docker images for cache + if: always() + run: | + if [ ! -f /tmp/docker-images-before.txt ]; then + echo "Skipping cache save: docker-images-before.txt not found" + exit 0 + fi + mkdir -p /tmp/docker-cache + comm -13 /tmp/docker-images-before.txt <(docker images -q | sort -u) | while read -r id; do + img=$(docker inspect --format '{{index .RepoTags 0}}' "$id" 2>/dev/null || true) + [ -z "$img" ] && continue + fname=$(echo "$img" | tr '/:' '_') + docker save "$img" -o "/tmp/docker-cache/${fname}.tar" 2>/dev/null || true + done + - name: Ensure no changes to git-tracked files if: ${{ !github.event.inputs.update_snapshots }} run: git --no-pager diff --exit-code diff --git a/.github/workflows/seed.yml b/.github/workflows/seed.yml index 8370bab53637..8d113109d502 100644 --- a/.github/workflows/seed.yml +++ b/.github/workflows/seed.yml @@ -326,6 +326,8 @@ jobs: job-index: ${{ strategy.job-index }} collect-metrics: ${{ needs.setup.outputs.post-metrics }} determinism-exclude-paths: '["seed/*/.mock/*", "seed/**/Gemfile.lock"]' + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} pydantic: runs-on: ubuntu-latest @@ -356,6 +358,8 @@ jobs: fixtures-to-run: ${{toJson(matrix.fixtures)}} job-index: ${{ strategy.job-index }} collect-metrics: ${{ needs.setup.outputs.post-metrics }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} python-sdk: runs-on: ubuntu-latest @@ -387,6 +391,8 @@ jobs: job-index: ${{ strategy.job-index }} collect-metrics: ${{ needs.setup.outputs.post-metrics }} determinism-exclude-paths: '["seed/*/.mock/*", "seed/**/poetry.lock"]' + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} openapi: runs-on: ubuntu-latest @@ -417,6 +423,8 @@ jobs: fixtures-to-run: ${{toJson(matrix.fixtures)}} job-index: ${{ strategy.job-index }} collect-metrics: ${{ needs.setup.outputs.post-metrics }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} java-sdk: runs-on: ubuntu-latest @@ -447,6 +455,8 @@ jobs: fixtures-to-run: ${{toJson(matrix.fixtures)}} job-index: ${{ strategy.job-index }} collect-metrics: ${{ needs.setup.outputs.post-metrics }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} java-model: runs-on: ubuntu-latest @@ -477,6 +487,8 @@ jobs: fixtures-to-run: ${{toJson(matrix.fixtures)}} job-index: ${{ strategy.job-index }} collect-metrics: ${{ needs.setup.outputs.post-metrics }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} ts-sdk: runs-on: ubuntu-latest @@ -508,6 +520,8 @@ jobs: job-index: ${{ strategy.job-index }} collect-metrics: ${{ needs.setup.outputs.post-metrics }} determinism-exclude-paths: '["seed/*/.mock/*", "seed/ts-sdk/**/pnpm-lock.yaml"]' + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} go-model: runs-on: ubuntu-latest @@ -538,6 +552,8 @@ jobs: fixtures-to-run: ${{toJson(matrix.fixtures)}} job-index: ${{ strategy.job-index }} collect-metrics: ${{ needs.setup.outputs.post-metrics }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} go-sdk: runs-on: ubuntu-latest @@ -568,6 +584,8 @@ jobs: fixtures-to-run: ${{toJson(matrix.fixtures)}} job-index: ${{ strategy.job-index }} collect-metrics: ${{ needs.setup.outputs.post-metrics }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} csharp-model: runs-on: ubuntu-latest @@ -598,6 +616,8 @@ jobs: fixtures-to-run: ${{toJson(matrix.fixtures)}} job-index: ${{ strategy.job-index }} collect-metrics: ${{ needs.setup.outputs.post-metrics }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} csharp-sdk: runs-on: ubuntu-latest @@ -628,6 +648,8 @@ jobs: fixtures-to-run: ${{toJson(matrix.fixtures)}} job-index: ${{ strategy.job-index }} collect-metrics: ${{ needs.setup.outputs.post-metrics }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} php-model: runs-on: ubuntu-latest @@ -658,6 +680,8 @@ jobs: fixtures-to-run: ${{toJson(matrix.fixtures)}} job-index: ${{ strategy.job-index }} collect-metrics: ${{ needs.setup.outputs.post-metrics }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} php-sdk: runs-on: ubuntu-latest @@ -688,6 +712,8 @@ jobs: fixtures-to-run: ${{toJson(matrix.fixtures)}} job-index: ${{ strategy.job-index }} collect-metrics: ${{ needs.setup.outputs.post-metrics }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} swift-sdk: runs-on: ubuntu-latest @@ -718,6 +744,8 @@ jobs: fixtures-to-run: ${{toJson(matrix.fixtures)}} job-index: ${{ strategy.job-index }} collect-metrics: ${{ needs.setup.outputs.post-metrics }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} rust-model: runs-on: ubuntu-latest @@ -748,6 +776,8 @@ jobs: fixtures-to-run: ${{toJson(matrix.fixtures)}} job-index: ${{ strategy.job-index }} collect-metrics: ${{ needs.setup.outputs.post-metrics }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} rust-sdk: runs-on: ubuntu-latest @@ -778,6 +808,8 @@ jobs: fixtures-to-run: ${{toJson(matrix.fixtures)}} job-index: ${{ strategy.job-index }} collect-metrics: ${{ needs.setup.outputs.post-metrics }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} # SDK Generation Benchmarks — runs affected SDK generators against large public OAS specs. # Uses --skip-scripts to measure generator-only time (no npm install/build/test noise). @@ -833,6 +865,34 @@ jobs: - name: Install uses: ./.github/actions/install + - name: Log in to Docker Hub + if: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY != '' }} + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + password: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} + + - name: Clear stale Docker image cache + run: rm -rf /tmp/docker-cache + + - name: Cache Docker images + id: docker-cache + uses: actions/cache@v4 + with: + path: /tmp/docker-cache + key: ${{ runner.os }}-docker-${{ matrix.generator }}-${{ hashFiles('**/Dockerfile*') }} + restore-keys: | + ${{ runner.os }}-docker-${{ matrix.generator }}- + + - name: Load cached Docker images + run: | + for f in /tmp/docker-cache/*.tar; do + [ -f "$f" ] && docker load -i "$f" || true + done + + - name: Record pre-existing Docker images + run: docker images -q | sort -u > /tmp/docker-images-before.txt + - name: Download shared specs uses: actions/download-artifact@v4 with: @@ -845,6 +905,21 @@ jobs: generator: ${{ matrix.generator }} skip-scripts: "true" + - name: Save Docker images for cache + if: always() + run: | + if [ ! -f /tmp/docker-images-before.txt ]; then + echo "Skipping cache save: docker-images-before.txt not found" + exit 0 + fi + mkdir -p /tmp/docker-cache + comm -13 /tmp/docker-images-before.txt <(docker images -q | sort -u) | while read -r id; do + img=$(docker inspect --format '{{index .RepoTags 0}}' "$id" 2>/dev/null || true) + [ -z "$img" ] && continue + fname=$(echo "$img" | tr '/:' '_') + docker save "$img" -o "/tmp/docker-cache/${fname}.tar" 2>/dev/null || true + done + - name: Upload PR results uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/update-seed.yml b/.github/workflows/update-seed.yml index 4feb6bc5737f..ca5825d81e79 100644 --- a/.github/workflows/update-seed.yml +++ b/.github/workflows/update-seed.yml @@ -365,6 +365,8 @@ jobs: fixtures-to-run: ${{ toJson(matrix.fixtures) }} index: ${{ strategy.job-index }} skip-scripts: ${{ needs.setup.outputs.skip-scripts }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} pydantic-seed-update: runs-on: ubuntu-latest @@ -399,6 +401,8 @@ jobs: fixtures-to-run: ${{ toJson(matrix.fixtures) }} index: ${{ strategy.job-index }} skip-scripts: ${{ needs.setup.outputs.skip-scripts }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} python-sdk-seed-update: runs-on: Seed @@ -433,6 +437,8 @@ jobs: fixtures-to-run: ${{ toJson(matrix.fixtures) }} index: ${{ strategy.job-index }} skip-scripts: ${{ needs.setup.outputs.skip-scripts }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} openapi-seed-update: runs-on: ubuntu-latest @@ -467,6 +473,8 @@ jobs: fixtures-to-run: ${{ toJson(matrix.fixtures) }} index: ${{ strategy.job-index }} skip-scripts: ${{ needs.setup.outputs.skip-scripts }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} java-sdk-seed-update: runs-on: Seed @@ -501,6 +509,8 @@ jobs: fixtures-to-run: ${{ toJson(matrix.fixtures) }} index: ${{ strategy.job-index }} skip-scripts: ${{ needs.setup.outputs.skip-scripts }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} java-model-seed-update: runs-on: ubuntu-latest @@ -535,6 +545,8 @@ jobs: fixtures-to-run: ${{ toJson(matrix.fixtures) }} index: ${{ strategy.job-index }} skip-scripts: ${{ needs.setup.outputs.skip-scripts }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} typescript-sdk-seed-update: runs-on: ubuntu-latest @@ -569,6 +581,8 @@ jobs: fixtures-to-run: ${{ toJson(matrix.fixtures) }} index: ${{ strategy.job-index }} skip-scripts: ${{ needs.setup.outputs.skip-scripts }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} go-model-seed-update: runs-on: ubuntu-latest @@ -603,6 +617,8 @@ jobs: fixtures-to-run: ${{ toJson(matrix.fixtures) }} index: ${{ strategy.job-index }} skip-scripts: ${{ needs.setup.outputs.skip-scripts }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} go-sdk-seed-update: runs-on: ubuntu-latest @@ -637,6 +653,8 @@ jobs: fixtures-to-run: ${{ toJson(matrix.fixtures) }} index: ${{ strategy.job-index }} skip-scripts: ${{ needs.setup.outputs.skip-scripts }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} csharp-model-seed-update: runs-on: ubuntu-latest @@ -671,6 +689,8 @@ jobs: fixtures-to-run: ${{ toJson(matrix.fixtures) }} index: ${{ strategy.job-index }} skip-scripts: ${{ needs.setup.outputs.skip-scripts }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} csharp-sdk-seed-update: runs-on: ubuntu-latest @@ -705,6 +725,8 @@ jobs: fixtures-to-run: ${{ toJson(matrix.fixtures) }} index: ${{ strategy.job-index }} skip-scripts: ${{ needs.setup.outputs.skip-scripts }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} php-model-seed-update: runs-on: ubuntu-latest @@ -739,6 +761,8 @@ jobs: fixtures-to-run: ${{ toJson(matrix.fixtures) }} index: ${{ strategy.job-index }} skip-scripts: ${{ needs.setup.outputs.skip-scripts }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} php-sdk-seed-update: runs-on: ubuntu-latest @@ -773,6 +797,8 @@ jobs: fixtures-to-run: ${{ toJson(matrix.fixtures) }} index: ${{ strategy.job-index }} skip-scripts: ${{ needs.setup.outputs.skip-scripts }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} swift-sdk-seed-update: runs-on: ubuntu-latest @@ -807,6 +833,8 @@ jobs: fixtures-to-run: ${{ toJson(matrix.fixtures) }} index: ${{ strategy.job-index }} skip-scripts: ${{ needs.setup.outputs.skip-scripts }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} rust-model-seed-update: runs-on: ubuntu-latest @@ -841,6 +869,8 @@ jobs: fixtures-to-run: ${{ toJson(matrix.fixtures) }} index: ${{ strategy.job-index }} skip-scripts: ${{ needs.setup.outputs.skip-scripts }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} rust-sdk-seed-update: runs-on: ubuntu-latest @@ -875,6 +905,8 @@ jobs: fixtures-to-run: ${{ toJson(matrix.fixtures) }} index: ${{ strategy.job-index }} skip-scripts: ${{ needs.setup.outputs.skip-scripts }} + dockerhub-username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} + dockerhub-token: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} # Use always() to ensure this runs even if stages from needs are skipped. # By default, block on failures. If commit-on-failure is true, continue even if some seed runs fail. From c32e032416e3099f419a0305f3249cb88fd2b304 Mon Sep 17 00:00:00 2001 From: David Konigsberg <72822263+davidkonigsberg@users.noreply.github.com> Date: Tue, 7 Apr 2026 12:42:40 -0400 Subject: [PATCH 18/21] fix(ci): use env vars instead of secrets in step if conditions (#14711) The secrets context cannot be used directly in step-level if expressions. GitHub Actions rejects this with 'Unrecognized named-value: secrets'. Use env vars to pass the secret value and check the env var instead. Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 4 +++- .github/workflows/seed.yml | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 399aea780dab..0172a03d8d79 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -180,8 +180,10 @@ jobs: uses: ./.github/actions/install - name: Log in to Docker Hub - if: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY != '' }} + if: ${{ env.DOCKERHUB_USERNAME != '' }} uses: docker/login-action@v3 + env: + DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} with: username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} password: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} diff --git a/.github/workflows/seed.yml b/.github/workflows/seed.yml index 8d113109d502..e3c4836dc680 100644 --- a/.github/workflows/seed.yml +++ b/.github/workflows/seed.yml @@ -866,8 +866,10 @@ jobs: uses: ./.github/actions/install - name: Log in to Docker Hub - if: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY != '' }} + if: ${{ env.DOCKERHUB_USERNAME != '' }} uses: docker/login-action@v3 + env: + DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} with: username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }} password: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }} From e14a749a2197e5275b39f658641cedf7bb0197a3 Mon Sep 17 00:00:00 2001 From: Aditya Arolkar Date: Tue, 7 Apr 2026 13:03:19 -0400 Subject: [PATCH 19/21] fix(python): fix snippet regressions in reference.md and docstrings (#14668) * fix(python-sdk): fix enum representation in snippets and missing OAuth params in docstrings - Fix DynamicTypeLiteralMapper.convertEnum() to render enum references (e.g., Operand.GREATER_THAN) instead of string literals when using python_enums or forward_compatible_python_enums pydantic config - Fix build_default_snippet_kwargs() to include client_id and client_secret in OAuth client credentials docstring examples - Also fix synthesizeDefaultNamed() for consistent enum rendering Co-Authored-By: adi * fix: pass raw customConfig to DynamicSnippetsGenerator to preserve pydantic_config Co-Authored-By: adi * fix(python): render objects and discriminated unions as Pydantic constructors in snippets Since v4.62.0, reference.md and README snippets rendered objects as dict literals when use_typeddict_requests was enabled, and discriminated unions as dicts unconditionally. This regressed from the v1 generator which always used Pydantic model constructors (e.g. OrderType_Market() instead of {"type": "MARKET"}). - Always use Pydantic constructors for objects in convertObject() and synthesizeDefaultNamed(), removing the useTypedDictRequests() gate - Add Pydantic constructor path for discriminated unions in convertDiscriminatedUnion() with variant class resolution - Add getDiscriminatedUnionVariantClassReference() to construct variant class names (e.g. OrderType_Market from OrderType + Market) Made-with: Cursor * fix(python): update 5.3.2 changelog with Pydantic constructor fix Made-with: Cursor * update python dynamic snippet snapshots --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../DynamicSnippetsGenerator.test.ts.snap | 12 ++-- .../DynamicSnippetsGeneratorContext.ts | 16 +++++ .../src/context/DynamicTypeLiteralMapper.ts | 63 +++++++++++++------ .../python-v2/sdk/src/SdkGeneratorCli.ts | 5 +- generators/python/sdk/versions.yml | 19 ++++++ .../client_generator/root_client_generator.py | 10 +++ 6 files changed, 99 insertions(+), 26 deletions(-) diff --git a/generators/python-v2/dynamic-snippets/src/__test__/__snapshots__/DynamicSnippetsGenerator.test.ts.snap b/generators/python-v2/dynamic-snippets/src/__test__/__snapshots__/DynamicSnippetsGenerator.test.ts.snap index f85e26f552ed..0be0c2a84180 100644 --- a/generators/python-v2/dynamic-snippets/src/__test__/__snapshots__/DynamicSnippetsGenerator.test.ts.snap +++ b/generators/python-v2/dynamic-snippets/src/__test__/__snapshots__/DynamicSnippetsGenerator.test.ts.snap @@ -38,6 +38,7 @@ client.service.get_metadata( exports[`snippets > examples > 'POST /big-entity (simple)' 1`] = ` "from acme import Acme from acme.types import Actor, ExtendedMovie, Migration +from acme.commons.types import EventInfo_Metadata client = Acme( token="", @@ -68,15 +69,14 @@ client.service.create_big_entity( }, revenue=1000000, ), - event_info={ - "type": "metadata", - "id": "event-12345", - "data": { + event_info=EventInfo_Metadata( + id="event-12345", + data={ "key1": "val1", "key2": "val2" }, - "json_string": "abc" - }, + json_string="abc", + ), migration=Migration( name="Migration 31 Aug", status="RUNNING", diff --git a/generators/python-v2/dynamic-snippets/src/context/DynamicSnippetsGeneratorContext.ts b/generators/python-v2/dynamic-snippets/src/context/DynamicSnippetsGeneratorContext.ts index f24fda2946bd..00bfeadbe45a 100644 --- a/generators/python-v2/dynamic-snippets/src/context/DynamicSnippetsGeneratorContext.ts +++ b/generators/python-v2/dynamic-snippets/src/context/DynamicSnippetsGeneratorContext.ts @@ -93,6 +93,22 @@ export class DynamicSnippetsGeneratorContext extends AbstractDynamicSnippetsGene return python.reference({ name: className, modulePath }); } + public getDiscriminatedUnionVariantClassReference({ + unionDeclaration, + discriminantValue + }: { + unionDeclaration: FernIr.dynamic.Declaration; + discriminantValue: FernIr.dynamic.NameAndWireValue; + }): python.Reference { + const unionClassName = this.getClassName(unionDeclaration.name); + const variantSuffix = discriminantValue.name.pascalCase.safeName; + const modulePath = [ + ...this.getRootModulePath(), + ...unionDeclaration.fernFilepath.allParts.map((part) => part.snakeCase.safeName) + ]; + return python.reference({ name: `${unionClassName}_${variantSuffix}`, modulePath }); + } + public useTypedDictRequests(): boolean { return this.customConfig.use_typeddict_requests === true; } diff --git a/generators/python-v2/dynamic-snippets/src/context/DynamicTypeLiteralMapper.ts b/generators/python-v2/dynamic-snippets/src/context/DynamicTypeLiteralMapper.ts index 3a04c03622aa..563ca8788c72 100644 --- a/generators/python-v2/dynamic-snippets/src/context/DynamicTypeLiteralMapper.ts +++ b/generators/python-v2/dynamic-snippets/src/context/DynamicTypeLiteralMapper.ts @@ -226,11 +226,19 @@ export class DynamicTypeLiteralMapper { if (unionProperties == null) { return python.TypeInstantiation.nop(); } - const discriminantProperty = { - name: this.context.getPropertyName(discriminatedUnion.discriminant.name), - value: python.TypeInstantiation.str(unionVariant.discriminantValue.wireValue) - }; - return python.TypeInstantiation.typedDict([discriminantProperty, ...unionProperties], { multiline: true }); + const variantClassReference = this.context.getDiscriminatedUnionVariantClassReference({ + unionDeclaration: discriminatedUnion.declaration, + discriminantValue: unionVariant.discriminantValue + }); + return python.TypeInstantiation.reference( + python.instantiateClass({ + classReference: variantClassReference, + arguments_: unionProperties.map((entry) => + python.methodArgument({ name: entry.name, value: entry.value }) + ), + multiline: true + }) + ); } private convertDiscriminatedUnionProperties({ @@ -387,13 +395,6 @@ export class DynamicTypeLiteralMapper { value: unknown; }): python.TypeInstantiation { const entries = this.convertObjectEntries({ object_, value }); - - // biome-ignore lint/correctness/useHookAtTopLevel: not a React hook - if (this.context.useTypedDictRequests()) { - return python.TypeInstantiation.typedDict(entries, { multiline: true }); - } - - // Pydantic model style: ClassName(key=value) const classReference = this.context.getTypeClassReference(object_.declaration); return python.TypeInstantiation.reference( python.instantiateClass({ @@ -415,10 +416,27 @@ export class DynamicTypeLiteralMapper { if (enumValue == null) { return python.TypeInstantiation.nop(); } - return python.TypeInstantiation.str(enumValue); + const enumType = this.context.customConfig.pydantic_config?.enum_type; + if (enumType === "python_enums" || enumType === "forward_compatible_python_enums") { + const classReference = this.context.getTypeClassReference(enum_.declaration); + const memberName = enumValue.name.screamingSnakeCase.safeName; + return python.TypeInstantiation.reference( + python.accessAttribute({ + lhs: classReference, + rhs: python.codeBlock(memberName) + }) + ); + } + return python.TypeInstantiation.str(enumValue.wireValue); } - private getEnumValue({ enum_, value }: { enum_: FernIr.dynamic.EnumType; value: unknown }): string | undefined { + private getEnumValue({ + enum_, + value + }: { + enum_: FernIr.dynamic.EnumType; + value: unknown; + }): FernIr.dynamic.NameAndWireValue | undefined { if (typeof value !== "string") { this.context.errors.add({ severity: Severity.Critical, @@ -434,7 +452,7 @@ export class DynamicTypeLiteralMapper { }); return undefined; } - return value; + return enumValue; } private convertUndiscriminatedUnion({ @@ -586,6 +604,17 @@ export class DynamicTypeLiteralMapper { if (firstValue == null) { return python.TypeInstantiation.nop(); } + const enumType = this.context.customConfig.pydantic_config?.enum_type; + if (enumType === "python_enums" || enumType === "forward_compatible_python_enums") { + const classReference = this.context.getTypeClassReference(named.declaration); + const memberName = firstValue.name.screamingSnakeCase.safeName; + return python.TypeInstantiation.reference( + python.accessAttribute({ + lhs: classReference, + rhs: python.codeBlock(memberName) + }) + ); + } return python.TypeInstantiation.str(firstValue.wireValue); } case "object": { @@ -605,10 +634,6 @@ export class DynamicTypeLiteralMapper { }); } } - // biome-ignore lint/correctness/useHookAtTopLevel: not a React hook - if (this.context.useTypedDictRequests()) { - return python.TypeInstantiation.typedDict(entries, { multiline: true }); - } const classReference = this.context.getTypeClassReference(named.declaration); return python.TypeInstantiation.reference( python.instantiateClass({ diff --git a/generators/python-v2/sdk/src/SdkGeneratorCli.ts b/generators/python-v2/sdk/src/SdkGeneratorCli.ts index b9d5e9f30bb6..cdf5ad88072d 100644 --- a/generators/python-v2/sdk/src/SdkGeneratorCli.ts +++ b/generators/python-v2/sdk/src/SdkGeneratorCli.ts @@ -110,7 +110,10 @@ export class SdkGeneratorCli extends AbstractPythonGeneratorCli List[typing.Tuple[str, AST.Expression]]: - Use kwargs (many root client params are keyword-only). - Include required parameters (those without defaults) with reasonable placeholders. - Prefer inferred-auth credentials (e.g. api_key) when present. + - Include client_id/client_secret for OAuth client credentials flows. """ required_params = [ @@ -1737,6 +1738,15 @@ def build_default_snippet_kwargs() -> List[typing.Tuple[str, AST.Expression]]: kwargs.append((name, AST.Expression(f'"YOUR_{name.upper()}"'))) + # For OAuth client credentials, explicitly include client_id and client_secret + # even though they have os.getenv() defaults. + if self._oauth_token_override: + oauth_param_names_in_kwargs = {name for name, _ in kwargs} + if "client_id" not in oauth_param_names_in_kwargs: + kwargs.append(("client_id", AST.Expression('"YOUR_CLIENT_ID"'))) + if "client_secret" not in oauth_param_names_in_kwargs: + kwargs.append(("client_secret", AST.Expression('"YOUR_CLIENT_SECRET"'))) + return kwargs if self._use_kwargs_snippets: From a3924e34299055bc5be53054109e0b218d873c71 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 7 Apr 2026 10:32:06 -0700 Subject: [PATCH 20/21] chore(ci): switch benchmark baseline from actions/cache to artifacts API (#14712) - Remove save-baseline-cache job from benchmark-baseline.yml (cache was being evicted due to 10 GB repo limit from turbo build caches) - Replace cache restore in seed.yml benchmark-report with GitHub REST API to download artifacts from latest successful nightly baseline run - Add flat e2e/ directory support to format-benchmark-report.sh - Increase artifact retention from 7 to 30 days for reliability - Add actions:read permission for artifact download Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: barry.zou --- .github/scripts/format-benchmark-report.sh | 19 +++- .../scripts/test-format-benchmark-report.sh | 21 +++- .github/workflows/benchmark-baseline.yml | 97 +------------------ .github/workflows/seed.yml | 69 +++++++++---- 4 files changed, 86 insertions(+), 120 deletions(-) diff --git a/.github/scripts/format-benchmark-report.sh b/.github/scripts/format-benchmark-report.sh index bd4b9b84b4b5..3cc01544805e 100755 --- a/.github/scripts/format-benchmark-report.sh +++ b/.github/scripts/format-benchmark-report.sh @@ -6,7 +6,8 @@ # {"generator":"ts-sdk","spec":"square","duration_seconds":45} # # The main-results-dir may contain either: -# - Flat .jsonl files (single baseline, legacy format) +# - Flat .jsonl files (single baseline) with optional e2e/ subdirectory +# for E2E results (artifacts API format) # - A history/ subdirectory with dated run folders, each containing .jsonl files. # When history exists, the median of all historical runs is used as the baseline. # E2E results are stored in history//e2e/ subdirectories. @@ -26,7 +27,8 @@ compute_median() { } # Look up E2E baseline duration for a given generator+spec. -# Reads from history//e2e/ subdirectories. +# Checks flat e2e/ subdirectory first (artifacts API format), +# then falls back to history//e2e/ subdirectories. # Sets: E2E_BASELINE_VAL, E2E_BASELINE_RUNS lookup_e2e_baseline() { local generator="$1" @@ -51,6 +53,17 @@ lookup_e2e_baseline() { if [ "$E2E_BASELINE_RUNS" -gt 0 ]; then E2E_BASELINE_VAL=$(printf '%s\n' "${durations[@]}" | compute_median) fi + else + # Flat E2E layout: MAIN_DIR/e2e/{generator}.jsonl (artifacts API format) + local e2e_file="${MAIN_DIR}/e2e/${generator}.jsonl" + if [ -f "$e2e_file" ]; then + local dur + dur=$(jq -r --arg spec "$spec" 'select(.spec == $spec) | .duration_seconds' "$e2e_file" 2>/dev/null || true) + if [ -n "$dur" ] && [ "$dur" != "null" ] && [ "$dur" != "0" ]; then + E2E_BASELINE_VAL="$dur" + E2E_BASELINE_RUNS=1 + fi + fi fi } @@ -105,7 +118,7 @@ if [ -n "${BASELINE_TIMESTAMP:-}" ]; then HISTORY_COUNT=$(ls -d "${MAIN_DIR}/history"/*/ 2>/dev/null | wc -l) echo "Comparing PR branch against **median of ${HISTORY_COUNT} nightly run(s)** on \`main\` (latest: ${BASELINE_TIMESTAMP})." else - echo "Comparing PR branch against cached \`main\` baseline (generated ${BASELINE_TIMESTAMP})." + echo "Comparing PR branch against latest nightly baseline on \`main\` (${BASELINE_TIMESTAMP})." fi else echo "Comparing PR branch against \`main\` baseline." diff --git a/.github/scripts/test-format-benchmark-report.sh b/.github/scripts/test-format-benchmark-report.sh index a098f22d8706..f071be98da7b 100755 --- a/.github/scripts/test-format-benchmark-report.sh +++ b/.github/scripts/test-format-benchmark-report.sh @@ -153,7 +153,7 @@ test_baseline_timestamp() { OUTPUT=$(BASELINE_TIMESTAMP="2026-03-30T04:00:00Z" bash "$REPORT_SCRIPT" "$PR_DIR" "$MAIN_DIR") - assert_contains "$OUTPUT" "cached" "Shows cached baseline label" + assert_contains "$OUTPUT" "latest nightly baseline" "Shows nightly baseline label" assert_contains "$OUTPUT" "2026-03-30T04:00:00Z" "Shows baseline timestamp" assert_contains "$OUTPUT" "benchmark-baseline" "Shows link to refresh workflow" } @@ -522,6 +522,25 @@ test_e2e_column_with_history test_e2e_column_missing test_e2e_partial_coverage test_e2e_median_odd + +# Test 22: E2E column from flat e2e/ directory (artifacts API layout) +test_e2e_flat_layout() { + echo "Test: E2E column populated from flat e2e/ directory (artifacts API)" + setup_dirs + echo '{"generator":"ts-sdk","spec":"square","duration_seconds":50,"exit_code":0}' > "$PR_DIR/ts-sdk.jsonl" + + # Flat layout: generator-only at root, E2E in e2e/ subdir + echo '{"generator":"ts-sdk","spec":"square","duration_seconds":45,"exit_code":0}' > "$MAIN_DIR/ts-sdk.jsonl" + mkdir -p "$MAIN_DIR/e2e" + echo '{"generator":"ts-sdk","spec":"square","duration_seconds":200,"exit_code":0}' > "$MAIN_DIR/e2e/ts-sdk.jsonl" + + OUTPUT=$(BASELINE_TIMESTAMP="2026-04-06T04:00:00Z" bash "$REPORT_SCRIPT" "$PR_DIR" "$MAIN_DIR") + + assert_contains "$OUTPUT" "| ts-sdk | square | 45s | 200s | 50s |" "Flat layout: both gen and E2E shown" + assert_contains "$OUTPUT" "latest nightly baseline" "Flat layout: shows nightly baseline label" +} + +test_e2e_flat_layout echo "" echo "=== PostHog JSON validation tests ===" echo "" diff --git a/.github/workflows/benchmark-baseline.yml b/.github/workflows/benchmark-baseline.yml index 7372309ea932..866c896d2681 100644 --- a/.github/workflows/benchmark-baseline.yml +++ b/.github/workflows/benchmark-baseline.yml @@ -78,7 +78,7 @@ jobs: with: name: benchmark-baseline-${{ matrix.generator }} path: benchmark-results/ - retention-days: 7 + retention-days: 30 # Run full E2E benchmarks (with build/test scripts) for informational purposes. # These capture the complete customer-observable generation time including @@ -116,100 +116,7 @@ jobs: with: name: benchmark-e2e-${{ matrix.generator }} path: benchmark-results/ - retention-days: 7 - - # Aggregate results into rolling 7-day history cache - save-baseline-cache: - runs-on: ubuntu-latest - timeout-minutes: 10 - needs: [baseline, baseline-e2e] - if: ${{ always() && !cancelled() && needs.baseline.result != 'skipped' }} - steps: - - name: Download generator-only baseline artifacts - uses: actions/download-artifact@v4 - with: - pattern: benchmark-baseline-* - path: todays-results - merge-multiple: true - - - name: Download E2E baseline artifacts - uses: actions/download-artifact@v4 - with: - pattern: benchmark-e2e-* - path: todays-e2e-results - merge-multiple: true - - - name: Restore existing history cache - id: history-cache - uses: actions/cache/restore@v4 - with: - path: benchmark-baseline-results - # Prefix fallback finds the most recent history cache entry - key: benchmark-baseline-never-matches - restore-keys: benchmark-baseline- - - - name: Append today's results and prune old entries - shell: bash - run: | - mkdir -p benchmark-baseline-results/history - TODAY=$(date -u +%Y-%m-%d) - RUN_DIR="benchmark-baseline-results/history/${TODAY}_${{ github.run_id }}" - mkdir -p "$RUN_DIR" - - # Copy generator-only results (used for PR comparisons) - if [ -d todays-results ] && ls todays-results/*.jsonl 1>/dev/null 2>&1; then - for f in todays-results/*.jsonl; do - [ -f "$f" ] && cp "$f" "$RUN_DIR/" - done - else - echo "::warning::No generator-only baseline results found. Removing empty history entry." - rm -rf "$RUN_DIR" - echo "SKIP_CACHE_SAVE=true" >> "$GITHUB_ENV" - fi - - # Only proceed with E2E + metadata if generator-only results were found - if [ -d "$RUN_DIR" ]; then - # Copy full E2E results into a separate subdirectory (informational only) - if [ -d todays-e2e-results ] && ls todays-e2e-results/*.jsonl 1>/dev/null 2>&1; then - mkdir -p "$RUN_DIR/e2e" - for f in todays-e2e-results/*.jsonl; do - [ -f "$f" ] && cp "$f" "$RUN_DIR/e2e/" - done - fi - - # Store metadata for this run - echo "{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"sha\":\"${{ github.sha }}\"}" \ - > "$RUN_DIR/_metadata.json" - fi - - # Prune history older than 7 days - # Note: 'date -d' is GNU coreutils syntax (CI runs on Ubuntu; not portable to macOS) - CUTOFF=$(date -u -d '7 days ago' +%Y-%m-%d) - for dir in benchmark-baseline-results/history/*/; do - [ -d "$dir" ] || continue - DIR_DATE=$(basename "$dir" | cut -d_ -f1) - if [[ "$DIR_DATE" < "$CUTOFF" ]]; then - echo "Pruning old baseline: $dir (date: $DIR_DATE, cutoff: $CUTOFF)" - rm -rf "$dir" - fi - done - - # Write top-level metadata summarizing the history - RUNS=$(ls -d benchmark-baseline-results/history/*/ 2>/dev/null | wc -l) - echo "{\"latest_timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"sha\":\"${{ github.sha }}\",\"total_runs\":${RUNS}}" \ - > benchmark-baseline-results/_metadata.json - - echo "Baseline history summary:" - cat benchmark-baseline-results/_metadata.json - echo "History entries:" - ls -la benchmark-baseline-results/history/ - - - name: Save baseline to cache - if: ${{ env.SKIP_CACHE_SAVE != 'true' }} - uses: actions/cache/save@v4 - with: - path: benchmark-baseline-results - key: benchmark-baseline-${{ github.run_id }} + retention-days: 30 # Send benchmark results to PostHog for long-term trend analysis and dashboards. # Uses the same PostHog project + API key as seed test metrics (seed.yml). diff --git a/.github/workflows/seed.yml b/.github/workflows/seed.yml index e3c4836dc680..78300d3ef791 100644 --- a/.github/workflows/seed.yml +++ b/.github/workflows/seed.yml @@ -813,7 +813,7 @@ jobs: # SDK Generation Benchmarks — runs affected SDK generators against large public OAS specs. # Uses --skip-scripts to measure generator-only time (no npm install/build/test noise). - # Baseline timings come from the nightly benchmark-baseline.yml workflow (cached). + # Baseline timings come from the nightly benchmark-baseline.yml workflow (via artifacts API). # # Specs are fetched once in benchmark-setup and shared via artifact to avoid redundant downloads. benchmark-setup: @@ -941,6 +941,7 @@ jobs: }} permissions: pull-requests: write + actions: read steps: - name: Checkout repo uses: actions/checkout@v6 @@ -954,30 +955,56 @@ jobs: path: benchmark-pr-results merge-multiple: true - - name: Restore baseline from nightly cache - id: baseline-cache - uses: actions/cache/restore@v4 - with: - path: benchmark-baseline-results - # Intentionally non-matching key forces prefix fallback via restore-keys, - # which returns the most recently saved nightly baseline cache entry. - key: benchmark-baseline-never-matches - restore-keys: benchmark-baseline- - - - name: Check baseline availability + - name: Download baseline from latest nightly artifacts + env: + GH_TOKEN: ${{ github.token }} shell: bash run: | - if [ "${{ steps.baseline-cache.outputs.cache-hit }}" != "true" ] && [ ! -d benchmark-baseline-results ]; then - echo "::warning::No cached baseline found. Report will show N/A for main timings." - echo "Run the benchmark-baseline workflow manually to generate initial baselines." - mkdir -p benchmark-baseline-results + mkdir -p benchmark-baseline-results + + # Find the latest successful run of benchmark-baseline.yml on main + LATEST_RUN=$(gh api "repos/${{ github.repository }}/actions/workflows/benchmark-baseline.yml/runs?status=success&per_page=1&branch=main" \ + --jq '.workflow_runs[0]' 2>/dev/null || true) + + if [ -z "$LATEST_RUN" ] || [ "$LATEST_RUN" = "null" ]; then + echo "::warning::No successful nightly baseline run found. Report will show N/A for main timings." + echo "Trigger the benchmark-baseline workflow manually to generate initial baselines." + exit 0 + fi + + RUN_ID=$(echo "$LATEST_RUN" | jq -r '.id') + RUN_DATE=$(echo "$LATEST_RUN" | jq -r '.created_at') + RUN_SHA=$(echo "$LATEST_RUN" | jq -r '.head_sha') + echo "Using baseline from nightly run ${RUN_ID} (${RUN_DATE}, sha: ${RUN_SHA})" + + # Save metadata for the report footer + echo "{\"latest_timestamp\":\"${RUN_DATE}\",\"sha\":\"${RUN_SHA}\",\"run_id\":\"${RUN_ID}\"}" \ + > benchmark-baseline-results/_metadata.json + + # Download generator-only baseline artifacts + echo "Downloading generator-only baseline artifacts..." + gh run download "$RUN_ID" --repo "${{ github.repository }}" --pattern "benchmark-baseline-*" --dir /tmp/baseline-gen/ 2>/dev/null || true + for f in /tmp/baseline-gen/*/*.jsonl; do + [ -f "$f" ] && cp "$f" benchmark-baseline-results/ + done + + # Download E2E baseline artifacts + echo "Downloading E2E baseline artifacts..." + mkdir -p benchmark-baseline-results/e2e + gh run download "$RUN_ID" --repo "${{ github.repository }}" --pattern "benchmark-e2e-*" --dir /tmp/baseline-e2e/ 2>/dev/null || true + for f in /tmp/baseline-e2e/*/*.jsonl; do + [ -f "$f" ] && cp "$f" benchmark-baseline-results/e2e/ + done + + # Check if we got any results + if ! ls benchmark-baseline-results/*.jsonl 1>/dev/null 2>&1; then + echo "::warning::No baseline artifacts found in run ${RUN_ID}. Artifacts may have expired." + echo "Trigger the benchmark-baseline workflow to generate fresh baselines." + rm -f benchmark-baseline-results/_metadata.json else - echo "Baseline cache restored successfully" - if [ -f benchmark-baseline-results/_metadata.json ]; then - echo "Baseline metadata:" - cat benchmark-baseline-results/_metadata.json - fi + echo "Baseline artifacts downloaded successfully:" ls -la benchmark-baseline-results/ + ls -la benchmark-baseline-results/e2e/ 2>/dev/null || echo "(no E2E results)" fi - name: Generate benchmark report From ef58cdc85ffeee3a60b97eac24410eeca1648f89 Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 7 Apr 2026 13:32:32 -0400 Subject: [PATCH 21/21] chore(python): update python-sdk seed (#14714) Co-authored-by: aditya-arolkar-swe <13171215+aditya-arolkar-swe@users.noreply.github.com> --- seed/python-sdk/any-auth/snippet.json | 12 +- .../any-auth/src/seed/auth/client.py | 4 + seed/python-sdk/any-auth/src/seed/client.py | 4 + .../any-auth/src/seed/user/client.py | 8 + .../endpoint-security-auth/snippet.json | 32 +- .../src/seed/auth/client.py | 4 + .../endpoint-security-auth/src/seed/client.py | 4 + .../src/seed/user/client.py | 28 ++ .../enum/real-enum-forward-compat/README.md | 14 +- .../real-enum-forward-compat/reference.md | 36 +- seed/python-sdk/enum/real-enum/README.md | 14 +- seed/python-sdk/enum/real-enum/reference.md | 36 +- .../reference.md | 50 ++- .../examples/client-filename/reference.md | 50 ++- .../examples/legacy-wire-tests/reference.md | 50 ++- .../examples/no-custom-config/reference.md | 50 ++- .../examples/omit-fern-headers/reference.md | 50 ++- seed/python-sdk/examples/readme/reference.md | 50 ++- .../additional_init_exports/reference.md | 10 +- .../aliases_with_validation/reference.md | 10 +- .../aliases_without_validation/reference.md | 10 +- .../exhaustive/custom-transport/reference.md | 10 +- .../datetime-milliseconds/reference.md | 10 +- .../deps_with_min_python_version/reference.md | 10 +- .../exhaustive/eager-imports/reference.md | 10 +- .../extra_dependencies/reference.md | 10 +- .../extra_dev_dependencies/reference.md | 10 +- .../five-second-timeout/reference.md | 10 +- .../follow_redirects_by_default/reference.md | 10 +- .../exhaustive/import-paths/reference.md | 10 +- .../exhaustive/improved_imports/reference.md | 10 +- .../exhaustive/infinite-timeout/reference.md | 10 +- .../inline-path-params/reference.md | 10 +- .../inline_request_params/reference.md | 10 +- .../exhaustive/no-custom-config/reference.md | 10 +- .../tests/wire/test_endpoints_union.py | 7 +- .../exhaustive/package-path/reference.md | 10 +- .../pydantic-extra-fields/reference.md | 10 +- .../pydantic-ignore-fields/reference.md | 10 +- .../pydantic-v1-with-utils/reference.md | 10 +- .../pydantic-v1-wrapped/reference.md | 10 +- .../exhaustive/pydantic-v1/reference.md | 10 +- .../pydantic-v2-wrapped/reference.md | 10 +- .../exhaustive/pyproject_extras/reference.md | 10 +- .../skip-pydantic-validation/reference.md | 10 +- .../exhaustive/union-utils/reference.md | 10 +- .../reference.md | 10 +- .../tests/wire/test_endpoints_union.py | 7 +- .../python-sdk/nullable-optional/reference.md | 167 ++++----- .../nullable/no-custom-config/README.md | 12 +- .../nullable/no-custom-config/reference.md | 6 +- .../nullable/use-typeddict-requests/README.md | 12 +- .../use-typeddict-requests/reference.md | 6 +- .../snippet.json | 20 +- .../src/seed/auth/client.py | 8 + .../src/seed/client.py | 4 + .../src/seed/nested/api/client.py | 4 + .../src/seed/nested_no_auth/api/client.py | 4 + .../src/seed/simple/client.py | 4 + .../snippet.json | 16 +- .../src/seed/auth/client.py | 4 + .../src/seed/client.py | 4 + .../src/seed/nested/api/client.py | 4 + .../src/seed/nested_no_auth/api/client.py | 4 + .../src/seed/simple/client.py | 4 + .../snippet.json | 20 +- .../src/seed/auth/client.py | 8 + .../src/seed/client.py | 4 + .../src/seed/nested/api/client.py | 4 + .../src/seed/nested_no_auth/api/client.py | 4 + .../src/seed/simple/client.py | 4 + .../no-custom-config/snippet.json | 16 +- .../no-custom-config/src/seed/auth/client.py | 8 + .../no-custom-config/src/seed/client.py | 4 + .../src/seed/nested/api/client.py | 4 + .../src/seed/simple/client.py | 4 + .../snippet.json | 16 +- .../src/seed/auth/client.py | 4 + .../src/seed/client.py | 4 + .../src/seed/nested/api/client.py | 4 + .../src/seed/nested_no_auth/api/client.py | 4 + .../src/seed/simple/client.py | 4 + .../snippet.json | 12 +- .../src/seed/client.py | 4 + .../src/seed/identity/client.py | 4 + .../src/seed/plants/client.py | 8 + .../snippet.json | 8 +- .../src/seed/auth/client.py | 4 + .../src/seed/client.py | 4 + .../src/seed/simple/client.py | 4 + .../snippet.json | 24 +- .../src/seed/auth/client.py | 8 + .../src/seed/client.py | 4 + .../src/seed/nested/api/client.py | 4 + .../src/seed/nested_no_auth/api/client.py | 4 + .../src/seed/service/client.py | 4 + .../src/seed/simple/client.py | 4 + .../oauth-client-credentials/snippet.json | 20 +- .../src/seed/auth/client.py | 8 + .../src/seed/client.py | 4 + .../src/seed/nested/api/client.py | 4 + .../src/seed/nested_no_auth/api/client.py | 4 + .../src/seed/simple/client.py | 4 + seed/python-sdk/trace/README.md | 10 +- seed/python-sdk/trace/reference.md | 345 +++++++++--------- .../unions-with-local-date/reference.md | 58 +-- .../unions/no-custom-config/reference.md | 51 ++- .../unions/union-naming-v1/reference.md | 51 ++- .../unions/union-utils/reference.md | 51 ++- 109 files changed, 1047 insertions(+), 852 deletions(-) diff --git a/seed/python-sdk/any-auth/snippet.json b/seed/python-sdk/any-auth/snippet.json index 2852e5b1878b..22b108a7af87 100644 --- a/seed/python-sdk/any-auth/snippet.json +++ b/seed/python-sdk/any-auth/snippet.json @@ -9,8 +9,8 @@ "identifier_override": "endpoint_auth.getToken" }, "snippet": { - "sync_client": "from seed import SeedAnyAuth\n\nclient = SeedAnyAuth(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n)\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedAnyAuth\n\nclient = AsyncSeedAnyAuth(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n )\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedAnyAuth\n\nclient = SeedAnyAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedAnyAuth\n\nclient = AsyncSeedAnyAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n )\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -22,8 +22,8 @@ "identifier_override": "endpoint_user.get" }, "snippet": { - "sync_client": "from seed import SeedAnyAuth\n\nclient = SeedAnyAuth(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.user.get()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedAnyAuth\n\nclient = AsyncSeedAnyAuth(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.user.get()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedAnyAuth\n\nclient = SeedAnyAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.user.get()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedAnyAuth\n\nclient = AsyncSeedAnyAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.user.get()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -35,8 +35,8 @@ "identifier_override": "endpoint_user.getAdmins" }, "snippet": { - "sync_client": "from seed import SeedAnyAuth\n\nclient = SeedAnyAuth(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.user.get_admins()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedAnyAuth\n\nclient = AsyncSeedAnyAuth(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.user.get_admins()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedAnyAuth\n\nclient = SeedAnyAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.user.get_admins()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedAnyAuth\n\nclient = AsyncSeedAnyAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.user.get_admins()\n\n\nasyncio.run(main())\n", "type": "python" } } diff --git a/seed/python-sdk/any-auth/src/seed/auth/client.py b/seed/python-sdk/any-auth/src/seed/auth/client.py index ac5ff450cbc9..7cf980bd0d3f 100644 --- a/seed/python-sdk/any-auth/src/seed/auth/client.py +++ b/seed/python-sdk/any-auth/src/seed/auth/client.py @@ -49,6 +49,8 @@ def get_token( client = SeedAnyAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.auth.get_token( client_id="client_id", @@ -101,6 +103,8 @@ async def get_token( client = AsyncSeedAnyAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/any-auth/src/seed/client.py b/seed/python-sdk/any-auth/src/seed/client.py index c62e482f8981..33ff6f470377 100644 --- a/seed/python-sdk/any-auth/src/seed/client.py +++ b/seed/python-sdk/any-auth/src/seed/client.py @@ -64,6 +64,8 @@ class SeedAnyAuth: client = SeedAnyAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... @@ -249,6 +251,8 @@ class AsyncSeedAnyAuth: client = AsyncSeedAnyAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... diff --git a/seed/python-sdk/any-auth/src/seed/user/client.py b/seed/python-sdk/any-auth/src/seed/user/client.py index 8e24f39a0a73..169713adc2f6 100644 --- a/seed/python-sdk/any-auth/src/seed/user/client.py +++ b/seed/python-sdk/any-auth/src/seed/user/client.py @@ -40,6 +40,8 @@ def get(self, *, request_options: typing.Optional[RequestOptions] = None) -> typ client = SeedAnyAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.user.get() """ @@ -63,6 +65,8 @@ def get_admins(self, *, request_options: typing.Optional[RequestOptions] = None) client = SeedAnyAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.user.get_admins() """ @@ -104,6 +108,8 @@ async def get(self, *, request_options: typing.Optional[RequestOptions] = None) client = AsyncSeedAnyAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) @@ -135,6 +141,8 @@ async def get_admins(self, *, request_options: typing.Optional[RequestOptions] = client = AsyncSeedAnyAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/endpoint-security-auth/snippet.json b/seed/python-sdk/endpoint-security-auth/snippet.json index c991176a1b27..15ade025a069 100644 --- a/seed/python-sdk/endpoint-security-auth/snippet.json +++ b/seed/python-sdk/endpoint-security-auth/snippet.json @@ -9,8 +9,8 @@ "identifier_override": "endpoint_auth.getToken" }, "snippet": { - "sync_client": "from seed import SeedEndpointSecurityAuth\n\nclient = SeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n)\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedEndpointSecurityAuth\n\nclient = AsyncSeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n )\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedEndpointSecurityAuth\n\nclient = SeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedEndpointSecurityAuth\n\nclient = AsyncSeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n )\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -22,8 +22,8 @@ "identifier_override": "endpoint_user.getWithBearer" }, "snippet": { - "sync_client": "from seed import SeedEndpointSecurityAuth\n\nclient = SeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.user.get_with_bearer()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedEndpointSecurityAuth\n\nclient = AsyncSeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.user.get_with_bearer()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedEndpointSecurityAuth\n\nclient = SeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.user.get_with_bearer()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedEndpointSecurityAuth\n\nclient = AsyncSeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.user.get_with_bearer()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -35,8 +35,8 @@ "identifier_override": "endpoint_user.getWithApiKey" }, "snippet": { - "sync_client": "from seed import SeedEndpointSecurityAuth\n\nclient = SeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.user.get_with_api_key()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedEndpointSecurityAuth\n\nclient = AsyncSeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.user.get_with_api_key()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedEndpointSecurityAuth\n\nclient = SeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.user.get_with_api_key()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedEndpointSecurityAuth\n\nclient = AsyncSeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.user.get_with_api_key()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -48,8 +48,8 @@ "identifier_override": "endpoint_user.getWithOAuth" }, "snippet": { - "sync_client": "from seed import SeedEndpointSecurityAuth\n\nclient = SeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.user.get_with_o_auth()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedEndpointSecurityAuth\n\nclient = AsyncSeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.user.get_with_o_auth()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedEndpointSecurityAuth\n\nclient = SeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.user.get_with_o_auth()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedEndpointSecurityAuth\n\nclient = AsyncSeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.user.get_with_o_auth()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -61,8 +61,8 @@ "identifier_override": "endpoint_user.getWithBasic" }, "snippet": { - "sync_client": "from seed import SeedEndpointSecurityAuth\n\nclient = SeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.user.get_with_basic()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedEndpointSecurityAuth\n\nclient = AsyncSeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.user.get_with_basic()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedEndpointSecurityAuth\n\nclient = SeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.user.get_with_basic()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedEndpointSecurityAuth\n\nclient = AsyncSeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.user.get_with_basic()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -74,8 +74,8 @@ "identifier_override": "endpoint_user.getWithInferredAuth" }, "snippet": { - "sync_client": "from seed import SeedEndpointSecurityAuth\n\nclient = SeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.user.get_with_inferred_auth()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedEndpointSecurityAuth\n\nclient = AsyncSeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.user.get_with_inferred_auth()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedEndpointSecurityAuth\n\nclient = SeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.user.get_with_inferred_auth()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedEndpointSecurityAuth\n\nclient = AsyncSeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.user.get_with_inferred_auth()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -87,8 +87,8 @@ "identifier_override": "endpoint_user.getWithAnyAuth" }, "snippet": { - "sync_client": "from seed import SeedEndpointSecurityAuth\n\nclient = SeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.user.get_with_any_auth()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedEndpointSecurityAuth\n\nclient = AsyncSeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.user.get_with_any_auth()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedEndpointSecurityAuth\n\nclient = SeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.user.get_with_any_auth()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedEndpointSecurityAuth\n\nclient = AsyncSeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.user.get_with_any_auth()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -100,8 +100,8 @@ "identifier_override": "endpoint_user.getWithAllAuth" }, "snippet": { - "sync_client": "from seed import SeedEndpointSecurityAuth\n\nclient = SeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.user.get_with_all_auth()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedEndpointSecurityAuth\n\nclient = AsyncSeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.user.get_with_all_auth()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedEndpointSecurityAuth\n\nclient = SeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.user.get_with_all_auth()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedEndpointSecurityAuth\n\nclient = AsyncSeedEndpointSecurityAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.user.get_with_all_auth()\n\n\nasyncio.run(main())\n", "type": "python" } } diff --git a/seed/python-sdk/endpoint-security-auth/src/seed/auth/client.py b/seed/python-sdk/endpoint-security-auth/src/seed/auth/client.py index 4503d6f31c42..6225d114a6d6 100644 --- a/seed/python-sdk/endpoint-security-auth/src/seed/auth/client.py +++ b/seed/python-sdk/endpoint-security-auth/src/seed/auth/client.py @@ -49,6 +49,8 @@ def get_token( client = SeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.auth.get_token( client_id="client_id", @@ -101,6 +103,8 @@ async def get_token( client = AsyncSeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/endpoint-security-auth/src/seed/client.py b/seed/python-sdk/endpoint-security-auth/src/seed/client.py index ff6214fc25f6..c20f520c54eb 100644 --- a/seed/python-sdk/endpoint-security-auth/src/seed/client.py +++ b/seed/python-sdk/endpoint-security-auth/src/seed/client.py @@ -64,6 +64,8 @@ class SeedEndpointSecurityAuth: client = SeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... @@ -249,6 +251,8 @@ class AsyncSeedEndpointSecurityAuth: client = AsyncSeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... diff --git a/seed/python-sdk/endpoint-security-auth/src/seed/user/client.py b/seed/python-sdk/endpoint-security-auth/src/seed/user/client.py index 1706b909f5ca..f39f24fe3568 100644 --- a/seed/python-sdk/endpoint-security-auth/src/seed/user/client.py +++ b/seed/python-sdk/endpoint-security-auth/src/seed/user/client.py @@ -40,6 +40,8 @@ def get_with_bearer(self, *, request_options: typing.Optional[RequestOptions] = client = SeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.user.get_with_bearer() """ @@ -63,6 +65,8 @@ def get_with_api_key(self, *, request_options: typing.Optional[RequestOptions] = client = SeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.user.get_with_api_key() """ @@ -86,6 +90,8 @@ def get_with_o_auth(self, *, request_options: typing.Optional[RequestOptions] = client = SeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.user.get_with_o_auth() """ @@ -109,6 +115,8 @@ def get_with_basic(self, *, request_options: typing.Optional[RequestOptions] = N client = SeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.user.get_with_basic() """ @@ -132,6 +140,8 @@ def get_with_inferred_auth(self, *, request_options: typing.Optional[RequestOpti client = SeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.user.get_with_inferred_auth() """ @@ -155,6 +165,8 @@ def get_with_any_auth(self, *, request_options: typing.Optional[RequestOptions] client = SeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.user.get_with_any_auth() """ @@ -178,6 +190,8 @@ def get_with_all_auth(self, *, request_options: typing.Optional[RequestOptions] client = SeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.user.get_with_all_auth() """ @@ -219,6 +233,8 @@ async def get_with_bearer(self, *, request_options: typing.Optional[RequestOptio client = AsyncSeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) @@ -250,6 +266,8 @@ async def get_with_api_key(self, *, request_options: typing.Optional[RequestOpti client = AsyncSeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) @@ -281,6 +299,8 @@ async def get_with_o_auth(self, *, request_options: typing.Optional[RequestOptio client = AsyncSeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) @@ -312,6 +332,8 @@ async def get_with_basic(self, *, request_options: typing.Optional[RequestOption client = AsyncSeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) @@ -345,6 +367,8 @@ async def get_with_inferred_auth( client = AsyncSeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) @@ -376,6 +400,8 @@ async def get_with_any_auth(self, *, request_options: typing.Optional[RequestOpt client = AsyncSeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) @@ -407,6 +433,8 @@ async def get_with_all_auth(self, *, request_options: typing.Optional[RequestOpt client = AsyncSeedEndpointSecurityAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/enum/real-enum-forward-compat/README.md b/seed/python-sdk/enum/real-enum-forward-compat/README.md index 7216a5e50317..72ca9b95c891 100644 --- a/seed/python-sdk/enum/real-enum-forward-compat/README.md +++ b/seed/python-sdk/enum/real-enum-forward-compat/README.md @@ -34,16 +34,16 @@ A full reference for this library is available [here](./reference.md). Instantiate and use the client with the following: ```python -from seed import SeedEnum +from seed import SeedEnum, Operand, Color client = SeedEnum( base_url="https://yourhost.com/path/to/api", ) client.headers.send( - operand=">", - maybe_operand=">", - operand_or_color="red", + operand=Operand.GREATER_THAN, + maybe_operand=Operand.GREATER_THAN, + operand_or_color=Color.RED, ) ``` @@ -63,9 +63,9 @@ client = AsyncSeedEnum( async def main() -> None: await client.headers.send( - operand=">", - maybe_operand=">", - operand_or_color="red", + operand=Operand.GREATER_THAN, + maybe_operand=Operand.GREATER_THAN, + operand_or_color=Color.RED, ) diff --git a/seed/python-sdk/enum/real-enum-forward-compat/reference.md b/seed/python-sdk/enum/real-enum-forward-compat/reference.md index 867179931a77..a2ab4c24d678 100644 --- a/seed/python-sdk/enum/real-enum-forward-compat/reference.md +++ b/seed/python-sdk/enum/real-enum-forward-compat/reference.md @@ -13,16 +13,16 @@
```python -from seed import SeedEnum +from seed import SeedEnum, Operand, Color client = SeedEnum( base_url="https://yourhost.com/path/to/api", ) client.headers.send( - operand=">", - maybe_operand=">", - operand_or_color="red", + operand=Operand.GREATER_THAN, + maybe_operand=Operand.GREATER_THAN, + operand_or_color=Color.RED, ) ``` @@ -97,15 +97,15 @@ client.headers.send(
```python -from seed import SeedEnum +from seed import SeedEnum, Operand, Color client = SeedEnum( base_url="https://yourhost.com/path/to/api", ) client.inlined_request.send( - operand=">", - operand_or_color="red", + operand=Operand.GREATER_THAN, + operand_or_color=Color.RED, ) ``` @@ -253,15 +253,15 @@ client.multipart_form.multipart_form(...)
```python -from seed import SeedEnum +from seed import SeedEnum, Operand, Color client = SeedEnum( base_url="https://yourhost.com/path/to/api", ) client.path_param.send( - operand=">", - operand_or_color="red", + operand=Operand.GREATER_THAN, + operand_or_color=Color.RED, ) ``` @@ -320,15 +320,15 @@ client.path_param.send(
```python -from seed import SeedEnum +from seed import SeedEnum, Operand, Color client = SeedEnum( base_url="https://yourhost.com/path/to/api", ) client.query_param.send( - operand=">", - operand_or_color="red", + operand=Operand.GREATER_THAN, + operand_or_color=Color.RED, ) ``` @@ -402,7 +402,7 @@ client.query_param.send(
```python -from seed import SeedEnum +from seed import SeedEnum, Operand, Color client = SeedEnum( base_url="https://yourhost.com/path/to/api", @@ -410,16 +410,16 @@ client = SeedEnum( client.query_param.send_list( operand=[ - ">" + Operand.GREATER_THAN ], maybe_operand=[ - ">" + Operand.GREATER_THAN ], operand_or_color=[ - "red" + Color.RED ], maybe_operand_or_color=[ - "red" + Color.RED ], ) diff --git a/seed/python-sdk/enum/real-enum/README.md b/seed/python-sdk/enum/real-enum/README.md index 7216a5e50317..72ca9b95c891 100644 --- a/seed/python-sdk/enum/real-enum/README.md +++ b/seed/python-sdk/enum/real-enum/README.md @@ -34,16 +34,16 @@ A full reference for this library is available [here](./reference.md). Instantiate and use the client with the following: ```python -from seed import SeedEnum +from seed import SeedEnum, Operand, Color client = SeedEnum( base_url="https://yourhost.com/path/to/api", ) client.headers.send( - operand=">", - maybe_operand=">", - operand_or_color="red", + operand=Operand.GREATER_THAN, + maybe_operand=Operand.GREATER_THAN, + operand_or_color=Color.RED, ) ``` @@ -63,9 +63,9 @@ client = AsyncSeedEnum( async def main() -> None: await client.headers.send( - operand=">", - maybe_operand=">", - operand_or_color="red", + operand=Operand.GREATER_THAN, + maybe_operand=Operand.GREATER_THAN, + operand_or_color=Color.RED, ) diff --git a/seed/python-sdk/enum/real-enum/reference.md b/seed/python-sdk/enum/real-enum/reference.md index 867179931a77..a2ab4c24d678 100644 --- a/seed/python-sdk/enum/real-enum/reference.md +++ b/seed/python-sdk/enum/real-enum/reference.md @@ -13,16 +13,16 @@
```python -from seed import SeedEnum +from seed import SeedEnum, Operand, Color client = SeedEnum( base_url="https://yourhost.com/path/to/api", ) client.headers.send( - operand=">", - maybe_operand=">", - operand_or_color="red", + operand=Operand.GREATER_THAN, + maybe_operand=Operand.GREATER_THAN, + operand_or_color=Color.RED, ) ``` @@ -97,15 +97,15 @@ client.headers.send(
```python -from seed import SeedEnum +from seed import SeedEnum, Operand, Color client = SeedEnum( base_url="https://yourhost.com/path/to/api", ) client.inlined_request.send( - operand=">", - operand_or_color="red", + operand=Operand.GREATER_THAN, + operand_or_color=Color.RED, ) ``` @@ -253,15 +253,15 @@ client.multipart_form.multipart_form(...)
```python -from seed import SeedEnum +from seed import SeedEnum, Operand, Color client = SeedEnum( base_url="https://yourhost.com/path/to/api", ) client.path_param.send( - operand=">", - operand_or_color="red", + operand=Operand.GREATER_THAN, + operand_or_color=Color.RED, ) ``` @@ -320,15 +320,15 @@ client.path_param.send(
```python -from seed import SeedEnum +from seed import SeedEnum, Operand, Color client = SeedEnum( base_url="https://yourhost.com/path/to/api", ) client.query_param.send( - operand=">", - operand_or_color="red", + operand=Operand.GREATER_THAN, + operand_or_color=Color.RED, ) ``` @@ -402,7 +402,7 @@ client.query_param.send(
```python -from seed import SeedEnum +from seed import SeedEnum, Operand, Color client = SeedEnum( base_url="https://yourhost.com/path/to/api", @@ -410,16 +410,16 @@ client = SeedEnum( client.query_param.send_list( operand=[ - ">" + Operand.GREATER_THAN ], maybe_operand=[ - ">" + Operand.GREATER_THAN ], operand_or_color=[ - "red" + Color.RED ], maybe_operand_or_color=[ - "red" + Color.RED ], ) diff --git a/seed/python-sdk/examples/additional_init_exports_with_duplicates/reference.md b/seed/python-sdk/examples/additional_init_exports_with_duplicates/reference.md index 7dc7fea57018..a8376bd0e330 100644 --- a/seed/python-sdk/examples/additional_init_exports_with_duplicates/reference.md +++ b/seed/python-sdk/examples/additional_init_exports_with_duplicates/reference.md @@ -613,8 +613,8 @@ client.service.get_metadata( ```python from seed import SeedExamples from seed.environment import SeedExamplesEnvironment -from seed.types import Actor, ExtendedMovie, Entity, Migration, Node, Tree, Directory, File, Moment -from seed.commons.types import Metadata +from seed.types import Actor, ExtendedMovie, Entity, Metadata_Html, Migration, Exception_Generic, Test_And, Node, Tree, Directory, File, Moment +from seed.commons.types import Metadata, EventInfo_Metadata, Data_String import uuid import datetime @@ -650,15 +650,15 @@ client.service.create_big_entity( type="primitive", name="name", ), - metadata={ - "type": "html", - "extra": { + metadata=Metadata_Html( + extra={ "extra": "extra" }, - "tags": [ + tags=[ "tags" - ] - }, + ], + html=, + ), common_metadata=Metadata( id="id", data={ @@ -666,30 +666,28 @@ client.service.create_big_entity( }, json_string="jsonString", ), - event_info={ - "type": "metadata", - "id": "id", - "data": { + event_info=EventInfo_Metadata( + id="id", + data={ "data": "data" }, - "json_string": "jsonString" - }, - data={ - "type": "string" - }, + json_string="jsonString", + ), + data=Data_String( + string=, + ), migration=Migration( name="name", status="RUNNING", ), - exception={ - "type": "generic", - "exception_type": "exceptionType", - "exception_message": "exceptionMessage", - "exception_stacktrace": "exceptionStacktrace" - }, - test={ - "type": "and" - }, + exception=Exception_Generic( + exception_type="exceptionType", + exception_message="exceptionMessage", + exception_stacktrace="exceptionStacktrace", + ), + test=Test_And( + and_=, + ), node=Node( name="name", nodes=[ diff --git a/seed/python-sdk/examples/client-filename/reference.md b/seed/python-sdk/examples/client-filename/reference.md index 8ba7010a420d..b59fc542a5f2 100644 --- a/seed/python-sdk/examples/client-filename/reference.md +++ b/seed/python-sdk/examples/client-filename/reference.md @@ -613,8 +613,8 @@ client.service.get_metadata( ```python from seed import SeedExhaustive from seed.environment import SeedExhaustiveEnvironment -from seed.types import Actor, ExtendedMovie, Entity, Migration, Node, Tree, Directory, File, Moment -from seed.commons.types import Metadata +from seed.types import Actor, ExtendedMovie, Entity, Metadata_Html, Migration, Exception_Generic, Test_And, Node, Tree, Directory, File, Moment +from seed.commons.types import Metadata, EventInfo_Metadata, Data_String import uuid import datetime @@ -650,15 +650,15 @@ client.service.create_big_entity( type="primitive", name="name", ), - metadata={ - "type": "html", - "extra": { + metadata=Metadata_Html( + extra={ "extra": "extra" }, - "tags": [ + tags=[ "tags" - ] - }, + ], + html=, + ), common_metadata=Metadata( id="id", data={ @@ -666,30 +666,28 @@ client.service.create_big_entity( }, json_string="jsonString", ), - event_info={ - "type": "metadata", - "id": "id", - "data": { + event_info=EventInfo_Metadata( + id="id", + data={ "data": "data" }, - "json_string": "jsonString" - }, - data={ - "type": "string" - }, + json_string="jsonString", + ), + data=Data_String( + string=, + ), migration=Migration( name="name", status="RUNNING", ), - exception={ - "type": "generic", - "exception_type": "exceptionType", - "exception_message": "exceptionMessage", - "exception_stacktrace": "exceptionStacktrace" - }, - test={ - "type": "and" - }, + exception=Exception_Generic( + exception_type="exceptionType", + exception_message="exceptionMessage", + exception_stacktrace="exceptionStacktrace", + ), + test=Test_And( + and_=, + ), node=Node( name="name", nodes=[ diff --git a/seed/python-sdk/examples/legacy-wire-tests/reference.md b/seed/python-sdk/examples/legacy-wire-tests/reference.md index 7dc7fea57018..a8376bd0e330 100644 --- a/seed/python-sdk/examples/legacy-wire-tests/reference.md +++ b/seed/python-sdk/examples/legacy-wire-tests/reference.md @@ -613,8 +613,8 @@ client.service.get_metadata( ```python from seed import SeedExamples from seed.environment import SeedExamplesEnvironment -from seed.types import Actor, ExtendedMovie, Entity, Migration, Node, Tree, Directory, File, Moment -from seed.commons.types import Metadata +from seed.types import Actor, ExtendedMovie, Entity, Metadata_Html, Migration, Exception_Generic, Test_And, Node, Tree, Directory, File, Moment +from seed.commons.types import Metadata, EventInfo_Metadata, Data_String import uuid import datetime @@ -650,15 +650,15 @@ client.service.create_big_entity( type="primitive", name="name", ), - metadata={ - "type": "html", - "extra": { + metadata=Metadata_Html( + extra={ "extra": "extra" }, - "tags": [ + tags=[ "tags" - ] - }, + ], + html=, + ), common_metadata=Metadata( id="id", data={ @@ -666,30 +666,28 @@ client.service.create_big_entity( }, json_string="jsonString", ), - event_info={ - "type": "metadata", - "id": "id", - "data": { + event_info=EventInfo_Metadata( + id="id", + data={ "data": "data" }, - "json_string": "jsonString" - }, - data={ - "type": "string" - }, + json_string="jsonString", + ), + data=Data_String( + string=, + ), migration=Migration( name="name", status="RUNNING", ), - exception={ - "type": "generic", - "exception_type": "exceptionType", - "exception_message": "exceptionMessage", - "exception_stacktrace": "exceptionStacktrace" - }, - test={ - "type": "and" - }, + exception=Exception_Generic( + exception_type="exceptionType", + exception_message="exceptionMessage", + exception_stacktrace="exceptionStacktrace", + ), + test=Test_And( + and_=, + ), node=Node( name="name", nodes=[ diff --git a/seed/python-sdk/examples/no-custom-config/reference.md b/seed/python-sdk/examples/no-custom-config/reference.md index 7dc7fea57018..a8376bd0e330 100644 --- a/seed/python-sdk/examples/no-custom-config/reference.md +++ b/seed/python-sdk/examples/no-custom-config/reference.md @@ -613,8 +613,8 @@ client.service.get_metadata( ```python from seed import SeedExamples from seed.environment import SeedExamplesEnvironment -from seed.types import Actor, ExtendedMovie, Entity, Migration, Node, Tree, Directory, File, Moment -from seed.commons.types import Metadata +from seed.types import Actor, ExtendedMovie, Entity, Metadata_Html, Migration, Exception_Generic, Test_And, Node, Tree, Directory, File, Moment +from seed.commons.types import Metadata, EventInfo_Metadata, Data_String import uuid import datetime @@ -650,15 +650,15 @@ client.service.create_big_entity( type="primitive", name="name", ), - metadata={ - "type": "html", - "extra": { + metadata=Metadata_Html( + extra={ "extra": "extra" }, - "tags": [ + tags=[ "tags" - ] - }, + ], + html=, + ), common_metadata=Metadata( id="id", data={ @@ -666,30 +666,28 @@ client.service.create_big_entity( }, json_string="jsonString", ), - event_info={ - "type": "metadata", - "id": "id", - "data": { + event_info=EventInfo_Metadata( + id="id", + data={ "data": "data" }, - "json_string": "jsonString" - }, - data={ - "type": "string" - }, + json_string="jsonString", + ), + data=Data_String( + string=, + ), migration=Migration( name="name", status="RUNNING", ), - exception={ - "type": "generic", - "exception_type": "exceptionType", - "exception_message": "exceptionMessage", - "exception_stacktrace": "exceptionStacktrace" - }, - test={ - "type": "and" - }, + exception=Exception_Generic( + exception_type="exceptionType", + exception_message="exceptionMessage", + exception_stacktrace="exceptionStacktrace", + ), + test=Test_And( + and_=, + ), node=Node( name="name", nodes=[ diff --git a/seed/python-sdk/examples/omit-fern-headers/reference.md b/seed/python-sdk/examples/omit-fern-headers/reference.md index 7dc7fea57018..a8376bd0e330 100644 --- a/seed/python-sdk/examples/omit-fern-headers/reference.md +++ b/seed/python-sdk/examples/omit-fern-headers/reference.md @@ -613,8 +613,8 @@ client.service.get_metadata( ```python from seed import SeedExamples from seed.environment import SeedExamplesEnvironment -from seed.types import Actor, ExtendedMovie, Entity, Migration, Node, Tree, Directory, File, Moment -from seed.commons.types import Metadata +from seed.types import Actor, ExtendedMovie, Entity, Metadata_Html, Migration, Exception_Generic, Test_And, Node, Tree, Directory, File, Moment +from seed.commons.types import Metadata, EventInfo_Metadata, Data_String import uuid import datetime @@ -650,15 +650,15 @@ client.service.create_big_entity( type="primitive", name="name", ), - metadata={ - "type": "html", - "extra": { + metadata=Metadata_Html( + extra={ "extra": "extra" }, - "tags": [ + tags=[ "tags" - ] - }, + ], + html=, + ), common_metadata=Metadata( id="id", data={ @@ -666,30 +666,28 @@ client.service.create_big_entity( }, json_string="jsonString", ), - event_info={ - "type": "metadata", - "id": "id", - "data": { + event_info=EventInfo_Metadata( + id="id", + data={ "data": "data" }, - "json_string": "jsonString" - }, - data={ - "type": "string" - }, + json_string="jsonString", + ), + data=Data_String( + string=, + ), migration=Migration( name="name", status="RUNNING", ), - exception={ - "type": "generic", - "exception_type": "exceptionType", - "exception_message": "exceptionMessage", - "exception_stacktrace": "exceptionStacktrace" - }, - test={ - "type": "and" - }, + exception=Exception_Generic( + exception_type="exceptionType", + exception_message="exceptionMessage", + exception_stacktrace="exceptionStacktrace", + ), + test=Test_And( + and_=, + ), node=Node( name="name", nodes=[ diff --git a/seed/python-sdk/examples/readme/reference.md b/seed/python-sdk/examples/readme/reference.md index 7dc7fea57018..a8376bd0e330 100644 --- a/seed/python-sdk/examples/readme/reference.md +++ b/seed/python-sdk/examples/readme/reference.md @@ -613,8 +613,8 @@ client.service.get_metadata( ```python from seed import SeedExamples from seed.environment import SeedExamplesEnvironment -from seed.types import Actor, ExtendedMovie, Entity, Migration, Node, Tree, Directory, File, Moment -from seed.commons.types import Metadata +from seed.types import Actor, ExtendedMovie, Entity, Metadata_Html, Migration, Exception_Generic, Test_And, Node, Tree, Directory, File, Moment +from seed.commons.types import Metadata, EventInfo_Metadata, Data_String import uuid import datetime @@ -650,15 +650,15 @@ client.service.create_big_entity( type="primitive", name="name", ), - metadata={ - "type": "html", - "extra": { + metadata=Metadata_Html( + extra={ "extra": "extra" }, - "tags": [ + tags=[ "tags" - ] - }, + ], + html=, + ), common_metadata=Metadata( id="id", data={ @@ -666,30 +666,28 @@ client.service.create_big_entity( }, json_string="jsonString", ), - event_info={ - "type": "metadata", - "id": "id", - "data": { + event_info=EventInfo_Metadata( + id="id", + data={ "data": "data" }, - "json_string": "jsonString" - }, - data={ - "type": "string" - }, + json_string="jsonString", + ), + data=Data_String( + string=, + ), migration=Migration( name="name", status="RUNNING", ), - exception={ - "type": "generic", - "exception_type": "exceptionType", - "exception_message": "exceptionMessage", - "exception_stacktrace": "exceptionStacktrace" - }, - test={ - "type": "and" - }, + exception=Exception_Generic( + exception_type="exceptionType", + exception_message="exceptionMessage", + exception_stacktrace="exceptionStacktrace", + ), + test=Test_And( + and_=, + ), node=Node( name="name", nodes=[ diff --git a/seed/python-sdk/exhaustive/additional_init_exports/reference.md b/seed/python-sdk/exhaustive/additional_init_exports/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/additional_init_exports/reference.md +++ b/seed/python-sdk/exhaustive/additional_init_exports/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/aliases_with_validation/reference.md b/seed/python-sdk/exhaustive/aliases_with_validation/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/aliases_with_validation/reference.md +++ b/seed/python-sdk/exhaustive/aliases_with_validation/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/aliases_without_validation/reference.md b/seed/python-sdk/exhaustive/aliases_without_validation/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/aliases_without_validation/reference.md +++ b/seed/python-sdk/exhaustive/aliases_without_validation/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/custom-transport/reference.md b/seed/python-sdk/exhaustive/custom-transport/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/custom-transport/reference.md +++ b/seed/python-sdk/exhaustive/custom-transport/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/datetime-milliseconds/reference.md b/seed/python-sdk/exhaustive/datetime-milliseconds/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/datetime-milliseconds/reference.md +++ b/seed/python-sdk/exhaustive/datetime-milliseconds/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/deps_with_min_python_version/reference.md b/seed/python-sdk/exhaustive/deps_with_min_python_version/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/deps_with_min_python_version/reference.md +++ b/seed/python-sdk/exhaustive/deps_with_min_python_version/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/eager-imports/reference.md b/seed/python-sdk/exhaustive/eager-imports/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/eager-imports/reference.md +++ b/seed/python-sdk/exhaustive/eager-imports/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/extra_dependencies/reference.md b/seed/python-sdk/exhaustive/extra_dependencies/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/extra_dependencies/reference.md +++ b/seed/python-sdk/exhaustive/extra_dependencies/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/extra_dev_dependencies/reference.md b/seed/python-sdk/exhaustive/extra_dev_dependencies/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/extra_dev_dependencies/reference.md +++ b/seed/python-sdk/exhaustive/extra_dev_dependencies/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/five-second-timeout/reference.md b/seed/python-sdk/exhaustive/five-second-timeout/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/five-second-timeout/reference.md +++ b/seed/python-sdk/exhaustive/five-second-timeout/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/follow_redirects_by_default/reference.md b/seed/python-sdk/exhaustive/follow_redirects_by_default/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/follow_redirects_by_default/reference.md +++ b/seed/python-sdk/exhaustive/follow_redirects_by_default/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/import-paths/reference.md b/seed/python-sdk/exhaustive/import-paths/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/import-paths/reference.md +++ b/seed/python-sdk/exhaustive/import-paths/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/improved_imports/reference.md b/seed/python-sdk/exhaustive/improved_imports/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/improved_imports/reference.md +++ b/seed/python-sdk/exhaustive/improved_imports/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/infinite-timeout/reference.md b/seed/python-sdk/exhaustive/infinite-timeout/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/infinite-timeout/reference.md +++ b/seed/python-sdk/exhaustive/infinite-timeout/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/inline-path-params/reference.md b/seed/python-sdk/exhaustive/inline-path-params/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/inline-path-params/reference.md +++ b/seed/python-sdk/exhaustive/inline-path-params/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/inline_request_params/reference.md b/seed/python-sdk/exhaustive/inline_request_params/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/inline_request_params/reference.md +++ b/seed/python-sdk/exhaustive/inline_request_params/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/no-custom-config/reference.md b/seed/python-sdk/exhaustive/no-custom-config/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/no-custom-config/reference.md +++ b/seed/python-sdk/exhaustive/no-custom-config/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/no-custom-config/tests/wire/test_endpoints_union.py b/seed/python-sdk/exhaustive/no-custom-config/tests/wire/test_endpoints_union.py index 55395be159ef..ec73fb4ff81a 100644 --- a/seed/python-sdk/exhaustive/no-custom-config/tests/wire/test_endpoints_union.py +++ b/seed/python-sdk/exhaustive/no-custom-config/tests/wire/test_endpoints_union.py @@ -1,11 +1,16 @@ from .conftest import get_client, verify_request_count +from seed.types.union import Animal_Dog + def test_endpoints_union_get_and_return_union() -> None: """Test getAndReturnUnion endpoint with WireMock""" test_id = "endpoints.union.get_and_return_union.0" client = get_client(test_id) client.endpoints.union.get_and_return_union( - request={"animal": "dog", "name": "name", "likes_to_woof": True}, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) verify_request_count(test_id, "POST", "/union", None, 1) diff --git a/seed/python-sdk/exhaustive/package-path/reference.md b/seed/python-sdk/exhaustive/package-path/reference.md index 5ba59360cd13..207641d21159 100644 --- a/seed/python-sdk/exhaustive/package-path/reference.md +++ b/seed/python-sdk/exhaustive/package-path/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/pydantic-extra-fields/reference.md b/seed/python-sdk/exhaustive/pydantic-extra-fields/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/pydantic-extra-fields/reference.md +++ b/seed/python-sdk/exhaustive/pydantic-extra-fields/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/pydantic-ignore-fields/reference.md b/seed/python-sdk/exhaustive/pydantic-ignore-fields/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/pydantic-ignore-fields/reference.md +++ b/seed/python-sdk/exhaustive/pydantic-ignore-fields/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/pydantic-v1-with-utils/reference.md b/seed/python-sdk/exhaustive/pydantic-v1-with-utils/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1-with-utils/reference.md +++ b/seed/python-sdk/exhaustive/pydantic-v1-with-utils/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/pydantic-v1-wrapped/reference.md b/seed/python-sdk/exhaustive/pydantic-v1-wrapped/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1-wrapped/reference.md +++ b/seed/python-sdk/exhaustive/pydantic-v1-wrapped/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/pydantic-v1/reference.md b/seed/python-sdk/exhaustive/pydantic-v1/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1/reference.md +++ b/seed/python-sdk/exhaustive/pydantic-v1/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/pydantic-v2-wrapped/reference.md b/seed/python-sdk/exhaustive/pydantic-v2-wrapped/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/pydantic-v2-wrapped/reference.md +++ b/seed/python-sdk/exhaustive/pydantic-v2-wrapped/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/pyproject_extras/reference.md b/seed/python-sdk/exhaustive/pyproject_extras/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/pyproject_extras/reference.md +++ b/seed/python-sdk/exhaustive/pyproject_extras/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/skip-pydantic-validation/reference.md b/seed/python-sdk/exhaustive/skip-pydantic-validation/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/skip-pydantic-validation/reference.md +++ b/seed/python-sdk/exhaustive/skip-pydantic-validation/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/union-utils/reference.md b/seed/python-sdk/exhaustive/union-utils/reference.md index 221e14ecddcc..b63cab942d1a 100644 --- a/seed/python-sdk/exhaustive/union-utils/reference.md +++ b/seed/python-sdk/exhaustive/union-utils/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import SeedExhaustive +from seed.types.union import Animal_Dog client = SeedExhaustive( token="", @@ -3253,11 +3254,10 @@ client = SeedExhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/wire-tests-custom-client-name/reference.md b/seed/python-sdk/exhaustive/wire-tests-custom-client-name/reference.md index 2f9864859326..c08b46b18e62 100644 --- a/seed/python-sdk/exhaustive/wire-tests-custom-client-name/reference.md +++ b/seed/python-sdk/exhaustive/wire-tests-custom-client-name/reference.md @@ -3246,6 +3246,7 @@ client.endpoints.put.add( ```python from seed import Exhaustive +from seed.types.union import Animal_Dog client = Exhaustive( token="", @@ -3253,11 +3254,10 @@ client = Exhaustive( ) client.endpoints.union.get_and_return_union( - request={ - "animal": "dog", - "name": "name", - "likes_to_woof": True - }, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) ``` diff --git a/seed/python-sdk/exhaustive/wire-tests-custom-client-name/tests/wire/test_endpoints_union.py b/seed/python-sdk/exhaustive/wire-tests-custom-client-name/tests/wire/test_endpoints_union.py index 55395be159ef..ec73fb4ff81a 100644 --- a/seed/python-sdk/exhaustive/wire-tests-custom-client-name/tests/wire/test_endpoints_union.py +++ b/seed/python-sdk/exhaustive/wire-tests-custom-client-name/tests/wire/test_endpoints_union.py @@ -1,11 +1,16 @@ from .conftest import get_client, verify_request_count +from seed.types.union import Animal_Dog + def test_endpoints_union_get_and_return_union() -> None: """Test getAndReturnUnion endpoint with WireMock""" test_id = "endpoints.union.get_and_return_union.0" client = get_client(test_id) client.endpoints.union.get_and_return_union( - request={"animal": "dog", "name": "name", "likes_to_woof": True}, + request=Animal_Dog( + name="name", + likes_to_woof=True, + ), ) verify_request_count(test_id, "POST", "/union", None, 1) diff --git a/seed/python-sdk/nullable-optional/reference.md b/seed/python-sdk/nullable-optional/reference.md index 5912452f0dcf..748a9e114fd3 100644 --- a/seed/python-sdk/nullable-optional/reference.md +++ b/seed/python-sdk/nullable-optional/reference.md @@ -470,8 +470,8 @@ Create a complex profile to test nullable enums and unions ```python from seed import SeedNullableOptional +from seed.nullable_optional import NotificationMethod_Email, SearchResult_User, Address import datetime -from seed.nullable_optional import Address client = SeedNullableOptional( base_url="https://yourhost.com/path/to/api", @@ -485,33 +485,29 @@ client.nullable_optional.create_complex_profile( nullable_status="active", optional_status="active", optional_nullable_status="active", - nullable_notification={ - "type": "email", - "email_address": "emailAddress", - "subject": "subject", - "html_content": "htmlContent" - }, - optional_notification={ - "type": "email", - "email_address": "emailAddress", - "subject": "subject", - "html_content": "htmlContent" - }, - optional_nullable_notification={ - "type": "email", - "email_address": "emailAddress", - "subject": "subject", - "html_content": "htmlContent" - }, - nullable_search_result={ - "type": "user", - "id": "id", - "username": "username", - "email": "email", - "phone": "phone", - "created_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "updated_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "address": Address( + nullable_notification=NotificationMethod_Email( + email_address="emailAddress", + subject="subject", + html_content="htmlContent", + ), + optional_notification=NotificationMethod_Email( + email_address="emailAddress", + subject="subject", + html_content="htmlContent", + ), + optional_nullable_notification=NotificationMethod_Email( + email_address="emailAddress", + subject="subject", + html_content="htmlContent", + ), + nullable_search_result=SearchResult_User( + id="id", + username="username", + email="email", + phone="phone", + created_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + updated_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + address=Address( street="street", city="city", state="state", @@ -519,17 +515,16 @@ client.nullable_optional.create_complex_profile( country="country", building_id="buildingId", tenant_id="tenantId", - ) - }, - optional_search_result={ - "type": "user", - "id": "id", - "username": "username", - "email": "email", - "phone": "phone", - "created_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "updated_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "address": Address( + ), + ), + optional_search_result=SearchResult_User( + id="id", + username="username", + email="email", + phone="phone", + created_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + updated_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + address=Address( street="street", city="city", state="state", @@ -537,8 +532,8 @@ client.nullable_optional.create_complex_profile( country="country", building_id="buildingId", tenant_id="tenantId", - ) - }, + ), + ), nullable_array=[ "nullableArray", "nullableArray" @@ -567,18 +562,16 @@ client.nullable_optional.create_complex_profile( ) }, nullable_list_of_unions=[ - { - "type": "email", - "email_address": "emailAddress", - "subject": "subject", - "html_content": "htmlContent" - }, - { - "type": "email", - "email_address": "emailAddress", - "subject": "subject", - "html_content": "htmlContent" - } + NotificationMethod_Email( + email_address="emailAddress", + subject="subject", + html_content="htmlContent", + ), + NotificationMethod_Email( + email_address="emailAddress", + subject="subject", + html_content="htmlContent", + ) ], optional_map_of_enums={ "optionalMapOfEnums": "ADMIN" @@ -718,8 +711,8 @@ Update complex profile to test nullable field updates ```python from seed import SeedNullableOptional +from seed.nullable_optional import NotificationMethod_Email, SearchResult_User, Address import datetime -from seed.nullable_optional import Address client = SeedNullableOptional( base_url="https://yourhost.com/path/to/api", @@ -729,21 +722,19 @@ client.nullable_optional.update_complex_profile( profile_id="profileId", nullable_role="ADMIN", nullable_status="active", - nullable_notification={ - "type": "email", - "email_address": "emailAddress", - "subject": "subject", - "html_content": "htmlContent" - }, - nullable_search_result={ - "type": "user", - "id": "id", - "username": "username", - "email": "email", - "phone": "phone", - "created_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "updated_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "address": Address( + nullable_notification=NotificationMethod_Email( + email_address="emailAddress", + subject="subject", + html_content="htmlContent", + ), + nullable_search_result=SearchResult_User( + id="id", + username="username", + email="email", + phone="phone", + created_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + updated_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + address=Address( street="street", city="city", state="state", @@ -751,8 +742,8 @@ client.nullable_optional.update_complex_profile( country="country", building_id="buildingId", tenant_id="tenantId", - ) - }, + ), + ), nullable_array=[ "nullableArray", "nullableArray" @@ -861,8 +852,8 @@ Test endpoint for validating null deserialization ```python from seed import SeedNullableOptional +from seed.nullable_optional import NotificationMethod_Email, SearchResult_User, Address, Organization import datetime -from seed.nullable_optional import Address, Organization client = SeedNullableOptional( base_url="https://yourhost.com/path/to/api", @@ -875,21 +866,19 @@ client.nullable_optional.test_deserialization( optional_nullable_string="optionalNullableString", nullable_enum="ADMIN", optional_enum="active", - nullable_union={ - "type": "email", - "email_address": "emailAddress", - "subject": "subject", - "html_content": "htmlContent" - }, - optional_union={ - "type": "user", - "id": "id", - "username": "username", - "email": "email", - "phone": "phone", - "created_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "updated_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "address": Address( + nullable_union=NotificationMethod_Email( + email_address="emailAddress", + subject="subject", + html_content="htmlContent", + ), + optional_union=SearchResult_User( + id="id", + username="username", + email="email", + phone="phone", + created_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + updated_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + address=Address( street="street", city="city", state="state", @@ -897,8 +886,8 @@ client.nullable_optional.test_deserialization( country="country", building_id="buildingId", tenant_id="tenantId", - ) - }, + ), + ), nullable_list=[ "nullableList", "nullableList" diff --git a/seed/python-sdk/nullable/no-custom-config/README.md b/seed/python-sdk/nullable/no-custom-config/README.md index 2506cad36ba3..d59ff2bfcd5c 100644 --- a/seed/python-sdk/nullable/no-custom-config/README.md +++ b/seed/python-sdk/nullable/no-custom-config/README.md @@ -35,7 +35,7 @@ Instantiate and use the client with the following: ```python from seed import SeedNullable -from seed.nullable import Metadata +from seed.nullable import Metadata, Status_Active import datetime client = SeedNullable( @@ -53,9 +53,7 @@ client.nullable.create_user( updated_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), avatar="avatar", activated=True, - status={ - "type": "active" - }, + status=Status_Active(), values={ "values": "values" }, @@ -70,7 +68,7 @@ The SDK also exports an `async` client so that you can make non-blocking calls t ```python import asyncio -from seed.nullable import Metadata +from seed.nullable import Metadata, Status_Active import datetime from seed import AsyncSeedNullable @@ -92,9 +90,7 @@ async def main() -> None: updated_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), avatar="avatar", activated=True, - status={ - "type": "active" - }, + status=Status_Active(), values={ "values": "values" }, diff --git a/seed/python-sdk/nullable/no-custom-config/reference.md b/seed/python-sdk/nullable/no-custom-config/reference.md index 317d9201a0c1..33bae979aa70 100644 --- a/seed/python-sdk/nullable/no-custom-config/reference.md +++ b/seed/python-sdk/nullable/no-custom-config/reference.md @@ -113,7 +113,7 @@ client.nullable.get_users( ```python from seed import SeedNullable -from seed.nullable import Metadata +from seed.nullable import Metadata, Status_Active import datetime client = SeedNullable( @@ -131,9 +131,7 @@ client.nullable.create_user( updated_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), avatar="avatar", activated=True, - status={ - "type": "active" - }, + status=Status_Active(), values={ "values": "values" }, diff --git a/seed/python-sdk/nullable/use-typeddict-requests/README.md b/seed/python-sdk/nullable/use-typeddict-requests/README.md index 2506cad36ba3..d59ff2bfcd5c 100644 --- a/seed/python-sdk/nullable/use-typeddict-requests/README.md +++ b/seed/python-sdk/nullable/use-typeddict-requests/README.md @@ -35,7 +35,7 @@ Instantiate and use the client with the following: ```python from seed import SeedNullable -from seed.nullable import Metadata +from seed.nullable import Metadata, Status_Active import datetime client = SeedNullable( @@ -53,9 +53,7 @@ client.nullable.create_user( updated_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), avatar="avatar", activated=True, - status={ - "type": "active" - }, + status=Status_Active(), values={ "values": "values" }, @@ -70,7 +68,7 @@ The SDK also exports an `async` client so that you can make non-blocking calls t ```python import asyncio -from seed.nullable import Metadata +from seed.nullable import Metadata, Status_Active import datetime from seed import AsyncSeedNullable @@ -92,9 +90,7 @@ async def main() -> None: updated_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), avatar="avatar", activated=True, - status={ - "type": "active" - }, + status=Status_Active(), values={ "values": "values" }, diff --git a/seed/python-sdk/nullable/use-typeddict-requests/reference.md b/seed/python-sdk/nullable/use-typeddict-requests/reference.md index 317d9201a0c1..33bae979aa70 100644 --- a/seed/python-sdk/nullable/use-typeddict-requests/reference.md +++ b/seed/python-sdk/nullable/use-typeddict-requests/reference.md @@ -113,7 +113,7 @@ client.nullable.get_users( ```python from seed import SeedNullable -from seed.nullable import Metadata +from seed.nullable import Metadata, Status_Active import datetime client = SeedNullable( @@ -131,9 +131,7 @@ client.nullable.create_user( updated_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), avatar="avatar", activated=True, - status={ - "type": "active" - }, + status=Status_Active(), values={ "values": "values" }, diff --git a/seed/python-sdk/oauth-client-credentials-custom/snippet.json b/seed/python-sdk/oauth-client-credentials-custom/snippet.json index 8abd46727fd4..4e90a7707d60 100644 --- a/seed/python-sdk/oauth-client-credentials-custom/snippet.json +++ b/seed/python-sdk/oauth-client-credentials-custom/snippet.json @@ -9,8 +9,8 @@ "identifier_override": "endpoint_auth.getTokenWithClientCredentials" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.auth.get_token_with_client_credentials(\n cid=\"cid\",\n csr=\"csr\",\n scp=\"scp\",\n entity_id=\"entity_id\",\n scope=\"scope\",\n)\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token_with_client_credentials(\n cid=\"cid\",\n csr=\"csr\",\n scp=\"scp\",\n entity_id=\"entity_id\",\n scope=\"scope\",\n )\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.auth.get_token_with_client_credentials(\n cid=\"cid\",\n csr=\"csr\",\n scp=\"scp\",\n entity_id=\"entity_id\",\n scope=\"scope\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token_with_client_credentials(\n cid=\"cid\",\n csr=\"csr\",\n scp=\"scp\",\n entity_id=\"entity_id\",\n scope=\"scope\",\n )\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -22,8 +22,8 @@ "identifier_override": "endpoint_auth.refreshToken" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.auth.refresh_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n refresh_token=\"refresh_token\",\n scope=\"scope\",\n)\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.auth.refresh_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n refresh_token=\"refresh_token\",\n scope=\"scope\",\n )\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.auth.refresh_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n refresh_token=\"refresh_token\",\n scope=\"scope\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.auth.refresh_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n refresh_token=\"refresh_token\",\n scope=\"scope\",\n )\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -35,8 +35,8 @@ "identifier_override": "endpoint_nested-no-auth/api.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.nested_no_auth.api.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.nested_no_auth.api.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.nested_no_auth.api.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.nested_no_auth.api.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -48,8 +48,8 @@ "identifier_override": "endpoint_nested/api.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.nested.api.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.nested.api.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.nested.api.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.nested.api.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -61,8 +61,8 @@ "identifier_override": "endpoint_simple.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.simple.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.simple.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } } diff --git a/seed/python-sdk/oauth-client-credentials-custom/src/seed/auth/client.py b/seed/python-sdk/oauth-client-credentials-custom/src/seed/auth/client.py index 9e2c26893519..edf7e6eea038 100644 --- a/seed/python-sdk/oauth-client-credentials-custom/src/seed/auth/client.py +++ b/seed/python-sdk/oauth-client-credentials-custom/src/seed/auth/client.py @@ -62,6 +62,8 @@ def get_token_with_client_credentials( client = SeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.auth.get_token_with_client_credentials( cid="cid", @@ -109,6 +111,8 @@ def refresh_token( client = SeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.auth.refresh_token( client_id="client_id", @@ -180,6 +184,8 @@ async def get_token_with_client_credentials( client = AsyncSeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) @@ -235,6 +241,8 @@ async def refresh_token( client = AsyncSeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-custom/src/seed/client.py b/seed/python-sdk/oauth-client-credentials-custom/src/seed/client.py index 5725dd13344e..1a02e8bef87c 100644 --- a/seed/python-sdk/oauth-client-credentials-custom/src/seed/client.py +++ b/seed/python-sdk/oauth-client-credentials-custom/src/seed/client.py @@ -65,6 +65,8 @@ class SeedOauthClientCredentials: client = SeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... @@ -268,6 +270,8 @@ class AsyncSeedOauthClientCredentials: client = AsyncSeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... diff --git a/seed/python-sdk/oauth-client-credentials-custom/src/seed/nested/api/client.py b/seed/python-sdk/oauth-client-credentials-custom/src/seed/nested/api/client.py index de2f4d540486..e5f06a7c54aa 100644 --- a/seed/python-sdk/oauth-client-credentials-custom/src/seed/nested/api/client.py +++ b/seed/python-sdk/oauth-client-credentials-custom/src/seed/nested/api/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.nested.api.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-custom/src/seed/nested_no_auth/api/client.py b/seed/python-sdk/oauth-client-credentials-custom/src/seed/nested_no_auth/api/client.py index 57ca496ff740..8616bdce5c34 100644 --- a/seed/python-sdk/oauth-client-credentials-custom/src/seed/nested_no_auth/api/client.py +++ b/seed/python-sdk/oauth-client-credentials-custom/src/seed/nested_no_auth/api/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.nested_no_auth.api.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-custom/src/seed/simple/client.py b/seed/python-sdk/oauth-client-credentials-custom/src/seed/simple/client.py index 9652161b4431..5d4c57ef0c07 100644 --- a/seed/python-sdk/oauth-client-credentials-custom/src/seed/simple/client.py +++ b/seed/python-sdk/oauth-client-credentials-custom/src/seed/simple/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.simple.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-default/snippet.json b/seed/python-sdk/oauth-client-credentials-default/snippet.json index 7a46bb0317f5..cc08bf098d78 100644 --- a/seed/python-sdk/oauth-client-credentials-default/snippet.json +++ b/seed/python-sdk/oauth-client-credentials-default/snippet.json @@ -9,8 +9,8 @@ "identifier_override": "endpoint_auth.getToken" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsDefault\n\nclient = SeedOauthClientCredentialsDefault(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n)\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsDefault\n\nclient = AsyncSeedOauthClientCredentialsDefault(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n )\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsDefault\n\nclient = SeedOauthClientCredentialsDefault(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsDefault\n\nclient = AsyncSeedOauthClientCredentialsDefault(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n )\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -22,8 +22,8 @@ "identifier_override": "endpoint_nested-no-auth/api.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsDefault\n\nclient = SeedOauthClientCredentialsDefault(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.nested_no_auth.api.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsDefault\n\nclient = AsyncSeedOauthClientCredentialsDefault(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.nested_no_auth.api.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsDefault\n\nclient = SeedOauthClientCredentialsDefault(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.nested_no_auth.api.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsDefault\n\nclient = AsyncSeedOauthClientCredentialsDefault(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.nested_no_auth.api.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -35,8 +35,8 @@ "identifier_override": "endpoint_nested/api.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsDefault\n\nclient = SeedOauthClientCredentialsDefault(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.nested.api.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsDefault\n\nclient = AsyncSeedOauthClientCredentialsDefault(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.nested.api.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsDefault\n\nclient = SeedOauthClientCredentialsDefault(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.nested.api.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsDefault\n\nclient = AsyncSeedOauthClientCredentialsDefault(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.nested.api.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -48,8 +48,8 @@ "identifier_override": "endpoint_simple.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsDefault\n\nclient = SeedOauthClientCredentialsDefault(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.simple.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsDefault\n\nclient = AsyncSeedOauthClientCredentialsDefault(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsDefault\n\nclient = SeedOauthClientCredentialsDefault(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.simple.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsDefault\n\nclient = AsyncSeedOauthClientCredentialsDefault(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } } diff --git a/seed/python-sdk/oauth-client-credentials-default/src/seed/auth/client.py b/seed/python-sdk/oauth-client-credentials-default/src/seed/auth/client.py index 6701fde0e125..e145d777192a 100644 --- a/seed/python-sdk/oauth-client-credentials-default/src/seed/auth/client.py +++ b/seed/python-sdk/oauth-client-credentials-default/src/seed/auth/client.py @@ -49,6 +49,8 @@ def get_token( client = SeedOauthClientCredentialsDefault( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.auth.get_token( client_id="client_id", @@ -101,6 +103,8 @@ async def get_token( client = AsyncSeedOauthClientCredentialsDefault( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-default/src/seed/client.py b/seed/python-sdk/oauth-client-credentials-default/src/seed/client.py index 231b3ce4e78e..068e38ce4c36 100644 --- a/seed/python-sdk/oauth-client-credentials-default/src/seed/client.py +++ b/seed/python-sdk/oauth-client-credentials-default/src/seed/client.py @@ -65,6 +65,8 @@ class SeedOauthClientCredentialsDefault: client = SeedOauthClientCredentialsDefault( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... @@ -268,6 +270,8 @@ class AsyncSeedOauthClientCredentialsDefault: client = AsyncSeedOauthClientCredentialsDefault( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... diff --git a/seed/python-sdk/oauth-client-credentials-default/src/seed/nested/api/client.py b/seed/python-sdk/oauth-client-credentials-default/src/seed/nested/api/client.py index 790b6856aa99..42ceb878b18a 100644 --- a/seed/python-sdk/oauth-client-credentials-default/src/seed/nested/api/client.py +++ b/seed/python-sdk/oauth-client-credentials-default/src/seed/nested/api/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentialsDefault( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.nested.api.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentialsDefault( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-default/src/seed/nested_no_auth/api/client.py b/seed/python-sdk/oauth-client-credentials-default/src/seed/nested_no_auth/api/client.py index 30b26cb8effc..fa40fdbe1010 100644 --- a/seed/python-sdk/oauth-client-credentials-default/src/seed/nested_no_auth/api/client.py +++ b/seed/python-sdk/oauth-client-credentials-default/src/seed/nested_no_auth/api/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentialsDefault( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.nested_no_auth.api.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentialsDefault( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-default/src/seed/simple/client.py b/seed/python-sdk/oauth-client-credentials-default/src/seed/simple/client.py index ed39fdd3b4d3..6c6f4de72ce8 100644 --- a/seed/python-sdk/oauth-client-credentials-default/src/seed/simple/client.py +++ b/seed/python-sdk/oauth-client-credentials-default/src/seed/simple/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentialsDefault( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.simple.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentialsDefault( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-environment-variables/snippet.json b/seed/python-sdk/oauth-client-credentials-environment-variables/snippet.json index cba6b6c3980e..66687c0fbe54 100644 --- a/seed/python-sdk/oauth-client-credentials-environment-variables/snippet.json +++ b/seed/python-sdk/oauth-client-credentials-environment-variables/snippet.json @@ -9,8 +9,8 @@ "identifier_override": "endpoint_auth.getTokenWithClientCredentials" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsEnvironmentVariables\n\nclient = SeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.auth.get_token_with_client_credentials(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n scope=\"scope\",\n)\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsEnvironmentVariables\n\nclient = AsyncSeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token_with_client_credentials(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n scope=\"scope\",\n )\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsEnvironmentVariables\n\nclient = SeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.auth.get_token_with_client_credentials(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n scope=\"scope\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsEnvironmentVariables\n\nclient = AsyncSeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token_with_client_credentials(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n scope=\"scope\",\n )\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -22,8 +22,8 @@ "identifier_override": "endpoint_auth.refreshToken" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsEnvironmentVariables\n\nclient = SeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.auth.refresh_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n refresh_token=\"refresh_token\",\n scope=\"scope\",\n)\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsEnvironmentVariables\n\nclient = AsyncSeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.auth.refresh_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n refresh_token=\"refresh_token\",\n scope=\"scope\",\n )\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsEnvironmentVariables\n\nclient = SeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.auth.refresh_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n refresh_token=\"refresh_token\",\n scope=\"scope\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsEnvironmentVariables\n\nclient = AsyncSeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.auth.refresh_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n refresh_token=\"refresh_token\",\n scope=\"scope\",\n )\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -35,8 +35,8 @@ "identifier_override": "endpoint_nested-no-auth/api.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsEnvironmentVariables\n\nclient = SeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.nested_no_auth.api.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsEnvironmentVariables\n\nclient = AsyncSeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.nested_no_auth.api.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsEnvironmentVariables\n\nclient = SeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.nested_no_auth.api.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsEnvironmentVariables\n\nclient = AsyncSeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.nested_no_auth.api.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -48,8 +48,8 @@ "identifier_override": "endpoint_nested/api.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsEnvironmentVariables\n\nclient = SeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.nested.api.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsEnvironmentVariables\n\nclient = AsyncSeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.nested.api.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsEnvironmentVariables\n\nclient = SeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.nested.api.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsEnvironmentVariables\n\nclient = AsyncSeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.nested.api.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -61,8 +61,8 @@ "identifier_override": "endpoint_simple.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsEnvironmentVariables\n\nclient = SeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.simple.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsEnvironmentVariables\n\nclient = AsyncSeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsEnvironmentVariables\n\nclient = SeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.simple.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsEnvironmentVariables\n\nclient = AsyncSeedOauthClientCredentialsEnvironmentVariables(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } } diff --git a/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/auth/client.py b/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/auth/client.py index 8f3e95e40ae5..980a2cd0ff13 100644 --- a/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/auth/client.py +++ b/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/auth/client.py @@ -56,6 +56,8 @@ def get_token_with_client_credentials( client = SeedOauthClientCredentialsEnvironmentVariables( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.auth.get_token_with_client_credentials( client_id="client_id", @@ -101,6 +103,8 @@ def refresh_token( client = SeedOauthClientCredentialsEnvironmentVariables( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.auth.refresh_token( client_id="client_id", @@ -166,6 +170,8 @@ async def get_token_with_client_credentials( client = AsyncSeedOauthClientCredentialsEnvironmentVariables( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) @@ -219,6 +225,8 @@ async def refresh_token( client = AsyncSeedOauthClientCredentialsEnvironmentVariables( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/client.py b/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/client.py index e560025cfc8d..441d3aa8810e 100644 --- a/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/client.py +++ b/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/client.py @@ -66,6 +66,8 @@ class SeedOauthClientCredentialsEnvironmentVariables: client = SeedOauthClientCredentialsEnvironmentVariables( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... @@ -269,6 +271,8 @@ class AsyncSeedOauthClientCredentialsEnvironmentVariables: client = AsyncSeedOauthClientCredentialsEnvironmentVariables( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... diff --git a/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/nested/api/client.py b/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/nested/api/client.py index 85a60c1a629b..dbc842e16450 100644 --- a/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/nested/api/client.py +++ b/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/nested/api/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentialsEnvironmentVariables( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.nested.api.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentialsEnvironmentVariables( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/nested_no_auth/api/client.py b/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/nested_no_auth/api/client.py index 5394ee665a8c..e4686790ebf6 100644 --- a/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/nested_no_auth/api/client.py +++ b/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/nested_no_auth/api/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentialsEnvironmentVariables( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.nested_no_auth.api.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentialsEnvironmentVariables( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/simple/client.py b/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/simple/client.py index 41bf6e61222d..aa69c187abcd 100644 --- a/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/simple/client.py +++ b/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/simple/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentialsEnvironmentVariables( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.simple.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentialsEnvironmentVariables( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/snippet.json b/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/snippet.json index 31ca76d6733c..d6acbb2f87ca 100644 --- a/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/snippet.json +++ b/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/snippet.json @@ -9,8 +9,8 @@ "identifier_override": "endpoint_auth.getTokenWithClientCredentials" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsMandatoryAuth\n\nclient = SeedOauthClientCredentialsMandatoryAuth(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.auth.get_token_with_client_credentials(\n client_id=\"my_oauth_app_123\",\n client_secret=\"sk_live_abcdef123456789\",\n scope=\"read:users\",\n)\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsMandatoryAuth\n\nclient = AsyncSeedOauthClientCredentialsMandatoryAuth(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token_with_client_credentials(\n client_id=\"my_oauth_app_123\",\n client_secret=\"sk_live_abcdef123456789\",\n scope=\"read:users\",\n )\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsMandatoryAuth\n\nclient = SeedOauthClientCredentialsMandatoryAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.auth.get_token_with_client_credentials(\n client_id=\"my_oauth_app_123\",\n client_secret=\"sk_live_abcdef123456789\",\n scope=\"read:users\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsMandatoryAuth\n\nclient = AsyncSeedOauthClientCredentialsMandatoryAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token_with_client_credentials(\n client_id=\"my_oauth_app_123\",\n client_secret=\"sk_live_abcdef123456789\",\n scope=\"read:users\",\n )\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -22,8 +22,8 @@ "identifier_override": "endpoint_auth.refreshToken" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsMandatoryAuth\n\nclient = SeedOauthClientCredentialsMandatoryAuth(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.auth.refresh_token(\n client_id=\"my_oauth_app_123\",\n client_secret=\"sk_live_abcdef123456789\",\n refresh_token=\"refresh_token\",\n scope=\"read:users\",\n)\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsMandatoryAuth\n\nclient = AsyncSeedOauthClientCredentialsMandatoryAuth(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.auth.refresh_token(\n client_id=\"my_oauth_app_123\",\n client_secret=\"sk_live_abcdef123456789\",\n refresh_token=\"refresh_token\",\n scope=\"read:users\",\n )\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsMandatoryAuth\n\nclient = SeedOauthClientCredentialsMandatoryAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.auth.refresh_token(\n client_id=\"my_oauth_app_123\",\n client_secret=\"sk_live_abcdef123456789\",\n refresh_token=\"refresh_token\",\n scope=\"read:users\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsMandatoryAuth\n\nclient = AsyncSeedOauthClientCredentialsMandatoryAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.auth.refresh_token(\n client_id=\"my_oauth_app_123\",\n client_secret=\"sk_live_abcdef123456789\",\n refresh_token=\"refresh_token\",\n scope=\"read:users\",\n )\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -35,8 +35,8 @@ "identifier_override": "endpoint_nested/api.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsMandatoryAuth\n\nclient = SeedOauthClientCredentialsMandatoryAuth(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.nested.api.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsMandatoryAuth\n\nclient = AsyncSeedOauthClientCredentialsMandatoryAuth(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.nested.api.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsMandatoryAuth\n\nclient = SeedOauthClientCredentialsMandatoryAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.nested.api.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsMandatoryAuth\n\nclient = AsyncSeedOauthClientCredentialsMandatoryAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.nested.api.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -48,8 +48,8 @@ "identifier_override": "endpoint_simple.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsMandatoryAuth\n\nclient = SeedOauthClientCredentialsMandatoryAuth(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.simple.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsMandatoryAuth\n\nclient = AsyncSeedOauthClientCredentialsMandatoryAuth(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsMandatoryAuth\n\nclient = SeedOauthClientCredentialsMandatoryAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.simple.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsMandatoryAuth\n\nclient = AsyncSeedOauthClientCredentialsMandatoryAuth(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } } diff --git a/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/auth/client.py b/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/auth/client.py index 5865fce6910e..cb1e8ccba91e 100644 --- a/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/auth/client.py +++ b/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/auth/client.py @@ -56,6 +56,8 @@ def get_token_with_client_credentials( client = SeedOauthClientCredentialsMandatoryAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.auth.get_token_with_client_credentials( client_id="my_oauth_app_123", @@ -101,6 +103,8 @@ def refresh_token( client = SeedOauthClientCredentialsMandatoryAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.auth.refresh_token( client_id="my_oauth_app_123", @@ -166,6 +170,8 @@ async def get_token_with_client_credentials( client = AsyncSeedOauthClientCredentialsMandatoryAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) @@ -219,6 +225,8 @@ async def refresh_token( client = AsyncSeedOauthClientCredentialsMandatoryAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/client.py b/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/client.py index cdd3356b174d..54020c80e415 100644 --- a/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/client.py +++ b/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/client.py @@ -64,6 +64,8 @@ class SeedOauthClientCredentialsMandatoryAuth: client = SeedOauthClientCredentialsMandatoryAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... @@ -258,6 +260,8 @@ class AsyncSeedOauthClientCredentialsMandatoryAuth: client = AsyncSeedOauthClientCredentialsMandatoryAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... diff --git a/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/nested/api/client.py b/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/nested/api/client.py index a772980b6504..5ee0e6d00a0b 100644 --- a/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/nested/api/client.py +++ b/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/nested/api/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentialsMandatoryAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.nested.api.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentialsMandatoryAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/simple/client.py b/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/simple/client.py index cd988beb30da..c1fbc5b02e36 100644 --- a/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/simple/client.py +++ b/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/simple/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentialsMandatoryAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.simple.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentialsMandatoryAuth( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-nested-root/snippet.json b/seed/python-sdk/oauth-client-credentials-nested-root/snippet.json index ff2715bfed8e..3a38850449bc 100644 --- a/seed/python-sdk/oauth-client-credentials-nested-root/snippet.json +++ b/seed/python-sdk/oauth-client-credentials-nested-root/snippet.json @@ -9,8 +9,8 @@ "identifier_override": "endpoint_auth.getToken" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n scope=\"scope\",\n)\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n scope=\"scope\",\n )\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n scope=\"scope\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n scope=\"scope\",\n )\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -22,8 +22,8 @@ "identifier_override": "endpoint_nested-no-auth/api.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.nested_no_auth.api.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.nested_no_auth.api.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.nested_no_auth.api.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.nested_no_auth.api.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -35,8 +35,8 @@ "identifier_override": "endpoint_nested/api.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.nested.api.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.nested.api.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.nested.api.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.nested.api.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -48,8 +48,8 @@ "identifier_override": "endpoint_simple.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.simple.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.simple.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } } diff --git a/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/auth/client.py b/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/auth/client.py index 3495d4e02807..45fbaef5f488 100644 --- a/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/auth/client.py +++ b/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/auth/client.py @@ -56,6 +56,8 @@ def get_token( client = SeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.auth.get_token( client_id="client_id", @@ -116,6 +118,8 @@ async def get_token( client = AsyncSeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/client.py b/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/client.py index 5725dd13344e..1a02e8bef87c 100644 --- a/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/client.py +++ b/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/client.py @@ -65,6 +65,8 @@ class SeedOauthClientCredentials: client = SeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... @@ -268,6 +270,8 @@ class AsyncSeedOauthClientCredentials: client = AsyncSeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... diff --git a/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/nested/api/client.py b/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/nested/api/client.py index de2f4d540486..e5f06a7c54aa 100644 --- a/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/nested/api/client.py +++ b/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/nested/api/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.nested.api.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/nested_no_auth/api/client.py b/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/nested_no_auth/api/client.py index 57ca496ff740..8616bdce5c34 100644 --- a/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/nested_no_auth/api/client.py +++ b/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/nested_no_auth/api/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.nested_no_auth.api.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/simple/client.py b/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/simple/client.py index 9652161b4431..5d4c57ef0c07 100644 --- a/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/simple/client.py +++ b/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/simple/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.simple.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-openapi/snippet.json b/seed/python-sdk/oauth-client-credentials-openapi/snippet.json index 11b9eff2e822..61dd488a8bb6 100644 --- a/seed/python-sdk/oauth-client-credentials-openapi/snippet.json +++ b/seed/python-sdk/oauth-client-credentials-openapi/snippet.json @@ -9,8 +9,8 @@ "identifier_override": "endpoint_identity.getToken" }, "snippet": { - "sync_client": "from seed import SeedApi\n\nclient = SeedApi(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.identity.get_token(\n username=\"username\",\n password=\"password\",\n)\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedApi\n\nclient = AsyncSeedApi(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.identity.get_token(\n username=\"username\",\n password=\"password\",\n )\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedApi\n\nclient = SeedApi(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.identity.get_token(\n username=\"username\",\n password=\"password\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedApi\n\nclient = AsyncSeedApi(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.identity.get_token(\n username=\"username\",\n password=\"password\",\n )\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -22,8 +22,8 @@ "identifier_override": "endpoint_plants.list" }, "snippet": { - "sync_client": "from seed import SeedApi\n\nclient = SeedApi(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.plants.list()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedApi\n\nclient = AsyncSeedApi(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.plants.list()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedApi\n\nclient = SeedApi(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.plants.list()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedApi\n\nclient = AsyncSeedApi(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.plants.list()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -35,8 +35,8 @@ "identifier_override": "endpoint_plants.get" }, "snippet": { - "sync_client": "from seed import SeedApi\n\nclient = SeedApi(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.plants.get(\n plant_id=\"plantId\",\n)\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedApi\n\nclient = AsyncSeedApi(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.plants.get(\n plant_id=\"plantId\",\n )\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedApi\n\nclient = SeedApi(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.plants.get(\n plant_id=\"plantId\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedApi\n\nclient = AsyncSeedApi(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.plants.get(\n plant_id=\"plantId\",\n )\n\n\nasyncio.run(main())\n", "type": "python" } } diff --git a/seed/python-sdk/oauth-client-credentials-openapi/src/seed/client.py b/seed/python-sdk/oauth-client-credentials-openapi/src/seed/client.py index 9eaa74749f05..99b90adea5bf 100644 --- a/seed/python-sdk/oauth-client-credentials-openapi/src/seed/client.py +++ b/seed/python-sdk/oauth-client-credentials-openapi/src/seed/client.py @@ -63,6 +63,8 @@ class SeedApi: client = SeedApi( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... @@ -248,6 +250,8 @@ class AsyncSeedApi: client = AsyncSeedApi( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... diff --git a/seed/python-sdk/oauth-client-credentials-openapi/src/seed/identity/client.py b/seed/python-sdk/oauth-client-credentials-openapi/src/seed/identity/client.py index 2a6e293fa695..4c28f782f0ce 100644 --- a/seed/python-sdk/oauth-client-credentials-openapi/src/seed/identity/client.py +++ b/seed/python-sdk/oauth-client-credentials-openapi/src/seed/identity/client.py @@ -50,6 +50,8 @@ def get_token( client = SeedApi( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.identity.get_token( username="username", @@ -101,6 +103,8 @@ async def get_token( client = AsyncSeedApi( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-openapi/src/seed/plants/client.py b/seed/python-sdk/oauth-client-credentials-openapi/src/seed/plants/client.py index ff85ebdb0ce2..6efe32254c31 100644 --- a/seed/python-sdk/oauth-client-credentials-openapi/src/seed/plants/client.py +++ b/seed/python-sdk/oauth-client-credentials-openapi/src/seed/plants/client.py @@ -41,6 +41,8 @@ def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> ty client = SeedApi( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.plants.list() """ @@ -67,6 +69,8 @@ def get(self, plant_id: str, *, request_options: typing.Optional[RequestOptions] client = SeedApi( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.plants.get( plant_id="plantId", @@ -111,6 +115,8 @@ async def list(self, *, request_options: typing.Optional[RequestOptions] = None) client = AsyncSeedApi( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) @@ -145,6 +151,8 @@ async def get(self, plant_id: str, *, request_options: typing.Optional[RequestOp client = AsyncSeedApi( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-reference/snippet.json b/seed/python-sdk/oauth-client-credentials-reference/snippet.json index d3f2d36d84bc..91e5b3dda376 100644 --- a/seed/python-sdk/oauth-client-credentials-reference/snippet.json +++ b/seed/python-sdk/oauth-client-credentials-reference/snippet.json @@ -9,8 +9,8 @@ "identifier_override": "endpoint_auth.getToken" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsReference\n\nclient = SeedOauthClientCredentialsReference(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n)\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsReference\n\nclient = AsyncSeedOauthClientCredentialsReference(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n )\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsReference\n\nclient = SeedOauthClientCredentialsReference(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsReference\n\nclient = AsyncSeedOauthClientCredentialsReference(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n )\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -22,8 +22,8 @@ "identifier_override": "endpoint_simple.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsReference\n\nclient = SeedOauthClientCredentialsReference(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.simple.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsReference\n\nclient = AsyncSeedOauthClientCredentialsReference(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsReference\n\nclient = SeedOauthClientCredentialsReference(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.simple.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsReference\n\nclient = AsyncSeedOauthClientCredentialsReference(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } } diff --git a/seed/python-sdk/oauth-client-credentials-reference/src/seed/auth/client.py b/seed/python-sdk/oauth-client-credentials-reference/src/seed/auth/client.py index 86b18bddcd8c..9988897eacbd 100644 --- a/seed/python-sdk/oauth-client-credentials-reference/src/seed/auth/client.py +++ b/seed/python-sdk/oauth-client-credentials-reference/src/seed/auth/client.py @@ -49,6 +49,8 @@ def get_token( client = SeedOauthClientCredentialsReference( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.auth.get_token( client_id="client_id", @@ -101,6 +103,8 @@ async def get_token( client = AsyncSeedOauthClientCredentialsReference( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-reference/src/seed/client.py b/seed/python-sdk/oauth-client-credentials-reference/src/seed/client.py index 54be94a338ea..629f9ff6764b 100644 --- a/seed/python-sdk/oauth-client-credentials-reference/src/seed/client.py +++ b/seed/python-sdk/oauth-client-credentials-reference/src/seed/client.py @@ -63,6 +63,8 @@ class SeedOauthClientCredentialsReference: client = SeedOauthClientCredentialsReference( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... @@ -248,6 +250,8 @@ class AsyncSeedOauthClientCredentialsReference: client = AsyncSeedOauthClientCredentialsReference( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... diff --git a/seed/python-sdk/oauth-client-credentials-reference/src/seed/simple/client.py b/seed/python-sdk/oauth-client-credentials-reference/src/seed/simple/client.py index cc82bea006b1..649044a94148 100644 --- a/seed/python-sdk/oauth-client-credentials-reference/src/seed/simple/client.py +++ b/seed/python-sdk/oauth-client-credentials-reference/src/seed/simple/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentialsReference( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.simple.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentialsReference( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-with-variables/snippet.json b/seed/python-sdk/oauth-client-credentials-with-variables/snippet.json index 7f38981359aa..3451d4660846 100644 --- a/seed/python-sdk/oauth-client-credentials-with-variables/snippet.json +++ b/seed/python-sdk/oauth-client-credentials-with-variables/snippet.json @@ -9,8 +9,8 @@ "identifier_override": "endpoint_auth.getTokenWithClientCredentials" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsWithVariables\n\nclient = SeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n)\nclient.auth.get_token_with_client_credentials(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n scope=\"scope\",\n)\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsWithVariables\n\nclient = AsyncSeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token_with_client_credentials(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n scope=\"scope\",\n )\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsWithVariables\n\nclient = SeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.auth.get_token_with_client_credentials(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n scope=\"scope\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsWithVariables\n\nclient = AsyncSeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token_with_client_credentials(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n scope=\"scope\",\n )\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -22,8 +22,8 @@ "identifier_override": "endpoint_auth.refreshToken" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsWithVariables\n\nclient = SeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n)\nclient.auth.refresh_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n refresh_token=\"refresh_token\",\n scope=\"scope\",\n)\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsWithVariables\n\nclient = AsyncSeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n)\n\n\nasync def main() -> None:\n await client.auth.refresh_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n refresh_token=\"refresh_token\",\n scope=\"scope\",\n )\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsWithVariables\n\nclient = SeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.auth.refresh_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n refresh_token=\"refresh_token\",\n scope=\"scope\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsWithVariables\n\nclient = AsyncSeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.auth.refresh_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n refresh_token=\"refresh_token\",\n scope=\"scope\",\n )\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -35,8 +35,8 @@ "identifier_override": "endpoint_nested-no-auth/api.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsWithVariables\n\nclient = SeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n)\nclient.nested_no_auth.api.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsWithVariables\n\nclient = AsyncSeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n)\n\n\nasync def main() -> None:\n await client.nested_no_auth.api.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsWithVariables\n\nclient = SeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.nested_no_auth.api.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsWithVariables\n\nclient = AsyncSeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.nested_no_auth.api.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -48,8 +48,8 @@ "identifier_override": "endpoint_nested/api.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsWithVariables\n\nclient = SeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n)\nclient.nested.api.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsWithVariables\n\nclient = AsyncSeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n)\n\n\nasync def main() -> None:\n await client.nested.api.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsWithVariables\n\nclient = SeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.nested.api.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsWithVariables\n\nclient = AsyncSeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.nested.api.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -61,8 +61,8 @@ "identifier_override": "endpoint_service.post" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsWithVariables\n\nclient = SeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n)\nclient.service.post()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsWithVariables\n\nclient = AsyncSeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n)\n\n\nasync def main() -> None:\n await client.service.post()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsWithVariables\n\nclient = SeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.service.post()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsWithVariables\n\nclient = AsyncSeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.service.post()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -74,8 +74,8 @@ "identifier_override": "endpoint_simple.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentialsWithVariables\n\nclient = SeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n)\nclient.simple.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsWithVariables\n\nclient = AsyncSeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentialsWithVariables\n\nclient = SeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.simple.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentialsWithVariables\n\nclient = AsyncSeedOauthClientCredentialsWithVariables(\n base_url=\"YOUR_BASE_URL\",\n root_variable=\"YOUR_ROOT_VARIABLE\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } } diff --git a/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/auth/client.py b/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/auth/client.py index bb5b27d838c3..d0bd3b6eb231 100644 --- a/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/auth/client.py +++ b/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/auth/client.py @@ -57,6 +57,8 @@ def get_token_with_client_credentials( client = SeedOauthClientCredentialsWithVariables( base_url="YOUR_BASE_URL", root_variable="YOUR_ROOT_VARIABLE", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.auth.get_token_with_client_credentials( client_id="client_id", @@ -103,6 +105,8 @@ def refresh_token( client = SeedOauthClientCredentialsWithVariables( base_url="YOUR_BASE_URL", root_variable="YOUR_ROOT_VARIABLE", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.auth.refresh_token( client_id="client_id", @@ -169,6 +173,8 @@ async def get_token_with_client_credentials( client = AsyncSeedOauthClientCredentialsWithVariables( base_url="YOUR_BASE_URL", root_variable="YOUR_ROOT_VARIABLE", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) @@ -223,6 +229,8 @@ async def refresh_token( client = AsyncSeedOauthClientCredentialsWithVariables( base_url="YOUR_BASE_URL", root_variable="YOUR_ROOT_VARIABLE", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/client.py b/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/client.py index 53ccf0bcb1b5..067016b41959 100644 --- a/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/client.py +++ b/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/client.py @@ -67,6 +67,8 @@ class SeedOauthClientCredentialsWithVariables: client = SeedOauthClientCredentialsWithVariables( base_url="YOUR_BASE_URL", root_variable="YOUR_ROOT_VARIABLE", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... @@ -286,6 +288,8 @@ class AsyncSeedOauthClientCredentialsWithVariables: client = AsyncSeedOauthClientCredentialsWithVariables( base_url="YOUR_BASE_URL", root_variable="YOUR_ROOT_VARIABLE", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... diff --git a/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/nested/api/client.py b/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/nested/api/client.py index 3c7240cf195e..4712f5044f21 100644 --- a/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/nested/api/client.py +++ b/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/nested/api/client.py @@ -40,6 +40,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentialsWithVariables( base_url="YOUR_BASE_URL", root_variable="YOUR_ROOT_VARIABLE", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.nested.api.get_something() """ @@ -82,6 +84,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentialsWithVariables( base_url="YOUR_BASE_URL", root_variable="YOUR_ROOT_VARIABLE", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/nested_no_auth/api/client.py b/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/nested_no_auth/api/client.py index 6b7528d4d690..369aff008f81 100644 --- a/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/nested_no_auth/api/client.py +++ b/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/nested_no_auth/api/client.py @@ -40,6 +40,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentialsWithVariables( base_url="YOUR_BASE_URL", root_variable="YOUR_ROOT_VARIABLE", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.nested_no_auth.api.get_something() """ @@ -82,6 +84,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentialsWithVariables( base_url="YOUR_BASE_URL", root_variable="YOUR_ROOT_VARIABLE", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/service/client.py b/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/service/client.py index 915061b41caa..d8b27f483901 100644 --- a/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/service/client.py +++ b/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/service/client.py @@ -40,6 +40,8 @@ def post(self, *, request_options: typing.Optional[RequestOptions] = None) -> No client = SeedOauthClientCredentialsWithVariables( base_url="YOUR_BASE_URL", root_variable="YOUR_ROOT_VARIABLE", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.service.post() """ @@ -82,6 +84,8 @@ async def post(self, *, request_options: typing.Optional[RequestOptions] = None) client = AsyncSeedOauthClientCredentialsWithVariables( base_url="YOUR_BASE_URL", root_variable="YOUR_ROOT_VARIABLE", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/simple/client.py b/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/simple/client.py index a35f47aa8ce3..d0bf13abd207 100644 --- a/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/simple/client.py +++ b/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/simple/client.py @@ -40,6 +40,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentialsWithVariables( base_url="YOUR_BASE_URL", root_variable="YOUR_ROOT_VARIABLE", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.simple.get_something() """ @@ -82,6 +84,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentialsWithVariables( base_url="YOUR_BASE_URL", root_variable="YOUR_ROOT_VARIABLE", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials/snippet.json b/seed/python-sdk/oauth-client-credentials/snippet.json index fb38130be8e4..0ea8fa8ac90c 100644 --- a/seed/python-sdk/oauth-client-credentials/snippet.json +++ b/seed/python-sdk/oauth-client-credentials/snippet.json @@ -9,8 +9,8 @@ "identifier_override": "endpoint_auth.getTokenWithClientCredentials" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.auth.get_token_with_client_credentials(\n client_id=\"my_oauth_app_123\",\n client_secret=\"sk_live_abcdef123456789\",\n scope=\"read:users\",\n)\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token_with_client_credentials(\n client_id=\"my_oauth_app_123\",\n client_secret=\"sk_live_abcdef123456789\",\n scope=\"read:users\",\n )\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.auth.get_token_with_client_credentials(\n client_id=\"my_oauth_app_123\",\n client_secret=\"sk_live_abcdef123456789\",\n scope=\"read:users\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token_with_client_credentials(\n client_id=\"my_oauth_app_123\",\n client_secret=\"sk_live_abcdef123456789\",\n scope=\"read:users\",\n )\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -22,8 +22,8 @@ "identifier_override": "endpoint_auth.refreshToken" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.auth.refresh_token(\n client_id=\"my_oauth_app_123\",\n client_secret=\"sk_live_abcdef123456789\",\n refresh_token=\"refresh_token\",\n scope=\"read:users\",\n)\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.auth.refresh_token(\n client_id=\"my_oauth_app_123\",\n client_secret=\"sk_live_abcdef123456789\",\n refresh_token=\"refresh_token\",\n scope=\"read:users\",\n )\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.auth.refresh_token(\n client_id=\"my_oauth_app_123\",\n client_secret=\"sk_live_abcdef123456789\",\n refresh_token=\"refresh_token\",\n scope=\"read:users\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.auth.refresh_token(\n client_id=\"my_oauth_app_123\",\n client_secret=\"sk_live_abcdef123456789\",\n refresh_token=\"refresh_token\",\n scope=\"read:users\",\n )\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -35,8 +35,8 @@ "identifier_override": "endpoint_nested-no-auth/api.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.nested_no_auth.api.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.nested_no_auth.api.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.nested_no_auth.api.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.nested_no_auth.api.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -48,8 +48,8 @@ "identifier_override": "endpoint_nested/api.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.nested.api.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.nested.api.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.nested.api.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.nested.api.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } }, @@ -61,8 +61,8 @@ "identifier_override": "endpoint_simple.getSomething" }, "snippet": { - "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\nclient.simple.get_something()\n", - "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", + "sync_client": "from seed import SeedOauthClientCredentials\n\nclient = SeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\nclient.simple.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedOauthClientCredentials\n\nclient = AsyncSeedOauthClientCredentials(\n base_url=\"YOUR_BASE_URL\",\n client_id=\"YOUR_CLIENT_ID\",\n client_secret=\"YOUR_CLIENT_SECRET\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", "type": "python" } } diff --git a/seed/python-sdk/oauth-client-credentials/src/seed/auth/client.py b/seed/python-sdk/oauth-client-credentials/src/seed/auth/client.py index a3c3e6ce3051..612e9ee56353 100644 --- a/seed/python-sdk/oauth-client-credentials/src/seed/auth/client.py +++ b/seed/python-sdk/oauth-client-credentials/src/seed/auth/client.py @@ -56,6 +56,8 @@ def get_token_with_client_credentials( client = SeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.auth.get_token_with_client_credentials( client_id="my_oauth_app_123", @@ -101,6 +103,8 @@ def refresh_token( client = SeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.auth.refresh_token( client_id="my_oauth_app_123", @@ -166,6 +170,8 @@ async def get_token_with_client_credentials( client = AsyncSeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) @@ -219,6 +225,8 @@ async def refresh_token( client = AsyncSeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials/src/seed/client.py b/seed/python-sdk/oauth-client-credentials/src/seed/client.py index 5725dd13344e..1a02e8bef87c 100644 --- a/seed/python-sdk/oauth-client-credentials/src/seed/client.py +++ b/seed/python-sdk/oauth-client-credentials/src/seed/client.py @@ -65,6 +65,8 @@ class SeedOauthClientCredentials: client = SeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... @@ -268,6 +270,8 @@ class AsyncSeedOauthClientCredentials: client = AsyncSeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) # or ... diff --git a/seed/python-sdk/oauth-client-credentials/src/seed/nested/api/client.py b/seed/python-sdk/oauth-client-credentials/src/seed/nested/api/client.py index de2f4d540486..e5f06a7c54aa 100644 --- a/seed/python-sdk/oauth-client-credentials/src/seed/nested/api/client.py +++ b/seed/python-sdk/oauth-client-credentials/src/seed/nested/api/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.nested.api.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials/src/seed/nested_no_auth/api/client.py b/seed/python-sdk/oauth-client-credentials/src/seed/nested_no_auth/api/client.py index 57ca496ff740..8616bdce5c34 100644 --- a/seed/python-sdk/oauth-client-credentials/src/seed/nested_no_auth/api/client.py +++ b/seed/python-sdk/oauth-client-credentials/src/seed/nested_no_auth/api/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.nested_no_auth.api.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/oauth-client-credentials/src/seed/simple/client.py b/seed/python-sdk/oauth-client-credentials/src/seed/simple/client.py index 9652161b4431..5d4c57ef0c07 100644 --- a/seed/python-sdk/oauth-client-credentials/src/seed/simple/client.py +++ b/seed/python-sdk/oauth-client-credentials/src/seed/simple/client.py @@ -39,6 +39,8 @@ def get_something(self, *, request_options: typing.Optional[RequestOptions] = No client = SeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) client.simple.get_something() """ @@ -80,6 +82,8 @@ async def get_something(self, *, request_options: typing.Optional[RequestOptions client = AsyncSeedOauthClientCredentials( base_url="YOUR_BASE_URL", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", ) diff --git a/seed/python-sdk/trace/README.md b/seed/python-sdk/trace/README.md index fd815eadd39c..d95508ab4b0c 100644 --- a/seed/python-sdk/trace/README.md +++ b/seed/python-sdk/trace/README.md @@ -37,6 +37,7 @@ Instantiate and use the client with the following: ```python from seed import SeedTrace import uuid +from seed.submission import TestSubmissionStatus_Stopped client = SeedTrace( token="", @@ -44,9 +45,7 @@ client = SeedTrace( client.admin.update_test_submission_status( submission_id=uuid.UUID("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"), - request={ - "type": "stopped" - }, + request=TestSubmissionStatus_Stopped(), ) ``` @@ -70,6 +69,7 @@ The SDK also exports an `async` client so that you can make non-blocking calls t ```python import asyncio import uuid +from seed.submission import TestSubmissionStatus_Stopped from seed import AsyncSeedTrace @@ -81,9 +81,7 @@ client = AsyncSeedTrace( async def main() -> None: await client.admin.update_test_submission_status( submission_id=uuid.UUID("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"), - request={ - "type": "stopped" - }, + request=TestSubmissionStatus_Stopped(), ) diff --git a/seed/python-sdk/trace/reference.md b/seed/python-sdk/trace/reference.md index 71da395f3275..ca2f25be2979 100644 --- a/seed/python-sdk/trace/reference.md +++ b/seed/python-sdk/trace/reference.md @@ -66,6 +66,7 @@ client.v_2.test() from seed import SeedTrace from seed.environment import SeedTraceEnvironment import uuid +from seed.submission import TestSubmissionStatus_Stopped client = SeedTrace( token="", @@ -74,9 +75,7 @@ client = SeedTrace( client.admin.update_test_submission_status( submission_id=uuid.UUID("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"), - request={ - "type": "stopped" - }, + request=TestSubmissionStatus_Stopped(), ) ``` @@ -138,6 +137,7 @@ from seed import SeedTrace from seed.environment import SeedTraceEnvironment import uuid import datetime +from seed.submission import TestSubmissionUpdateInfo_Running client = SeedTrace( token="", @@ -147,9 +147,9 @@ client = SeedTrace( client.admin.send_test_submission_update( submission_id=uuid.UUID("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"), update_time=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - update_info={ - "type": "running" - }, + update_info=TestSubmissionUpdateInfo_Running( + running=, + ), ) ``` @@ -210,6 +210,7 @@ client.admin.send_test_submission_update( from seed import SeedTrace from seed.environment import SeedTraceEnvironment import uuid +from seed.submission import WorkspaceSubmissionStatus_Stopped client = SeedTrace( token="", @@ -218,9 +219,7 @@ client = SeedTrace( client.admin.update_workspace_submission_status( submission_id=uuid.UUID("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"), - request={ - "type": "stopped" - }, + request=WorkspaceSubmissionStatus_Stopped(), ) ``` @@ -282,6 +281,7 @@ from seed import SeedTrace from seed.environment import SeedTraceEnvironment import uuid import datetime +from seed.submission import WorkspaceSubmissionUpdateInfo_Running client = SeedTrace( token="", @@ -291,9 +291,9 @@ client = SeedTrace( client.admin.send_workspace_submission_update( submission_id=uuid.UUID("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"), update_time=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - update_info={ - "type": "running" - }, + update_info=WorkspaceSubmissionUpdateInfo_Running( + running=, + ), ) ``` @@ -354,7 +354,8 @@ client.admin.send_workspace_submission_update( from seed import SeedTrace from seed.environment import SeedTraceEnvironment import uuid -from seed.submission import TestCaseResultWithStdout, TestCaseResult, TraceResponse, ExpressionLocation, StackInformation, StackFrame, Scope +from seed.submission import TestCaseResultWithStdout, TestCaseResult, ActualResult_Value, TraceResponse, ExpressionLocation, StackInformation, StackFrame, Scope +from seed.commons import VariableValue_IntegerValue, DebugVariableValue_IntegerValue client = SeedTrace( token="", @@ -366,15 +367,14 @@ client.admin.store_traced_test_case( test_case_id="testCaseId", result=TestCaseResultWithStdout( result=TestCaseResult( - expected_result={ - "type": "integerValue" - }, - actual_result={ - "type": "value", - "value": { - "type": "integerValue" - } - }, + expected_result=VariableValue_IntegerValue( + integer_value=, + ), + actual_result=ActualResult_Value( + value=VariableValue_IntegerValue( + integer_value=, + ), + ), passed=True, ), stdout="stdout", @@ -383,9 +383,9 @@ client.admin.store_traced_test_case( TraceResponse( submission_id=uuid.UUID("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"), line_number=1, - return_value={ - "type": "integerValue" - }, + return_value=DebugVariableValue_IntegerValue( + integer_value=, + ), expression_location=ExpressionLocation( start=1, offset=1, @@ -398,16 +398,16 @@ client.admin.store_traced_test_case( scopes=[ Scope( variables={ - "variables": { - "type": "integerValue" - } + "variables": DebugVariableValue_IntegerValue( + integer_value=, + ) }, ), Scope( variables={ - "variables": { - "type": "integerValue" - } + "variables": DebugVariableValue_IntegerValue( + integer_value=, + ) }, ) ], @@ -418,9 +418,9 @@ client.admin.store_traced_test_case( TraceResponse( submission_id=uuid.UUID("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"), line_number=1, - return_value={ - "type": "integerValue" - }, + return_value=DebugVariableValue_IntegerValue( + integer_value=, + ), expression_location=ExpressionLocation( start=1, offset=1, @@ -433,16 +433,16 @@ client.admin.store_traced_test_case( scopes=[ Scope( variables={ - "variables": { - "type": "integerValue" - } + "variables": DebugVariableValue_IntegerValue( + integer_value=, + ) }, ), Scope( variables={ - "variables": { - "type": "integerValue" - } + "variables": DebugVariableValue_IntegerValue( + integer_value=, + ) }, ) ], @@ -528,6 +528,7 @@ from seed import SeedTrace from seed.environment import SeedTraceEnvironment import uuid from seed.submission import TraceResponseV2, TracedFile, ExpressionLocation, StackInformation, StackFrame, Scope +from seed.commons import DebugVariableValue_IntegerValue client = SeedTrace( token="", @@ -545,9 +546,9 @@ client.admin.store_traced_test_case_v_2( filename="filename", directory="directory", ), - return_value={ - "type": "integerValue" - }, + return_value=DebugVariableValue_IntegerValue( + integer_value=, + ), expression_location=ExpressionLocation( start=1, offset=1, @@ -560,16 +561,16 @@ client.admin.store_traced_test_case_v_2( scopes=[ Scope( variables={ - "variables": { - "type": "integerValue" - } + "variables": DebugVariableValue_IntegerValue( + integer_value=, + ) }, ), Scope( variables={ - "variables": { - "type": "integerValue" - } + "variables": DebugVariableValue_IntegerValue( + integer_value=, + ) }, ) ], @@ -584,9 +585,9 @@ client.admin.store_traced_test_case_v_2( filename="filename", directory="directory", ), - return_value={ - "type": "integerValue" - }, + return_value=DebugVariableValue_IntegerValue( + integer_value=, + ), expression_location=ExpressionLocation( start=1, offset=1, @@ -599,16 +600,16 @@ client.admin.store_traced_test_case_v_2( scopes=[ Scope( variables={ - "variables": { - "type": "integerValue" - } + "variables": DebugVariableValue_IntegerValue( + integer_value=, + ) }, ), Scope( variables={ - "variables": { - "type": "integerValue" - } + "variables": DebugVariableValue_IntegerValue( + integer_value=, + ) }, ) ], @@ -685,7 +686,8 @@ client.admin.store_traced_test_case_v_2( from seed import SeedTrace from seed.environment import SeedTraceEnvironment import uuid -from seed.submission import WorkspaceRunDetails, ExceptionInfo, TraceResponse, ExpressionLocation, StackInformation, StackFrame, Scope +from seed.submission import WorkspaceRunDetails, ExceptionV2_Generic, ExceptionInfo, TraceResponse, ExpressionLocation, StackInformation, StackFrame, Scope +from seed.commons import DebugVariableValue_IntegerValue client = SeedTrace( token="", @@ -695,12 +697,11 @@ client = SeedTrace( client.admin.store_traced_workspace( submission_id=uuid.UUID("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"), workspace_run_details=WorkspaceRunDetails( - exception_v_2={ - "type": "generic", - "exception_type": "exceptionType", - "exception_message": "exceptionMessage", - "exception_stacktrace": "exceptionStacktrace" - }, + exception_v_2=ExceptionV2_Generic( + exception_type="exceptionType", + exception_message="exceptionMessage", + exception_stacktrace="exceptionStacktrace", + ), exception=ExceptionInfo( exception_type="exceptionType", exception_message="exceptionMessage", @@ -712,9 +713,9 @@ client.admin.store_traced_workspace( TraceResponse( submission_id=uuid.UUID("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"), line_number=1, - return_value={ - "type": "integerValue" - }, + return_value=DebugVariableValue_IntegerValue( + integer_value=, + ), expression_location=ExpressionLocation( start=1, offset=1, @@ -727,16 +728,16 @@ client.admin.store_traced_workspace( scopes=[ Scope( variables={ - "variables": { - "type": "integerValue" - } + "variables": DebugVariableValue_IntegerValue( + integer_value=, + ) }, ), Scope( variables={ - "variables": { - "type": "integerValue" - } + "variables": DebugVariableValue_IntegerValue( + integer_value=, + ) }, ) ], @@ -747,9 +748,9 @@ client.admin.store_traced_workspace( TraceResponse( submission_id=uuid.UUID("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"), line_number=1, - return_value={ - "type": "integerValue" - }, + return_value=DebugVariableValue_IntegerValue( + integer_value=, + ), expression_location=ExpressionLocation( start=1, offset=1, @@ -762,16 +763,16 @@ client.admin.store_traced_workspace( scopes=[ Scope( variables={ - "variables": { - "type": "integerValue" - } + "variables": DebugVariableValue_IntegerValue( + integer_value=, + ) }, ), Scope( variables={ - "variables": { - "type": "integerValue" - } + "variables": DebugVariableValue_IntegerValue( + integer_value=, + ) }, ) ], @@ -849,6 +850,7 @@ from seed import SeedTrace from seed.environment import SeedTraceEnvironment import uuid from seed.submission import TraceResponseV2, TracedFile, ExpressionLocation, StackInformation, StackFrame, Scope +from seed.commons import DebugVariableValue_IntegerValue client = SeedTrace( token="", @@ -865,9 +867,9 @@ client.admin.store_traced_workspace_v_2( filename="filename", directory="directory", ), - return_value={ - "type": "integerValue" - }, + return_value=DebugVariableValue_IntegerValue( + integer_value=, + ), expression_location=ExpressionLocation( start=1, offset=1, @@ -880,16 +882,16 @@ client.admin.store_traced_workspace_v_2( scopes=[ Scope( variables={ - "variables": { - "type": "integerValue" - } + "variables": DebugVariableValue_IntegerValue( + integer_value=, + ) }, ), Scope( variables={ - "variables": { - "type": "integerValue" - } + "variables": DebugVariableValue_IntegerValue( + integer_value=, + ) }, ) ], @@ -904,9 +906,9 @@ client.admin.store_traced_workspace_v_2( filename="filename", directory="directory", ), - return_value={ - "type": "integerValue" - }, + return_value=DebugVariableValue_IntegerValue( + integer_value=, + ), expression_location=ExpressionLocation( start=1, offset=1, @@ -919,16 +921,16 @@ client.admin.store_traced_workspace_v_2( scopes=[ Scope( variables={ - "variables": { - "type": "integerValue" - } + "variables": DebugVariableValue_IntegerValue( + integer_value=, + ) }, ), Scope( variables={ - "variables": { - "type": "integerValue" - } + "variables": DebugVariableValue_IntegerValue( + integer_value=, + ) }, ) ], @@ -1676,8 +1678,8 @@ Creates a problem ```python from seed import SeedTrace from seed.environment import SeedTraceEnvironment -from seed.problem import ProblemDescription, ProblemFiles, VariableTypeAndName -from seed.commons import FileInfo, TestCaseWithExpectedResult, TestCase +from seed.problem import ProblemDescription, ProblemDescriptionBoard_Html, ProblemFiles, VariableTypeAndName +from seed.commons import FileInfo, VariableType_IntegerType, TestCaseWithExpectedResult, TestCase, VariableValue_IntegerValue client = SeedTrace( token="", @@ -1688,12 +1690,12 @@ client.problem.create_problem( problem_name="problemName", problem_description=ProblemDescription( boards=[ - { - "type": "html" - }, - { - "type": "html" - } + ProblemDescriptionBoard_Html( + html=, + ), + ProblemDescriptionBoard_Html( + html=, + ) ], ), files={ @@ -1716,53 +1718,47 @@ client.problem.create_problem( }, input_params=[ VariableTypeAndName( - variable_type={ - "type": "integerType" - }, + variable_type=VariableType_IntegerType(), name="name", ), VariableTypeAndName( - variable_type={ - "type": "integerType" - }, + variable_type=VariableType_IntegerType(), name="name", ) ], - output_type={ - "type": "integerType" - }, + output_type=VariableType_IntegerType(), testcases=[ TestCaseWithExpectedResult( test_case=TestCase( id="id", params=[ - { - "type": "integerValue" - }, - { - "type": "integerValue" - } + VariableValue_IntegerValue( + integer_value=, + ), + VariableValue_IntegerValue( + integer_value=, + ) ], ), - expected_result={ - "type": "integerValue" - }, + expected_result=VariableValue_IntegerValue( + integer_value=, + ), ), TestCaseWithExpectedResult( test_case=TestCase( id="id", params=[ - { - "type": "integerValue" - }, - { - "type": "integerValue" - } + VariableValue_IntegerValue( + integer_value=, + ), + VariableValue_IntegerValue( + integer_value=, + ) ], ), - expected_result={ - "type": "integerValue" - }, + expected_result=VariableValue_IntegerValue( + integer_value=, + ), ) ], method_name="methodName", @@ -1831,8 +1827,8 @@ Updates a problem ```python from seed import SeedTrace from seed.environment import SeedTraceEnvironment -from seed.problem import ProblemDescription, ProblemFiles, VariableTypeAndName -from seed.commons import FileInfo, TestCaseWithExpectedResult, TestCase +from seed.problem import ProblemDescription, ProblemDescriptionBoard_Html, ProblemFiles, VariableTypeAndName +from seed.commons import FileInfo, VariableType_IntegerType, TestCaseWithExpectedResult, TestCase, VariableValue_IntegerValue client = SeedTrace( token="", @@ -1844,12 +1840,12 @@ client.problem.update_problem( problem_name="problemName", problem_description=ProblemDescription( boards=[ - { - "type": "html" - }, - { - "type": "html" - } + ProblemDescriptionBoard_Html( + html=, + ), + ProblemDescriptionBoard_Html( + html=, + ) ], ), files={ @@ -1872,53 +1868,47 @@ client.problem.update_problem( }, input_params=[ VariableTypeAndName( - variable_type={ - "type": "integerType" - }, + variable_type=VariableType_IntegerType(), name="name", ), VariableTypeAndName( - variable_type={ - "type": "integerType" - }, + variable_type=VariableType_IntegerType(), name="name", ) ], - output_type={ - "type": "integerType" - }, + output_type=VariableType_IntegerType(), testcases=[ TestCaseWithExpectedResult( test_case=TestCase( id="id", params=[ - { - "type": "integerValue" - }, - { - "type": "integerValue" - } + VariableValue_IntegerValue( + integer_value=, + ), + VariableValue_IntegerValue( + integer_value=, + ) ], ), - expected_result={ - "type": "integerValue" - }, + expected_result=VariableValue_IntegerValue( + integer_value=, + ), ), TestCaseWithExpectedResult( test_case=TestCase( id="id", params=[ - { - "type": "integerValue" - }, - { - "type": "integerValue" - } + VariableValue_IntegerValue( + integer_value=, + ), + VariableValue_IntegerValue( + integer_value=, + ) ], ), - expected_result={ - "type": "integerValue" - }, + expected_result=VariableValue_IntegerValue( + integer_value=, + ), ) ], method_name="methodName", @@ -2069,6 +2059,7 @@ Returns default starter files for problem from seed import SeedTrace from seed.environment import SeedTraceEnvironment from seed.problem import VariableTypeAndName +from seed.commons import VariableType_IntegerType client = SeedTrace( token="", @@ -2078,21 +2069,15 @@ client = SeedTrace( client.problem.get_default_starter_files( input_params=[ VariableTypeAndName( - variable_type={ - "type": "integerType" - }, + variable_type=VariableType_IntegerType(), name="name", ), VariableTypeAndName( - variable_type={ - "type": "integerType" - }, + variable_type=VariableType_IntegerType(), name="name", ) ], - output_type={ - "type": "integerType" - }, + output_type=VariableType_IntegerType(), method_name="methodName", ) diff --git a/seed/python-sdk/unions-with-local-date/reference.md b/seed/python-sdk/unions-with-local-date/reference.md index c81accab093a..ad61819a28ff 100644 --- a/seed/python-sdk/unions-with-local-date/reference.md +++ b/seed/python-sdk/unions-with-local-date/reference.md @@ -71,6 +71,7 @@ client.bigunion.get( ```python from seed import SeedUnions +from seed.bigunion import BigUnion_NormalSweet import datetime client = SeedUnions( @@ -78,13 +79,12 @@ client = SeedUnions( ) client.bigunion.update( - request={ - "type": "normalSweet", - "id": "id", - "created_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "archived_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "value": "value" - }, + request=BigUnion_NormalSweet( + id="id", + created_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + archived_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + value="value", + ), ) ``` @@ -135,6 +135,7 @@ client.bigunion.update( ```python from seed import SeedUnions +from seed.bigunion import BigUnion_NormalSweet import datetime client = SeedUnions( @@ -143,20 +144,18 @@ client = SeedUnions( client.bigunion.update_many( request=[ - { - "type": "normalSweet", - "id": "id", - "created_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "archived_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "value": "value" - }, - { - "type": "normalSweet", - "id": "id", - "created_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "archived_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "value": "value" - } + BigUnion_NormalSweet( + id="id", + created_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + archived_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + value="value", + ), + BigUnion_NormalSweet( + id="id", + created_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + archived_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + value="value", + ) ], ) @@ -266,15 +265,16 @@ client.types.get( ```python from seed import SeedUnions +from seed.types import UnionWithTime_Date client = SeedUnions( base_url="https://yourhost.com/path/to/api", ) client.types.update( - request={ - "type": "date" - }, + request=UnionWithTime_Date( + date=, + ), ) ``` @@ -383,17 +383,17 @@ client.bigunion.get( ```python from seed import SeedUnions +from seed.union import Shape_Circle client = SeedUnions( base_url="https://yourhost.com/path/to/api", ) client.union.update( - request={ - "type": "circle", - "id": "id", - "radius": 1.1 - }, + request=Shape_Circle( + id="id", + radius=1.1, + ), ) ``` diff --git a/seed/python-sdk/unions/no-custom-config/reference.md b/seed/python-sdk/unions/no-custom-config/reference.md index 847cc4d885ab..ef52dd4c84c7 100644 --- a/seed/python-sdk/unions/no-custom-config/reference.md +++ b/seed/python-sdk/unions/no-custom-config/reference.md @@ -71,6 +71,7 @@ client.bigunion.get( ```python from seed import SeedUnions +from seed.bigunion import BigUnion_NormalSweet import datetime client = SeedUnions( @@ -78,13 +79,12 @@ client = SeedUnions( ) client.bigunion.update( - request={ - "type": "normalSweet", - "id": "id", - "created_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "archived_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "value": "value" - }, + request=BigUnion_NormalSweet( + id="id", + created_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + archived_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + value="value", + ), ) ``` @@ -135,6 +135,7 @@ client.bigunion.update( ```python from seed import SeedUnions +from seed.bigunion import BigUnion_NormalSweet import datetime client = SeedUnions( @@ -143,20 +144,18 @@ client = SeedUnions( client.bigunion.update_many( request=[ - { - "type": "normalSweet", - "id": "id", - "created_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "archived_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "value": "value" - }, - { - "type": "normalSweet", - "id": "id", - "created_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "archived_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "value": "value" - } + BigUnion_NormalSweet( + id="id", + created_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + archived_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + value="value", + ), + BigUnion_NormalSweet( + id="id", + created_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + archived_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + value="value", + ) ], ) @@ -266,17 +265,17 @@ client.bigunion.get( ```python from seed import SeedUnions +from seed.union import Shape_Circle client = SeedUnions( base_url="https://yourhost.com/path/to/api", ) client.union.update( - request={ - "type": "circle", - "id": "id", - "radius": 1.1 - }, + request=Shape_Circle( + id="id", + radius=1.1, + ), ) ``` diff --git a/seed/python-sdk/unions/union-naming-v1/reference.md b/seed/python-sdk/unions/union-naming-v1/reference.md index 847cc4d885ab..ef52dd4c84c7 100644 --- a/seed/python-sdk/unions/union-naming-v1/reference.md +++ b/seed/python-sdk/unions/union-naming-v1/reference.md @@ -71,6 +71,7 @@ client.bigunion.get( ```python from seed import SeedUnions +from seed.bigunion import BigUnion_NormalSweet import datetime client = SeedUnions( @@ -78,13 +79,12 @@ client = SeedUnions( ) client.bigunion.update( - request={ - "type": "normalSweet", - "id": "id", - "created_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "archived_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "value": "value" - }, + request=BigUnion_NormalSweet( + id="id", + created_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + archived_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + value="value", + ), ) ``` @@ -135,6 +135,7 @@ client.bigunion.update( ```python from seed import SeedUnions +from seed.bigunion import BigUnion_NormalSweet import datetime client = SeedUnions( @@ -143,20 +144,18 @@ client = SeedUnions( client.bigunion.update_many( request=[ - { - "type": "normalSweet", - "id": "id", - "created_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "archived_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "value": "value" - }, - { - "type": "normalSweet", - "id": "id", - "created_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "archived_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "value": "value" - } + BigUnion_NormalSweet( + id="id", + created_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + archived_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + value="value", + ), + BigUnion_NormalSweet( + id="id", + created_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + archived_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + value="value", + ) ], ) @@ -266,17 +265,17 @@ client.bigunion.get( ```python from seed import SeedUnions +from seed.union import Shape_Circle client = SeedUnions( base_url="https://yourhost.com/path/to/api", ) client.union.update( - request={ - "type": "circle", - "id": "id", - "radius": 1.1 - }, + request=Shape_Circle( + id="id", + radius=1.1, + ), ) ``` diff --git a/seed/python-sdk/unions/union-utils/reference.md b/seed/python-sdk/unions/union-utils/reference.md index 847cc4d885ab..ef52dd4c84c7 100644 --- a/seed/python-sdk/unions/union-utils/reference.md +++ b/seed/python-sdk/unions/union-utils/reference.md @@ -71,6 +71,7 @@ client.bigunion.get( ```python from seed import SeedUnions +from seed.bigunion import BigUnion_NormalSweet import datetime client = SeedUnions( @@ -78,13 +79,12 @@ client = SeedUnions( ) client.bigunion.update( - request={ - "type": "normalSweet", - "id": "id", - "created_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "archived_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "value": "value" - }, + request=BigUnion_NormalSweet( + id="id", + created_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + archived_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + value="value", + ), ) ``` @@ -135,6 +135,7 @@ client.bigunion.update( ```python from seed import SeedUnions +from seed.bigunion import BigUnion_NormalSweet import datetime client = SeedUnions( @@ -143,20 +144,18 @@ client = SeedUnions( client.bigunion.update_many( request=[ - { - "type": "normalSweet", - "id": "id", - "created_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "archived_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "value": "value" - }, - { - "type": "normalSweet", - "id": "id", - "created_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "archived_at": datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), - "value": "value" - } + BigUnion_NormalSweet( + id="id", + created_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + archived_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + value="value", + ), + BigUnion_NormalSweet( + id="id", + created_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + archived_at=datetime.datetime.fromisoformat("2024-01-15T09:30:00+00:00"), + value="value", + ) ], ) @@ -266,17 +265,17 @@ client.bigunion.get( ```python from seed import SeedUnions +from seed.union import Shape_Circle client = SeedUnions( base_url="https://yourhost.com/path/to/api", ) client.union.update( - request={ - "type": "circle", - "id": "id", - "radius": 1.1 - }, + request=Shape_Circle( + id="id", + radius=1.1, + ), ) ```