From ee56b56d65673a9a6dd92552e12906665ed3256a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Feb 2026 21:27:13 +0000 Subject: [PATCH 01/54] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 2702d73..3004020 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 7 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-48f00d1c04c23fb4d1cb7cf4af4f56b0c920d758c1f06e06e5373e5b15e9c27d.yml openapi_spec_hash: 6ee2a94bb9840aceb4a6161c724ce46c -config_hash: 249869757b6eb98ae3d58f2a47ce21e2 +config_hash: c8d97d58d67dad9eeb65eb58fc781724 From a3828431e022b0c294eea5dd6d3dfec91dc50f3c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 05:12:35 +0000 Subject: [PATCH 02/54] chore(internal): move stringifyQuery implementation to internal function --- src/client.ts | 22 +++++----------------- src/internal/utils.ts | 1 + src/internal/utils/query.ts | 23 +++++++++++++++++++++++ tests/stringifyQuery.test.ts | 6 ++---- 4 files changed, 31 insertions(+), 21 deletions(-) create mode 100644 src/internal/utils/query.ts diff --git a/src/client.ts b/src/client.ts index 889cbc0..0b9842c 100644 --- a/src/client.ts +++ b/src/client.ts @@ -11,6 +11,7 @@ import type { APIResponseProps } from './internal/parse'; import { getPlatformHeaders } from './internal/detect-platform'; import * as Shims from './internal/shims'; import * as Opts from './internal/request-options'; +import { stringifyQuery } from './internal/utils/query'; import { VERSION } from './version'; import * as Errors from './core/error'; import * as Pagination from './core/pagination'; @@ -271,21 +272,8 @@ export class Unlayer { /** * Basic re-implementation of `qs.stringify` for primitive types. */ - protected stringifyQuery(query: Record): string { - return Object.entries(query) - .filter(([_, value]) => typeof value !== 'undefined') - .map(([key, value]) => { - if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { - return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`; - } - if (value === null) { - return `${encodeURIComponent(key)}=`; - } - throw new Errors.UnlayerError( - `Cannot stringify type ${typeof value}; Expected string, number, boolean, or null. If you need to pass nested query parameters, you can manually encode them, e.g. { query: { 'foo[key1]': value1, 'foo[key2]': value2 } }, and please open a GitHub issue requesting better support for your use case.`, - ); - }) - .join('&'); + protected stringifyQuery(query: object | Record): string { + return stringifyQuery(query); } private getUserAgent(): string { @@ -322,7 +310,7 @@ export class Unlayer { } if (typeof query === 'object' && query && !Array.isArray(query)) { - url.search = this.stringifyQuery(query as Record); + url.search = this.stringifyQuery(query); } return url.toString(); @@ -786,7 +774,7 @@ export class Unlayer { ) { return { bodyHeaders: { 'content-type': 'application/x-www-form-urlencoded' }, - body: this.stringifyQuery(body as Record), + body: this.stringifyQuery(body), }; } else { return this.#encoder({ body, headers }); diff --git a/src/internal/utils.ts b/src/internal/utils.ts index 3cbfacc..c591353 100644 --- a/src/internal/utils.ts +++ b/src/internal/utils.ts @@ -6,3 +6,4 @@ export * from './utils/env'; export * from './utils/log'; export * from './utils/uuid'; export * from './utils/sleep'; +export * from './utils/query'; diff --git a/src/internal/utils/query.ts b/src/internal/utils/query.ts new file mode 100644 index 0000000..bd0eb5e --- /dev/null +++ b/src/internal/utils/query.ts @@ -0,0 +1,23 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { UnlayerError } from '../../core/error'; + +/** + * Basic re-implementation of `qs.stringify` for primitive types. + */ +export function stringifyQuery(query: object | Record) { + return Object.entries(query) + .filter(([_, value]) => typeof value !== 'undefined') + .map(([key, value]) => { + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`; + } + if (value === null) { + return `${encodeURIComponent(key)}=`; + } + throw new UnlayerError( + `Cannot stringify type ${typeof value}; Expected string, number, boolean, or null. If you need to pass nested query parameters, you can manually encode them, e.g. { query: { 'foo[key1]': value1, 'foo[key2]': value2 } }, and please open a GitHub issue requesting better support for your use case.`, + ); + }) + .join('&'); +} diff --git a/tests/stringifyQuery.test.ts b/tests/stringifyQuery.test.ts index 4f47883..37eca7e 100644 --- a/tests/stringifyQuery.test.ts +++ b/tests/stringifyQuery.test.ts @@ -1,8 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { Unlayer } from '@unlayer/sdk'; - -const { stringifyQuery } = Unlayer.prototype as any; +import { stringifyQuery } from '@unlayer/sdk/internal/utils/query'; describe(stringifyQuery, () => { for (const [input, expected] of [ @@ -15,7 +13,7 @@ describe(stringifyQuery, () => { 'e=f', )}=${encodeURIComponent('g&h')}`, ], - ]) { + ] as const) { it(`${JSON.stringify(input)} -> ${expected}`, () => { expect(stringifyQuery(input)).toEqual(expected); }); From c6204bdf620e82a1b0de3859c74a0b04ddb220be Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 05:10:29 +0000 Subject: [PATCH 03/54] chore(internal): codegen related update --- src/client.ts | 9 +++++++++ src/resources/convert/full-to-simple.ts | 3 +++ src/resources/convert/simple-to-full.ts | 3 +++ src/resources/projects.ts | 3 +++ src/resources/templates.ts | 3 +++ src/resources/workspaces.ts | 3 +++ 6 files changed, 24 insertions(+) diff --git a/src/client.ts b/src/client.ts index 0b9842c..c6731e9 100644 --- a/src/client.ts +++ b/src/client.ts @@ -801,8 +801,17 @@ export class Unlayer { static toFile = Uploads.toFile; convert: API.Convert = new API.Convert(this); + /** + * Project details and configuration. + */ projects: API.Projects = new API.Projects(this); + /** + * Template management and retrieval. + */ templates: API.Templates = new API.Templates(this); + /** + * Workspace access and management. + */ workspaces: API.Workspaces = new API.Workspaces(this); } diff --git a/src/resources/convert/full-to-simple.ts b/src/resources/convert/full-to-simple.ts index 44e02b9..ceb1e69 100644 --- a/src/resources/convert/full-to-simple.ts +++ b/src/resources/convert/full-to-simple.ts @@ -4,6 +4,9 @@ import { APIResource } from '../../core/resource'; import { APIPromise } from '../../core/api-promise'; import { RequestOptions } from '../../internal/request-options'; +/** + * Design schema conversion between Full and Simple formats. + */ export class FullToSimple extends APIResource { /** * Convert design json from Full to Simple schema. diff --git a/src/resources/convert/simple-to-full.ts b/src/resources/convert/simple-to-full.ts index c1051de..2790174 100644 --- a/src/resources/convert/simple-to-full.ts +++ b/src/resources/convert/simple-to-full.ts @@ -4,6 +4,9 @@ import { APIResource } from '../../core/resource'; import { APIPromise } from '../../core/api-promise'; import { RequestOptions } from '../../internal/request-options'; +/** + * Design schema conversion between Full and Simple formats. + */ export class SimpleToFull extends APIResource { /** * Convert design json from Simple to Full schema. diff --git a/src/resources/projects.ts b/src/resources/projects.ts index 6b97668..0d15122 100644 --- a/src/resources/projects.ts +++ b/src/resources/projects.ts @@ -5,6 +5,9 @@ import { APIPromise } from '../core/api-promise'; import { RequestOptions } from '../internal/request-options'; import { path } from '../internal/utils/path'; +/** + * Project details and configuration. + */ export class Projects extends APIResource { /** * Get project details by ID. diff --git a/src/resources/templates.ts b/src/resources/templates.ts index 38eef4b..8d1cbb8 100644 --- a/src/resources/templates.ts +++ b/src/resources/templates.ts @@ -6,6 +6,9 @@ import { CursorPage, type CursorPageParams, PagePromise } from '../core/paginati import { RequestOptions } from '../internal/request-options'; import { path } from '../internal/utils/path'; +/** + * Template management and retrieval. + */ export class Templates extends APIResource { /** * Get template by ID. diff --git a/src/resources/workspaces.ts b/src/resources/workspaces.ts index e51624e..4b338e1 100644 --- a/src/resources/workspaces.ts +++ b/src/resources/workspaces.ts @@ -5,6 +5,9 @@ import { APIPromise } from '../core/api-promise'; import { RequestOptions } from '../internal/request-options'; import { path } from '../internal/utils/path'; +/** + * Workspace access and management. + */ export class Workspaces extends APIResource { /** * Get a specific workspace by ID with its projects. Requires a Personal Access From 5d0b8cae4d04648e5904edde4c4be65d2f22b144 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2026 05:07:33 +0000 Subject: [PATCH 04/54] chore(internal): codegen related update --- src/client.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client.ts b/src/client.ts index c6731e9..88a8145 100644 --- a/src/client.ts +++ b/src/client.ts @@ -639,9 +639,9 @@ export class Unlayer { } } - // If the API asks us to wait a certain amount of time (and it's a reasonable amount), - // just do what it says, but otherwise calculate a default - if (!(timeoutMillis && 0 <= timeoutMillis && timeoutMillis < 60 * 1000)) { + // If the API asks us to wait a certain amount of time, just do what it + // says, but otherwise calculate a default + if (timeoutMillis === undefined) { const maxRetries = options.maxRetries ?? this.maxRetries; timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries); } From 3f4f1372bc0146d0abd93d289d1edf1a4c6ce864 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2026 05:30:01 +0000 Subject: [PATCH 05/54] chore(test): do not count install time for mock server timeout --- scripts/mock | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/scripts/mock b/scripts/mock index 0b28f6e..bcf3b39 100755 --- a/scripts/mock +++ b/scripts/mock @@ -21,11 +21,22 @@ echo "==> Starting mock server with URL ${URL}" # Run prism mock on the given spec if [ "$1" == "--daemon" ]; then + # Pre-install the package so the download doesn't eat into the startup timeout + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism --version + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & - # Wait for server to come online + # Wait for server to come online (max 30s) echo -n "Waiting for server" + attempts=0 while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + attempts=$((attempts + 1)) + if [ "$attempts" -ge 300 ]; then + echo + echo "Timed out waiting for Prism server to start" + cat .prism.log + exit 1 + fi echo -n "." sleep 0.1 done From 5ba5d2871132cd901801f72d9d82ae8a0d03adc1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 7 Mar 2026 16:13:43 +0000 Subject: [PATCH 06/54] chore(ci): skip uploading artifacts on stainless-internal branches --- .github/workflows/ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 746dabb..585f542 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,14 +55,18 @@ jobs: run: ./scripts/build - name: Get GitHub OIDC Token - if: github.repository == 'stainless-sdks/unlayer-typescript' + if: |- + github.repository == 'stainless-sdks/unlayer-typescript' && + !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc uses: actions/github-script@v8 with: script: core.setOutput('github_token', await core.getIDToken()); - name: Upload tarball - if: github.repository == 'stainless-sdks/unlayer-typescript' + if: |- + github.repository == 'stainless-sdks/unlayer-typescript' && + !startsWith(github.ref, 'refs/heads/stl/') env: URL: https://pkg.stainless.com/s AUTH: ${{ steps.github-oidc.outputs.github_token }} From aa35c3f9daed566ab225bb5460667b55accfdec3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 7 Mar 2026 16:20:53 +0000 Subject: [PATCH 07/54] fix(client): preserve URL params already embedded in path --- src/client.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/client.ts b/src/client.ts index 88a8145..8044a89 100644 --- a/src/client.ts +++ b/src/client.ts @@ -305,8 +305,9 @@ export class Unlayer { : new URL(baseURL + (baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path)); const defaultQuery = this.defaultQuery(); - if (!isEmptyObj(defaultQuery)) { - query = { ...defaultQuery, ...query }; + const pathQuery = Object.fromEntries(url.searchParams); + if (!isEmptyObj(defaultQuery) || !isEmptyObj(pathQuery)) { + query = { ...pathQuery, ...defaultQuery, ...query }; } if (typeof query === 'object' && query && !Array.isArray(query)) { From cd210f0014bf4c6f91f96b0458b97b28b9b02004 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 02:54:14 +0000 Subject: [PATCH 08/54] chore(internal): update dependencies to address dependabot vulnerabilities --- package.json | 11 +++++++++++ yarn.lock | 39 ++++++--------------------------------- 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index af883de..ef78334 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,17 @@ "typescript": "5.8.3", "typescript-eslint": "8.31.1" }, + "overrides": { + "minimatch": "^9.0.5" + }, + "pnpm": { + "overrides": { + "minimatch": "^9.0.5" + } + }, + "resolutions": { + "minimatch": "^9.0.5" + }, "exports": { ".": { "import": "./dist/index.mjs", diff --git a/yarn.lock b/yarn.lock index fc9f262..078f09a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1219,15 +1219,7 @@ baseline-browser-mapping@^2.9.0: resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.14.tgz#3b6af0bc032445bca04de58caa9a87cfe921cbb3" integrity sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg== -brace-expansion@^1.1.7: - version "1.1.12" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" - integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -brace-expansion@^2.0.1: +brace-expansion@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== @@ -1395,11 +1387,6 @@ commander@^10.0.1: resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - convert-source-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" @@ -2600,26 +2587,12 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^5.0.1: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - -minimatch@^9.0.4: - version "9.0.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" - integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== +minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2, minimatch@^5.0.1, minimatch@^9.0.4, minimatch@^9.0.5: + version "9.0.9" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.9.tgz#9b0cb9fcb78087f6fd7eababe2511c4d3d60574e" + integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg== dependencies: - brace-expansion "^2.0.1" + brace-expansion "^2.0.2" minimist@^1.2.6: version "1.2.6" From 37047f64ad65e540363a5888057fb2031797fa89 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 10:20:31 +0000 Subject: [PATCH 09/54] chore(internal): tweak CI branches --- .github/workflows/ci.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 585f542..51c9c41 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,14 @@ name: CI on: push: - branches-ignore: - - 'generated' - - 'codegen/**' - - 'integrated/**' - - 'stl-preview-head/**' - - 'stl-preview-base/**' + branches: + - '**' + - '!integrated/**' + - '!stl-preview-head/**' + - '!stl-preview-base/**' + - '!generated' + - '!codegen/**' + - 'codegen/stl/**' pull_request: branches-ignore: - 'stl-preview-head/**' From 3b6f5e4fe8b7e7df20f500d9191075331c56310e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2026 03:24:05 +0000 Subject: [PATCH 10/54] refactor(tests): switch from prism to steady --- CONTRIBUTING.md | 2 +- scripts/mock | 26 +++++++++++++------------- scripts/test | 16 ++++++++-------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 051f357..6069b57 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,7 +65,7 @@ $ pnpm link --global @unlayer/sdk ## Running tests -Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. +Most tests require you to [set up a mock server](https://github.com/dgellow/steady) against the OpenAPI spec to run the tests. ```sh $ ./scripts/mock diff --git a/scripts/mock b/scripts/mock index bcf3b39..38201de 100755 --- a/scripts/mock +++ b/scripts/mock @@ -19,34 +19,34 @@ fi echo "==> Starting mock server with URL ${URL}" -# Run prism mock on the given spec +# Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism --version + npm exec --package=@stdy/cli@0.19.3 -- steady --version - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & + npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets "$URL" &> .stdy.log & - # Wait for server to come online (max 30s) + # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" attempts=0 - while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + while ! curl --silent --fail "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1; do + if ! kill -0 $! 2>/dev/null; then + echo + cat .stdy.log + exit 1 + fi attempts=$((attempts + 1)) if [ "$attempts" -ge 300 ]; then echo - echo "Timed out waiting for Prism server to start" - cat .prism.log + echo "Timed out waiting for Steady server to start" + cat .stdy.log exit 1 fi echo -n "." sleep 0.1 done - if grep -q "✖ fatal" ".prism.log"; then - cat .prism.log - exit 1 - fi - echo else - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" + npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 7bce051..af1c7a5 100755 --- a/scripts/test +++ b/scripts/test @@ -9,8 +9,8 @@ GREEN='\033[0;32m' YELLOW='\033[0;33m' NC='\033[0m' # No Color -function prism_is_running() { - curl --silent "http://localhost:4010" >/dev/null 2>&1 +function steady_is_running() { + curl --silent "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1 } kill_server_on_port() { @@ -25,7 +25,7 @@ function is_overriding_api_base_url() { [ -n "$TEST_API_BASE_URL" ] } -if ! is_overriding_api_base_url && ! prism_is_running ; then +if ! is_overriding_api_base_url && ! steady_is_running ; then # When we exit this script, make sure to kill the background mock server process trap 'kill_server_on_port 4010' EXIT @@ -36,19 +36,19 @@ fi if is_overriding_api_base_url ; then echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" echo -elif ! prism_is_running ; then - echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" +elif ! steady_is_running ; then + echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Steady server" echo -e "running against your OpenAPI spec." echo echo -e "To run the server, pass in the path or url of your OpenAPI" - echo -e "spec to the prism command:" + echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.3 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets${NC}" echo exit 1 else - echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" + echo -e "${GREEN}✔ Mock steady server is running with your OpenAPI spec${NC}" echo fi From bce44adfdaa4fca510a83d182fc9b4788ad454c1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 21 Mar 2026 04:17:16 +0000 Subject: [PATCH 11/54] chore(tests): bump steady to v0.19.4 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 38201de..e1c19e8 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.3 -- steady --version + npm exec --package=@stdy/cli@0.19.4 -- steady --version - npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index af1c7a5..8cf5220 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.3 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.4 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From 1ecdf4a084af6afb151688f3d9ae50ed99d978cb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 21 Mar 2026 04:21:48 +0000 Subject: [PATCH 12/54] chore(tests): bump steady to v0.19.5 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index e1c19e8..ab814d3 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.4 -- steady --version + npm exec --package=@stdy/cli@0.19.5 -- steady --version - npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 8cf5220..907f7be 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.4 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.5 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From 3f8fe21e12dd47a7ec5381b1d01c4b3319d48f0d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 04:19:53 +0000 Subject: [PATCH 13/54] chore(internal): update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2412bb7..c85fe68 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .prism.log +.stdy.log node_modules yarn-error.log codegen.log From 13a8f4ab35b1f6487aaf4b2ae1f9fcc771df00f8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 04:25:12 +0000 Subject: [PATCH 14/54] chore(tests): bump steady to v0.19.6 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index ab814d3..b319bdf 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.5 -- steady --version + npm exec --package=@stdy/cli@0.19.6 -- steady --version - npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 907f7be..8061e04 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.5 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.6 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From 2ff6735db5aee19634ac92a8f5903a2496b5d9fd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 03:05:16 +0000 Subject: [PATCH 15/54] chore(ci): skip lint on metadata-only changes Note that we still want to run tests, as these depend on the metadata. --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51c9c41..b770d07 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: timeout-minutes: 10 name: lint runs-on: ${{ github.repository == 'stainless-sdks/unlayer-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} - if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - uses: actions/checkout@v6 @@ -38,7 +38,7 @@ jobs: timeout-minutes: 5 name: build runs-on: ${{ github.repository == 'stainless-sdks/unlayer-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} - if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') permissions: contents: read id-token: write From 39442381621c93dbeed18b939de999606bcdf3f2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 03:05:49 +0000 Subject: [PATCH 16/54] chore(tests): bump steady to v0.19.7 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index b319bdf..09eb49f 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.6 -- steady --version + npm exec --package=@stdy/cli@0.19.7 -- steady --version - npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 8061e04..a7cf561 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.6 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From eb9810b160faa0c0eb5607a900d0bc3efe80c837 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 05:32:30 +0000 Subject: [PATCH 17/54] chore(internal): update multipart form array serialization --- scripts/mock | 4 ++-- scripts/test | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/mock b/scripts/mock index 09eb49f..290e21b 100755 --- a/scripts/mock +++ b/scripts/mock @@ -24,7 +24,7 @@ if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout npm exec --package=@stdy/cli@0.19.7 -- steady --version - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index a7cf561..a1ebb5e 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 From 6dc4c8844896dcc94667c0b7ff0d0ea37e886e1a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 28 Mar 2026 06:11:54 +0000 Subject: [PATCH 18/54] chore(internal): codegen related update --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 078f09a..e5e2a93 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1220,9 +1220,9 @@ baseline-browser-mapping@^2.9.0: integrity sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg== brace-expansion@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" - integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== + version "2.0.3" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.3.tgz#0493338bdd58e319b1039c67cf7ee439892c01d9" + integrity sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA== dependencies: balanced-match "^1.0.0" From f058a02837772b49e641ab186988c76aa4445695 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 07:20:07 +0000 Subject: [PATCH 19/54] chore(tests): bump steady to v0.20.1 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 290e21b..15c2994 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.7 -- steady --version + npm exec --package=@stdy/cli@0.20.1 -- steady --version - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index a1ebb5e..7431f9f 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 From e312dc60c1bf68b3faa91d94feee712f2ab22b60 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 07:23:48 +0000 Subject: [PATCH 20/54] chore(tests): bump steady to v0.20.2 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 15c2994..5cd7c15 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.20.1 -- steady --version + npm exec --package=@stdy/cli@0.20.2 -- steady --version - npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 7431f9f..a9d718c 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.2 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 From 7ce40c6dd2c9ff9f0442e6f89b60ca47c19a9351 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 08:52:34 +0000 Subject: [PATCH 21/54] chore(internal): codegen related update --- src/internal/utils/env.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/internal/utils/env.ts b/src/internal/utils/env.ts index 2d84800..cc5fa0f 100644 --- a/src/internal/utils/env.ts +++ b/src/internal/utils/env.ts @@ -9,10 +9,10 @@ */ export const readEnv = (env: string): string | undefined => { if (typeof (globalThis as any).process !== 'undefined') { - return (globalThis as any).process.env?.[env]?.trim() ?? undefined; + return (globalThis as any).process.env?.[env]?.trim() || undefined; } if (typeof (globalThis as any).Deno !== 'undefined') { - return (globalThis as any).Deno.env?.get?.(env)?.trim(); + return (globalThis as any).Deno.env?.get?.(env)?.trim() || undefined; } return undefined; }; From 740e1ae35ac61a36d7f1e109456844432d2bd6c6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 02:19:34 +0000 Subject: [PATCH 22/54] chore(internal): codegen related update --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index e5e2a93..f6eae3c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1220,9 +1220,9 @@ baseline-browser-mapping@^2.9.0: integrity sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg== brace-expansion@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.3.tgz#0493338bdd58e319b1039c67cf7ee439892c01d9" - integrity sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA== + version "2.1.0" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.0.tgz#4f41a41190216ee36067ec381526fe9539c4f0ae" + integrity sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w== dependencies: balanced-match "^1.0.0" From 005c46350d8b94a4273e5f2490c6f450a7eb8459 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 07:03:09 +0000 Subject: [PATCH 23/54] chore(tests): bump steady to v0.22.1 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 5cd7c15..feebe5e 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.20.2 -- steady --version + npm exec --package=@stdy/cli@0.22.1 -- steady --version - npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index a9d718c..19b8d0c 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.2 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.22.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 From 6f63cba6c4e4158a76951f682b5391bac71e74ee Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 22 Apr 2026 13:28:09 +0000 Subject: [PATCH 24/54] feat(api): api update --- .stats.yml | 8 +- api.md | 12 ++ src/client.ts | 5 + src/resources/ai.ts | 3 + src/resources/ai/ai.ts | 19 +++ src/resources/ai/generate.ts | 188 ++++++++++++++++++++++++ src/resources/ai/index.ts | 4 + src/resources/index.ts | 1 + tests/api-resources/ai/generate.test.ts | 55 +++++++ 9 files changed, 291 insertions(+), 4 deletions(-) create mode 100644 src/resources/ai.ts create mode 100644 src/resources/ai/ai.ts create mode 100644 src/resources/ai/generate.ts create mode 100644 src/resources/ai/index.ts create mode 100644 tests/api-resources/ai/generate.test.ts diff --git a/.stats.yml b/.stats.yml index 3004020..2750c56 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 7 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-48f00d1c04c23fb4d1cb7cf4af4f56b0c920d758c1f06e06e5373e5b15e9c27d.yml -openapi_spec_hash: 6ee2a94bb9840aceb4a6161c724ce46c -config_hash: c8d97d58d67dad9eeb65eb58fc781724 +configured_endpoints: 8 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-a1351fe18005248184e11e2c1d6e4a8df7ecfd0092f9fcfeabaa025a2c5b4986.yml +openapi_spec_hash: 3633a7fdec0e4c3d72dcbadeebaea907 +config_hash: 6f1858ca62cea01f7c1c4427b9263c25 diff --git a/api.md b/api.md index 6cee992..c4cfb8a 100644 --- a/api.md +++ b/api.md @@ -1,3 +1,15 @@ +# AI + +## Generate + +Types: + +- GenerateCreateResponse + +Methods: + +- client.ai.generate.create({ ...params }) -> GenerateCreateResponse + # Convert ## FullToSimple diff --git a/src/client.ts b/src/client.ts index 8044a89..86c7199 100644 --- a/src/client.ts +++ b/src/client.ts @@ -29,6 +29,7 @@ import { Templates, } from './resources/templates'; import { WorkspaceListResponse, WorkspaceRetrieveResponse, Workspaces } from './resources/workspaces'; +import { AI } from './resources/ai/ai'; import { Convert } from './resources/convert/convert'; import { type Fetch } from './internal/builtin-types'; import { HeadersLike, NullableHeaders, buildHeaders } from './internal/headers'; @@ -801,6 +802,7 @@ export class Unlayer { static toFile = Uploads.toFile; + ai: API.AI = new API.AI(this); convert: API.Convert = new API.Convert(this); /** * Project details and configuration. @@ -816,6 +818,7 @@ export class Unlayer { workspaces: API.Workspaces = new API.Workspaces(this); } +Unlayer.AI = AI; Unlayer.Convert = Convert; Unlayer.Projects = Projects; Unlayer.Templates = Templates; @@ -827,6 +830,8 @@ export declare namespace Unlayer { export import CursorPage = Pagination.CursorPage; export { type CursorPageParams as CursorPageParams, type CursorPageResponse as CursorPageResponse }; + export { AI as AI }; + export { Convert as Convert }; export { Projects as Projects, type ProjectRetrieveResponse as ProjectRetrieveResponse }; diff --git a/src/resources/ai.ts b/src/resources/ai.ts new file mode 100644 index 0000000..6bea0b9 --- /dev/null +++ b/src/resources/ai.ts @@ -0,0 +1,3 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export * from './ai/index'; diff --git a/src/resources/ai/ai.ts b/src/resources/ai/ai.ts new file mode 100644 index 0000000..c94d536 --- /dev/null +++ b/src/resources/ai/ai.ts @@ -0,0 +1,19 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import * as GenerateAPI from './generate'; +import { Generate, GenerateCreateParams, GenerateCreateResponse } from './generate'; + +export class AI extends APIResource { + generate: GenerateAPI.Generate = new GenerateAPI.Generate(this._client); +} + +AI.Generate = Generate; + +export declare namespace AI { + export { + Generate as Generate, + type GenerateCreateResponse as GenerateCreateResponse, + type GenerateCreateParams as GenerateCreateParams, + }; +} diff --git a/src/resources/ai/generate.ts b/src/resources/ai/generate.ts new file mode 100644 index 0000000..b55824c --- /dev/null +++ b/src/resources/ai/generate.ts @@ -0,0 +1,188 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; + +export class Generate extends APIResource { + /** + * Generate, modify, or import an Unlayer design using AI. Provide typed input + * parts to describe what to generate. + */ + create(params: GenerateCreateParams, options?: RequestOptions): APIPromise { + const { projectId, ...body } = params; + return this._client.post('/v3/ai/generate', { query: { projectId }, body, ...options }); + } +} + +/** + * Successfully generated design + */ +export interface GenerateCreateResponse { + /** + * AI response ID + */ + id?: string; + + model?: string; + + output?: GenerateCreateResponse.Output; + + provider?: string; + + usage?: GenerateCreateResponse.Usage; +} + +export namespace GenerateCreateResponse { + export interface Output { + blockType?: string; + + /** + * Generated design data + */ + data?: { [key: string]: unknown }; + + type?: string; + } + + export interface Usage { + cachedInputTokens?: number; + + inputTokens?: number; + + outputTokens?: number; + + reasoningTokens?: number; + + totalTokens?: number; + } +} + +export interface GenerateCreateParams { + /** + * Body param: Display mode for the design + */ + displayMode: 'email' | 'web' | 'popup' | 'document'; + + /** + * Body param: Array of typed input parts (max 50) + */ + input: Array; + + /** + * Body param: What to generate + */ + output: GenerateCreateParams.Output; + + /** + * Query param: The project ID (required for PAT auth, auto-resolved for API key + * auth) + */ + projectId?: string; + + /** + * Body param: Editor environment context + */ + context?: GenerateCreateParams.Context; + + /** + * Body param: AI model to use, in provider/model format. Optional — defaults to + * anthropic/claude-opus-4-6. + */ + model?: 'anthropic/claude-opus-4-6' | 'openai/gpt-5.2'; +} + +export namespace GenerateCreateParams { + export interface Input { + /** + * The type of input part + */ + type: 'text' | 'prompt' | 'json' | 'html' | 'image'; + + /** + * Predefined prompt ID: SPELLING, EXPAND, SUMMARIZE, REPHRASE, FRIENDLY, FORMAL + * (for type: "prompt") + */ + id?: string; + + /** + * Block type of the design data (for type: "json") + */ + blockType?: string; + + /** + * Existing design data (object, for type: "json") or base64 image data (string, + * for type: "image") + */ + data?: { [key: string]: unknown } | string; + + /** + * HTML string to import (for type: "html") + */ + html?: string; + + /** + * Design schema version (for type: "json") + */ + schemaVersion?: number; + + /** + * Natural language prompt (for type: "text") + */ + text?: string; + + /** + * Image URL to import (for type: "image") + */ + url?: string; + } + + /** + * What to generate + */ + export interface Output { + /** + * The type of design block to generate + */ + blockType: 'template' | 'page' | 'body' | 'content' | 'row' | 'column'; + + /** + * Output format — currently only "json" is supported + */ + type: 'json'; + } + + /** + * Editor environment context + */ + export interface Context { + /** + * Filter content types available in the generated design + */ + availableTools?: Array; + + /** + * Custom tool declarations with their options + */ + customTools?: Array; + + [k: string]: unknown; + } + + export namespace Context { + export interface CustomTool { + options: { [key: string]: unknown }; + + slug: string; + + [k: string]: unknown; + } + } +} + +export declare namespace Generate { + export { + type GenerateCreateResponse as GenerateCreateResponse, + type GenerateCreateParams as GenerateCreateParams, + }; +} diff --git a/src/resources/ai/index.ts b/src/resources/ai/index.ts new file mode 100644 index 0000000..9a970e8 --- /dev/null +++ b/src/resources/ai/index.ts @@ -0,0 +1,4 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export { AI } from './ai'; +export { Generate, type GenerateCreateResponse, type GenerateCreateParams } from './generate'; diff --git a/src/resources/index.ts b/src/resources/index.ts index 303eae0..07830a5 100644 --- a/src/resources/index.ts +++ b/src/resources/index.ts @@ -1,5 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +export { AI } from './ai/ai'; export { Convert } from './convert/convert'; export { Projects, type ProjectRetrieveResponse } from './projects'; export { diff --git a/tests/api-resources/ai/generate.test.ts b/tests/api-resources/ai/generate.test.ts new file mode 100644 index 0000000..603a967 --- /dev/null +++ b/tests/api-resources/ai/generate.test.ts @@ -0,0 +1,55 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource generate', () => { + test('create: only required params', async () => { + const responsePromise = client.ai.generate.create({ + displayMode: 'email', + input: [{ type: 'text' }], + output: { blockType: 'template', type: 'json' }, + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('create: required and optional params', async () => { + const response = await client.ai.generate.create({ + displayMode: 'email', + input: [ + { + type: 'text', + id: 'id', + blockType: 'blockType', + data: { foo: 'bar' }, + html: 'html', + schemaVersion: 0, + text: 'text', + url: 'url', + }, + ], + output: { blockType: 'template', type: 'json' }, + projectId: 'projectId', + context: { + availableTools: ['string'], + customTools: [ + { + options: { foo: 'bar' }, + slug: 'slug', + }, + ], + }, + model: 'anthropic/claude-opus-4-6', + }); + }); +}); From 3c105aadb230acbf26379b4e7ac258f7c1cf8637 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 03:42:33 +0000 Subject: [PATCH 25/54] chore(internal): more robust bootstrap script --- scripts/bootstrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/bootstrap b/scripts/bootstrap index a8b69ff..2e315f5 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,7 +4,7 @@ set -e cd "$(dirname "$0")/.." -if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "${SKIP_BREW:-}" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { echo -n "==> Install Homebrew dependencies? (y/N): " read -r response From c58eac28e0d8a7c42225766239777c882f238632 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 03:44:14 +0000 Subject: [PATCH 26/54] chore(internal): codegen related update --- scripts/utils/postprocess-files.cjs | 9 ++++++++- src/client.ts | 12 ++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/scripts/utils/postprocess-files.cjs b/scripts/utils/postprocess-files.cjs index deae575..a8cdeb7 100644 --- a/scripts/utils/postprocess-files.cjs +++ b/scripts/utils/postprocess-files.cjs @@ -23,12 +23,19 @@ async function postprocess() { // strip out lib="dom", types="node", and types="react" references; these // are needed at build time, but would pollute the user's TS environment - const transformed = code.replace( + let transformed = code.replace( /^ *\/\/\/ * ' '.repeat(match.length - 1) + '\n', ); + // TypeScript's declaration emitter collapses /** @ts-ignore */ onto the same + // line as the type declaration, which doesn't work. So we convert to // @ts-ignore + // on its own line to properly suppresses errors. + if (file.endsWith('.d.ts') || file.endsWith('.d.mts') || file.endsWith('.d.cts')) { + transformed = transformed.replace(/\/\*\* @ts-ignore\b[^*]*\*\/ /gm, '// @ts-ignore\n'); + } + if (transformed !== code) { console.error(`wrote ${path.relative(process.cwd(), file)}`); await fs.promises.writeFile(file, transformed, 'utf8'); diff --git a/src/client.ts b/src/client.ts index 86c7199..aeee2f8 100644 --- a/src/client.ts +++ b/src/client.ts @@ -193,6 +193,18 @@ export class Unlayer { this.fetch = options.fetch ?? Shims.getDefaultFetch(); this.#encoder = Opts.FallbackEncoder; + const customHeadersEnv = readEnv('UNLAYER_CUSTOM_HEADERS'); + if (customHeadersEnv) { + const parsed: Record = {}; + for (const line of customHeadersEnv.split('\n')) { + const colon = line.indexOf(':'); + if (colon >= 0) { + parsed[line.substring(0, colon).trim()] = line.substring(colon + 1).trim(); + } + } + options.defaultHeaders = { ...parsed, ...options.defaultHeaders }; + } + this._options = options; this.apiKey = apiKey; From bbe35648d10ca91c0d625385894e1dbeb6337bca Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 05:27:33 +0000 Subject: [PATCH 27/54] chore(internal): codegen related update --- .github/workflows/release-doctor.yml | 1 - eslint.config.mjs | 3 --- package.json | 1 - scripts/fast-format | 9 +++----- scripts/format | 3 +-- scripts/lint | 3 +++ src/internal/types.ts | 14 ++++++------ yarn.lock | 32 ---------------------------- 8 files changed, 13 insertions(+), 53 deletions(-) diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 71339c4..5ea6b81 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -19,4 +19,3 @@ jobs: bash ./bin/check-release-environment env: NPM_TOKEN: ${{ secrets.UNLAYER_NPM_TOKEN || secrets.NPM_TOKEN }} - diff --git a/eslint.config.mjs b/eslint.config.mjs index e0dbbf8..493d7dc 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,7 +1,6 @@ // @ts-check import tseslint from 'typescript-eslint'; import unusedImports from 'eslint-plugin-unused-imports'; -import prettier from 'eslint-plugin-prettier'; export default tseslint.config( { @@ -14,11 +13,9 @@ export default tseslint.config( plugins: { '@typescript-eslint': tseslint.plugin, 'unused-imports': unusedImports, - prettier, }, rules: { 'no-unused-vars': 'off', - 'prettier/prettier': 'error', 'unused-imports/no-unused-imports': 'error', 'no-restricted-imports': [ 'error', diff --git a/package.json b/package.json index ef78334..581b952 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "@typescript-eslint/eslint-plugin": "8.31.1", "@typescript-eslint/parser": "8.31.1", "eslint": "^9.39.1", - "eslint-plugin-prettier": "^5.4.1", "eslint-plugin-unused-imports": "^4.1.4", "iconv-lite": "^0.6.3", "jest": "^29.4.0", diff --git a/scripts/fast-format b/scripts/fast-format index 53721ac..f1873ae 100755 --- a/scripts/fast-format +++ b/scripts/fast-format @@ -31,10 +31,7 @@ if ! [ -z "$ESLINT_FILES" ]; then fi echo "==> Running prettier --write" -# format things eslint didn't -PRETTIER_FILES="$(grep '\.\(js\|json\)$' "$FILE_LIST" || true)" -if ! [ -z "$PRETTIER_FILES" ]; then - echo "$PRETTIER_FILES" | xargs ./node_modules/.bin/prettier \ - --write --cache --cache-strategy metadata --no-error-on-unmatched-pattern \ - '!**/dist' '!**/*.ts' '!**/*.mts' '!**/*.cts' '!**/*.js' '!**/*.mjs' '!**/*.cjs' +if ! [ -z "$FILE_LIST" ]; then + cat "$FILE_LIST" | xargs ./node_modules/.bin/prettier \ + --write --cache --cache-strategy metadata --no-error-on-unmatched-pattern --ignore-unknown fi diff --git a/scripts/format b/scripts/format index 7a75640..b1b2c17 100755 --- a/scripts/format +++ b/scripts/format @@ -8,5 +8,4 @@ echo "==> Running eslint --fix" ./node_modules/.bin/eslint --fix . echo "==> Running prettier --write" -# format things eslint didn't -./node_modules/.bin/prettier --write --cache --cache-strategy metadata . '!**/dist' '!**/*.ts' '!**/*.mts' '!**/*.cts' '!**/*.js' '!**/*.mjs' '!**/*.cjs' +./node_modules/.bin/prettier --write --cache --cache-strategy metadata . diff --git a/scripts/lint b/scripts/lint index 3ffb78a..1f53254 100755 --- a/scripts/lint +++ b/scripts/lint @@ -4,6 +4,9 @@ set -e cd "$(dirname "$0")/.." +echo "==> Running prettier --check" +./node_modules/.bin/prettier --check . + echo "==> Running eslint" ./node_modules/.bin/eslint . diff --git a/src/internal/types.ts b/src/internal/types.ts index b668dfc..a050513 100644 --- a/src/internal/types.ts +++ b/src/internal/types.ts @@ -40,7 +40,6 @@ type OverloadedParameters = : T extends (...args: infer A) => unknown ? A : never; -/* eslint-disable */ /** * These imports attempt to get types from a parent package's dependencies. * Unresolved bare specifiers can trigger [automatic type acquisition][1] in some projects, which @@ -63,19 +62,18 @@ type OverloadedParameters = * * [1]: https://www.typescriptlang.org/tsconfig/#typeAcquisition */ -/** @ts-ignore For users with \@types/node */ +/** @ts-ignore For users with \@types/node */ /* prettier-ignore */ type UndiciTypesRequestInit = NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny; -/** @ts-ignore For users with undici */ +/** @ts-ignore For users with undici */ /* prettier-ignore */ type UndiciRequestInit = NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny; -/** @ts-ignore For users with \@types/bun */ +/** @ts-ignore For users with \@types/bun */ /* prettier-ignore */ type BunRequestInit = globalThis.FetchRequestInit; -/** @ts-ignore For users with node-fetch@2 */ +/** @ts-ignore For users with node-fetch@2 */ /* prettier-ignore */ type NodeFetch2RequestInit = NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny; -/** @ts-ignore For users with node-fetch@3, doesn't need file extension because types are at ./@types/index.d.ts */ +/** @ts-ignore For users with node-fetch@3, doesn't need file extension because types are at ./@types/index.d.ts */ /* prettier-ignore */ type NodeFetch3RequestInit = NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny; -/** @ts-ignore For users who use Deno */ +/** @ts-ignore For users who use Deno */ /* prettier-ignore */ type FetchRequestInit = NonNullable[1]>; -/* eslint-enable */ type RequestInits = | NotAny diff --git a/yarn.lock b/yarn.lock index f6eae3c..18e7cbd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -709,11 +709,6 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@pkgr/core@^0.2.4": - version "0.2.4" - resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.4.tgz#d897170a2b0ba51f78a099edccd968f7b103387c" - integrity sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw== - "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -1515,14 +1510,6 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-plugin-prettier@^5.4.1: - version "5.4.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.1.tgz#99b55d7dd70047886b2222fdd853665f180b36af" - integrity sha512-9dF+KuU/Ilkq27A8idRP7N2DH8iUR6qXcjF3FR2wETY21PZdBrIjwCau8oboyGj9b7etWmTGEeM8e7oOed6ZWg== - dependencies: - prettier-linter-helpers "^1.0.0" - synckit "^0.11.7" - eslint-plugin-unused-imports@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz#62ddc7446ccbf9aa7b6f1f0b00a980423cda2738" @@ -1674,11 +1661,6 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-diff@^1.1.2: - version "1.3.0" - resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" - integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== - fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" @@ -2841,13 +2823,6 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier-linter-helpers@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" - integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== - dependencies: - fast-diff "^1.1.2" - prettier@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.1.1.tgz#6ba9f23165d690b6cbdaa88cb0807278f7019848" @@ -3144,13 +3119,6 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -synckit@^0.11.7: - version "0.11.8" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.8.tgz#b2aaae998a4ef47ded60773ad06e7cb821f55457" - integrity sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A== - dependencies: - "@pkgr/core" "^0.2.4" - test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" From 2f754e09f1e73319bd465857d831e6d6d1742d71 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 07:41:23 +0000 Subject: [PATCH 28/54] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 2750c56..ebf5bb6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-a1351fe18005248184e11e2c1d6e4a8df7ecfd0092f9fcfeabaa025a2c5b4986.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-a1351fe18005248184e11e2c1d6e4a8df7ecfd0092f9fcfeabaa025a2c5b4986.yml openapi_spec_hash: 3633a7fdec0e4c3d72dcbadeebaea907 config_hash: 6f1858ca62cea01f7c1c4427b9263c25 From 4380baa10dc67f3004cfd46963ebc61384bb1f54 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 04:38:45 +0000 Subject: [PATCH 29/54] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index ebf5bb6..740f0ed 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-a1351fe18005248184e11e2c1d6e4a8df7ecfd0092f9fcfeabaa025a2c5b4986.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-2f1a1daca1014db99ea15fb1caa33e8c7bbeb5ce8dfe3c438f54f85994d16cb3.yml openapi_spec_hash: 3633a7fdec0e4c3d72dcbadeebaea907 config_hash: 6f1858ca62cea01f7c1c4427b9263c25 From 5df33b94a8781fe7de5c54c232cc3dd556d80a6b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 8 May 2026 05:02:48 +0000 Subject: [PATCH 30/54] chore(internal): codegen related update --- src/internal/utils/log.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/internal/utils/log.ts b/src/internal/utils/log.ts index 1726922..a2a0730 100644 --- a/src/internal/utils/log.ts +++ b/src/internal/utils/log.ts @@ -107,6 +107,8 @@ export const formatRequestDetails = (details: { name, ( name.toLowerCase() === 'authorization' || + name.toLowerCase() === 'api-key' || + name.toLowerCase() === 'x-api-key' || name.toLowerCase() === 'cookie' || name.toLowerCase() === 'set-cookie' ) ? From 1cb7ba3cc2f50a631693aa22fea9cb83c14ab270 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 8 May 2026 08:27:55 +0000 Subject: [PATCH 31/54] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 740f0ed..0f2f691 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-2f1a1daca1014db99ea15fb1caa33e8c7bbeb5ce8dfe3c438f54f85994d16cb3.yml -openapi_spec_hash: 3633a7fdec0e4c3d72dcbadeebaea907 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-b4c4ec2aa5631a32336e52d2c64dc8dc5bfe262f8630e21900ccbab702071d50.yml +openapi_spec_hash: 12a39212e6991daf3731f164dad85455 config_hash: 6f1858ca62cea01f7c1c4427b9263c25 From b70eaba3b4daf2159bd9107c932954631ff5d3ca Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 02:58:45 +0000 Subject: [PATCH 32/54] chore(internal): codegen related update --- .github/workflows/ci.yml | 14 +++++++------- .github/workflows/publish-npm.yml | 4 ++-- .github/workflows/release-doctor.yml | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b770d07..05b5c45 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,10 +21,10 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/unlayer-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '20' @@ -43,10 +43,10 @@ jobs: contents: read id-token: write steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '20' @@ -61,7 +61,7 @@ jobs: github.repository == 'stainless-sdks/unlayer-typescript' && !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: core.setOutput('github_token', await core.getIDToken()); @@ -80,10 +80,10 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/unlayer-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '20' diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index 8d3b32c..59442f8 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -14,10 +14,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node - uses: actions/setup-node@v3 + uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1 with: node-version: '20' diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 5ea6b81..4b829da 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -12,7 +12,7 @@ jobs: if: github.repository == 'unlayer/unlayer-typescript' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Check release environment run: | From ebe2eb769f8ec18596f18a547efb5c750dc53230 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 02:55:17 +0000 Subject: [PATCH 33/54] chore(internal): codegen related update --- package.json | 2 +- tests/uploads.test.ts | 1 - yarn.lock | 6 +++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 581b952..a9085e4 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "publint": "^0.2.12", "ts-jest": "^29.1.0", "ts-node": "^10.5.0", - "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz", + "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.11/tsc-multi.tgz", "tsconfig-paths": "^4.0.0", "tslib": "^2.8.1", "typescript": "5.8.3", diff --git a/tests/uploads.test.ts b/tests/uploads.test.ts index 7765432..a29d9c2 100644 --- a/tests/uploads.test.ts +++ b/tests/uploads.test.ts @@ -1,7 +1,6 @@ import fs from 'fs'; import type { ResponseLike } from '@unlayer/sdk/internal/to-file'; import { toFile } from '@unlayer/sdk/core/uploads'; -import { File } from 'node:buffer'; class MyClass { name: string = 'foo'; diff --git a/yarn.lock b/yarn.lock index 18e7cbd..00842e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3192,9 +3192,9 @@ ts-node@^10.5.0: v8-compile-cache-lib "^3.0.0" yn "3.1.1" -"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz": - version "1.1.9" - resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz#777f6f5d9e26bf0e94e5170990dd3a841d6707cd" +"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.11/tsc-multi.tgz": + version "1.1.11" + resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.11/tsc-multi.tgz#010247051be13b55abdc98f787c017285149f4f2" dependencies: debug "^4.3.7" fast-glob "^3.3.2" From 58c42c366680e518d1d9fa45e67ee1c915ebb5c7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 13:19:54 +0000 Subject: [PATCH 34/54] feat(api): api update --- .stats.yml | 8 +- api.md | 46 ++--- src/client.ts | 16 +- src/resources/ai.ts | 3 - src/resources/ai/ai.ts | 19 -- src/resources/ai/index.ts | 4 - src/resources/convert.ts | 3 - src/resources/convert/convert.ts | 29 --- src/resources/convert/index.ts | 13 -- src/resources/convert/simple-to-full.ts | 66 ------- src/resources/index.ts | 4 +- src/resources/templates.ts | 112 +----------- .../convert-full-to-simple.ts} | 29 +-- .../templates/convert-simple-to-full.ts | 69 ++++++++ src/resources/{ai => templates}/generate.ts | 5 +- src/resources/templates/import.ts | 118 +++++++++++++ src/resources/templates/index.ts | 22 +++ src/resources/templates/templates.ts | 165 ++++++++++++++++++ .../convert-full-to-simple.test.ts} | 6 +- .../convert-simple-to-full.test.ts} | 6 +- .../{ai => templates}/generate.test.ts | 4 +- tests/api-resources/templates/import.test.ts | 41 +++++ .../{ => templates}/templates.test.ts | 0 23 files changed, 478 insertions(+), 310 deletions(-) delete mode 100644 src/resources/ai.ts delete mode 100644 src/resources/ai/ai.ts delete mode 100644 src/resources/ai/index.ts delete mode 100644 src/resources/convert.ts delete mode 100644 src/resources/convert/convert.ts delete mode 100644 src/resources/convert/index.ts delete mode 100644 src/resources/convert/simple-to-full.ts rename src/resources/{convert/full-to-simple.ts => templates/convert-full-to-simple.ts} (51%) create mode 100644 src/resources/templates/convert-simple-to-full.ts rename src/resources/{ai => templates}/generate.ts (95%) create mode 100644 src/resources/templates/import.ts create mode 100644 src/resources/templates/index.ts create mode 100644 src/resources/templates/templates.ts rename tests/api-resources/{convert/full-to-simple.test.ts => templates/convert-full-to-simple.test.ts} (80%) rename tests/api-resources/{convert/simple-to-full.test.ts => templates/convert-simple-to-full.test.ts} (81%) rename tests/api-resources/{ai => templates}/generate.test.ts (92%) create mode 100644 tests/api-resources/templates/import.test.ts rename tests/api-resources/{ => templates}/templates.test.ts (100%) diff --git a/.stats.yml b/.stats.yml index 0f2f691..f94ad81 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-b4c4ec2aa5631a32336e52d2c64dc8dc5bfe262f8630e21900ccbab702071d50.yml -openapi_spec_hash: 12a39212e6991daf3731f164dad85455 -config_hash: 6f1858ca62cea01f7c1c4427b9263c25 +configured_endpoints: 9 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-e87049219505c250c95423461c7fb7aa280a3238f98577973c687c1030da26bf.yml +openapi_spec_hash: 613060b45d55c1ab27b70973e4402dfd +config_hash: 2029dabcdae1b263de41e1a890ee90d8 diff --git a/api.md b/api.md index c4cfb8a..9eef13a 100644 --- a/api.md +++ b/api.md @@ -1,58 +1,64 @@ -# AI - -## Generate +# Projects Types: -- GenerateCreateResponse +- ProjectRetrieveResponse Methods: -- client.ai.generate.create({ ...params }) -> GenerateCreateResponse +- client.projects.retrieve(id) -> ProjectRetrieveResponse -# Convert +# Templates + +Types: -## FullToSimple +- TemplateRetrieveResponse +- TemplateListResponse + +Methods: + +- client.templates.retrieve(id, { ...params }) -> TemplateRetrieveResponse +- client.templates.list({ ...params }) -> TemplateListResponsesCursorPage + +## ConvertFullToSimple Types: -- FullToSimpleCreateResponse +- ConvertFullToSimpleCreateResponse Methods: -- client.convert.fullToSimple.create({ ...params }) -> FullToSimpleCreateResponse +- client.templates.convertFullToSimple.create({ ...params }) -> ConvertFullToSimpleCreateResponse -## SimpleToFull +## ConvertSimpleToFull Types: -- SimpleToFullCreateResponse +- ConvertSimpleToFullCreateResponse Methods: -- client.convert.simpleToFull.create({ ...params }) -> SimpleToFullCreateResponse +- client.templates.convertSimpleToFull.create({ ...params }) -> ConvertSimpleToFullCreateResponse -# Projects +## Generate Types: -- ProjectRetrieveResponse +- GenerateCreateResponse Methods: -- client.projects.retrieve(id) -> ProjectRetrieveResponse +- client.templates.generate.create({ ...params }) -> GenerateCreateResponse -# Templates +## Import Types: -- TemplateRetrieveResponse -- TemplateListResponse +- ImportCreateResponse Methods: -- client.templates.retrieve(id, { ...params }) -> TemplateRetrieveResponse -- client.templates.list({ ...params }) -> TemplateListResponsesCursorPage +- client.templates.import.create({ ...params }) -> ImportCreateResponse # Workspaces diff --git a/src/client.ts b/src/client.ts index aeee2f8..676adad 100644 --- a/src/client.ts +++ b/src/client.ts @@ -20,6 +20,7 @@ import * as Uploads from './core/uploads'; import * as API from './resources/index'; import { APIPromise } from './core/api-promise'; import { ProjectRetrieveResponse, Projects } from './resources/projects'; +import { WorkspaceListResponse, WorkspaceRetrieveResponse, Workspaces } from './resources/workspaces'; import { TemplateListParams, TemplateListResponse, @@ -27,10 +28,7 @@ import { TemplateRetrieveParams, TemplateRetrieveResponse, Templates, -} from './resources/templates'; -import { WorkspaceListResponse, WorkspaceRetrieveResponse, Workspaces } from './resources/workspaces'; -import { AI } from './resources/ai/ai'; -import { Convert } from './resources/convert/convert'; +} from './resources/templates/templates'; import { type Fetch } from './internal/builtin-types'; import { HeadersLike, NullableHeaders, buildHeaders } from './internal/headers'; import { FinalRequestOptions, RequestOptions } from './internal/request-options'; @@ -814,14 +812,12 @@ export class Unlayer { static toFile = Uploads.toFile; - ai: API.AI = new API.AI(this); - convert: API.Convert = new API.Convert(this); /** * Project details and configuration. */ projects: API.Projects = new API.Projects(this); /** - * Template management and retrieval. + * Template management — list, retrieve, generate, import, export, and convert designs. */ templates: API.Templates = new API.Templates(this); /** @@ -830,8 +826,6 @@ export class Unlayer { workspaces: API.Workspaces = new API.Workspaces(this); } -Unlayer.AI = AI; -Unlayer.Convert = Convert; Unlayer.Projects = Projects; Unlayer.Templates = Templates; Unlayer.Workspaces = Workspaces; @@ -842,10 +836,6 @@ export declare namespace Unlayer { export import CursorPage = Pagination.CursorPage; export { type CursorPageParams as CursorPageParams, type CursorPageResponse as CursorPageResponse }; - export { AI as AI }; - - export { Convert as Convert }; - export { Projects as Projects, type ProjectRetrieveResponse as ProjectRetrieveResponse }; export { diff --git a/src/resources/ai.ts b/src/resources/ai.ts deleted file mode 100644 index 6bea0b9..0000000 --- a/src/resources/ai.ts +++ /dev/null @@ -1,3 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -export * from './ai/index'; diff --git a/src/resources/ai/ai.ts b/src/resources/ai/ai.ts deleted file mode 100644 index c94d536..0000000 --- a/src/resources/ai/ai.ts +++ /dev/null @@ -1,19 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { APIResource } from '../../core/resource'; -import * as GenerateAPI from './generate'; -import { Generate, GenerateCreateParams, GenerateCreateResponse } from './generate'; - -export class AI extends APIResource { - generate: GenerateAPI.Generate = new GenerateAPI.Generate(this._client); -} - -AI.Generate = Generate; - -export declare namespace AI { - export { - Generate as Generate, - type GenerateCreateResponse as GenerateCreateResponse, - type GenerateCreateParams as GenerateCreateParams, - }; -} diff --git a/src/resources/ai/index.ts b/src/resources/ai/index.ts deleted file mode 100644 index 9a970e8..0000000 --- a/src/resources/ai/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -export { AI } from './ai'; -export { Generate, type GenerateCreateResponse, type GenerateCreateParams } from './generate'; diff --git a/src/resources/convert.ts b/src/resources/convert.ts deleted file mode 100644 index 1334f91..0000000 --- a/src/resources/convert.ts +++ /dev/null @@ -1,3 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -export * from './convert/index'; diff --git a/src/resources/convert/convert.ts b/src/resources/convert/convert.ts deleted file mode 100644 index d7930c4..0000000 --- a/src/resources/convert/convert.ts +++ /dev/null @@ -1,29 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { APIResource } from '../../core/resource'; -import * as FullToSimpleAPI from './full-to-simple'; -import { FullToSimple, FullToSimpleCreateParams, FullToSimpleCreateResponse } from './full-to-simple'; -import * as SimpleToFullAPI from './simple-to-full'; -import { SimpleToFull, SimpleToFullCreateParams, SimpleToFullCreateResponse } from './simple-to-full'; - -export class Convert extends APIResource { - fullToSimple: FullToSimpleAPI.FullToSimple = new FullToSimpleAPI.FullToSimple(this._client); - simpleToFull: SimpleToFullAPI.SimpleToFull = new SimpleToFullAPI.SimpleToFull(this._client); -} - -Convert.FullToSimple = FullToSimple; -Convert.SimpleToFull = SimpleToFull; - -export declare namespace Convert { - export { - FullToSimple as FullToSimple, - type FullToSimpleCreateResponse as FullToSimpleCreateResponse, - type FullToSimpleCreateParams as FullToSimpleCreateParams, - }; - - export { - SimpleToFull as SimpleToFull, - type SimpleToFullCreateResponse as SimpleToFullCreateResponse, - type SimpleToFullCreateParams as SimpleToFullCreateParams, - }; -} diff --git a/src/resources/convert/index.ts b/src/resources/convert/index.ts deleted file mode 100644 index 833a9fc..0000000 --- a/src/resources/convert/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -export { Convert } from './convert'; -export { - FullToSimple, - type FullToSimpleCreateResponse, - type FullToSimpleCreateParams, -} from './full-to-simple'; -export { - SimpleToFull, - type SimpleToFullCreateResponse, - type SimpleToFullCreateParams, -} from './simple-to-full'; diff --git a/src/resources/convert/simple-to-full.ts b/src/resources/convert/simple-to-full.ts deleted file mode 100644 index 2790174..0000000 --- a/src/resources/convert/simple-to-full.ts +++ /dev/null @@ -1,66 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { APIResource } from '../../core/resource'; -import { APIPromise } from '../../core/api-promise'; -import { RequestOptions } from '../../internal/request-options'; - -/** - * Design schema conversion between Full and Simple formats. - */ -export class SimpleToFull extends APIResource { - /** - * Convert design json from Simple to Full schema. - */ - create(body: SimpleToFullCreateParams, options?: RequestOptions): APIPromise { - return this._client.post('/v3/convert/simple-to-full', { body, ...options }); - } -} - -export interface SimpleToFullCreateResponse { - data?: SimpleToFullCreateResponse.Data; - - success?: true; -} - -export namespace SimpleToFullCreateResponse { - export interface Data { - design?: { [key: string]: unknown }; - } -} - -export interface SimpleToFullCreateParams { - design: SimpleToFullCreateParams.Design; - - displayMode?: 'email' | 'web' | 'popup' | 'document'; - - includeDefaultValues?: boolean; -} - -export namespace SimpleToFullCreateParams { - export interface Design { - body: { [key: string]: unknown }; - - _conversion?: Design._Conversion; - - counters?: { [key: string]: unknown }; - - schemaVersion?: number; - - [k: string]: unknown; - } - - export namespace Design { - export interface _Conversion { - data?: string; - - version?: number; - } - } -} - -export declare namespace SimpleToFull { - export { - type SimpleToFullCreateResponse as SimpleToFullCreateResponse, - type SimpleToFullCreateParams as SimpleToFullCreateParams, - }; -} diff --git a/src/resources/index.ts b/src/resources/index.ts index 07830a5..6ca716c 100644 --- a/src/resources/index.ts +++ b/src/resources/index.ts @@ -1,7 +1,5 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -export { AI } from './ai/ai'; -export { Convert } from './convert/convert'; export { Projects, type ProjectRetrieveResponse } from './projects'; export { Templates, @@ -10,5 +8,5 @@ export { type TemplateRetrieveParams, type TemplateListParams, type TemplateListResponsesCursorPage, -} from './templates'; +} from './templates/templates'; export { Workspaces, type WorkspaceRetrieveResponse, type WorkspaceListResponse } from './workspaces'; diff --git a/src/resources/templates.ts b/src/resources/templates.ts index 8d1cbb8..cf710e0 100644 --- a/src/resources/templates.ts +++ b/src/resources/templates.ts @@ -1,113 +1,3 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { APIResource } from '../core/resource'; -import { APIPromise } from '../core/api-promise'; -import { CursorPage, type CursorPageParams, PagePromise } from '../core/pagination'; -import { RequestOptions } from '../internal/request-options'; -import { path } from '../internal/utils/path'; - -/** - * Template management and retrieval. - */ -export class Templates extends APIResource { - /** - * Get template by ID. - */ - retrieve( - id: string, - query: TemplateRetrieveParams | null | undefined = {}, - options?: RequestOptions, - ): APIPromise { - return this._client.get(path`/v3/templates/${id}`, { query, ...options }); - } - - /** - * List templates with cursor-based pagination. Returns templates in descending - * order by update time. - */ - list( - query: TemplateListParams | null | undefined = {}, - options?: RequestOptions, - ): PagePromise { - return this._client.getAPIList('/v3/templates', CursorPage, { query, ...options }); - } -} - -export type TemplateListResponsesCursorPage = CursorPage; - -export interface TemplateRetrieveResponse { - data?: TemplateRetrieveResponse.Data; -} - -export namespace TemplateRetrieveResponse { - export interface Data { - id?: string; - - createdAt?: string; - - design?: { [key: string]: unknown }; - - displayMode?: 'email' | 'web' | 'document'; - - html?: string | null; - - name?: string; - - updatedAt?: string; - } -} - -export interface TemplateListResponse { - /** - * Template ID - */ - id?: string; - - createdAt?: string; - - /** - * Template type/display mode - */ - displayMode?: 'email' | 'web' | 'document'; - - /** - * Template name - */ - name?: string; - - updatedAt?: string; -} - -export interface TemplateRetrieveParams { - /** - * The project ID (required for PAT auth, auto-resolved for API key auth) - */ - projectId?: string; -} - -export interface TemplateListParams extends CursorPageParams { - /** - * Filter by template type - */ - displayMode?: 'email' | 'web' | 'document'; - - /** - * Filter by name (case-insensitive search) - */ - name?: string; - - /** - * The project ID to list templates for - */ - projectId?: string; -} - -export declare namespace Templates { - export { - type TemplateRetrieveResponse as TemplateRetrieveResponse, - type TemplateListResponse as TemplateListResponse, - type TemplateListResponsesCursorPage as TemplateListResponsesCursorPage, - type TemplateRetrieveParams as TemplateRetrieveParams, - type TemplateListParams as TemplateListParams, - }; -} +export * from './templates/index'; diff --git a/src/resources/convert/full-to-simple.ts b/src/resources/templates/convert-full-to-simple.ts similarity index 51% rename from src/resources/convert/full-to-simple.ts rename to src/resources/templates/convert-full-to-simple.ts index ceb1e69..c8b3e8c 100644 --- a/src/resources/convert/full-to-simple.ts +++ b/src/resources/templates/convert-full-to-simple.ts @@ -5,31 +5,34 @@ import { APIPromise } from '../../core/api-promise'; import { RequestOptions } from '../../internal/request-options'; /** - * Design schema conversion between Full and Simple formats. + * Template management — list, retrieve, generate, import, export, and convert designs. */ -export class FullToSimple extends APIResource { +export class ConvertFullToSimple extends APIResource { /** * Convert design json from Full to Simple schema. */ - create(body: FullToSimpleCreateParams, options?: RequestOptions): APIPromise { - return this._client.post('/v3/convert/full-to-simple', { body, ...options }); + create( + body: ConvertFullToSimpleCreateParams, + options?: RequestOptions, + ): APIPromise { + return this._client.post('/v3/templates/convert/full-to-simple', { body, ...options }); } } -export interface FullToSimpleCreateResponse { - data?: FullToSimpleCreateResponse.Data; +export interface ConvertFullToSimpleCreateResponse { + data?: ConvertFullToSimpleCreateResponse.Data; success?: true; } -export namespace FullToSimpleCreateResponse { +export namespace ConvertFullToSimpleCreateResponse { export interface Data { design?: { [key: string]: unknown }; } } -export interface FullToSimpleCreateParams { - design: FullToSimpleCreateParams.Design; +export interface ConvertFullToSimpleCreateParams { + design: ConvertFullToSimpleCreateParams.Design; displayMode?: 'email' | 'web' | 'popup' | 'document'; @@ -42,7 +45,7 @@ export interface FullToSimpleCreateParams { includeDefaultValues?: boolean; } -export namespace FullToSimpleCreateParams { +export namespace ConvertFullToSimpleCreateParams { export interface Design { body: { [key: string]: unknown }; @@ -54,9 +57,9 @@ export namespace FullToSimpleCreateParams { } } -export declare namespace FullToSimple { +export declare namespace ConvertFullToSimple { export { - type FullToSimpleCreateResponse as FullToSimpleCreateResponse, - type FullToSimpleCreateParams as FullToSimpleCreateParams, + type ConvertFullToSimpleCreateResponse as ConvertFullToSimpleCreateResponse, + type ConvertFullToSimpleCreateParams as ConvertFullToSimpleCreateParams, }; } diff --git a/src/resources/templates/convert-simple-to-full.ts b/src/resources/templates/convert-simple-to-full.ts new file mode 100644 index 0000000..c1c0af0 --- /dev/null +++ b/src/resources/templates/convert-simple-to-full.ts @@ -0,0 +1,69 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; + +/** + * Template management — list, retrieve, generate, import, export, and convert designs. + */ +export class ConvertSimpleToFull extends APIResource { + /** + * Convert design json from Simple to Full schema. + */ + create( + body: ConvertSimpleToFullCreateParams, + options?: RequestOptions, + ): APIPromise { + return this._client.post('/v3/templates/convert/simple-to-full', { body, ...options }); + } +} + +export interface ConvertSimpleToFullCreateResponse { + data?: ConvertSimpleToFullCreateResponse.Data; + + success?: true; +} + +export namespace ConvertSimpleToFullCreateResponse { + export interface Data { + design?: { [key: string]: unknown }; + } +} + +export interface ConvertSimpleToFullCreateParams { + design: ConvertSimpleToFullCreateParams.Design; + + displayMode?: 'email' | 'web' | 'popup' | 'document'; + + includeDefaultValues?: boolean; +} + +export namespace ConvertSimpleToFullCreateParams { + export interface Design { + body: { [key: string]: unknown }; + + _conversion?: Design._Conversion; + + counters?: { [key: string]: unknown }; + + schemaVersion?: number; + + [k: string]: unknown; + } + + export namespace Design { + export interface _Conversion { + data?: string; + + version?: number; + } + } +} + +export declare namespace ConvertSimpleToFull { + export { + type ConvertSimpleToFullCreateResponse as ConvertSimpleToFullCreateResponse, + type ConvertSimpleToFullCreateParams as ConvertSimpleToFullCreateParams, + }; +} diff --git a/src/resources/ai/generate.ts b/src/resources/templates/generate.ts similarity index 95% rename from src/resources/ai/generate.ts rename to src/resources/templates/generate.ts index b55824c..e5f73b6 100644 --- a/src/resources/ai/generate.ts +++ b/src/resources/templates/generate.ts @@ -4,6 +4,9 @@ import { APIResource } from '../../core/resource'; import { APIPromise } from '../../core/api-promise'; import { RequestOptions } from '../../internal/request-options'; +/** + * Template management — list, retrieve, generate, import, export, and convert designs. + */ export class Generate extends APIResource { /** * Generate, modify, or import an Unlayer design using AI. Provide typed input @@ -11,7 +14,7 @@ export class Generate extends APIResource { */ create(params: GenerateCreateParams, options?: RequestOptions): APIPromise { const { projectId, ...body } = params; - return this._client.post('/v3/ai/generate', { query: { projectId }, body, ...options }); + return this._client.post('/v3/templates/generate', { query: { projectId }, body, ...options }); } } diff --git a/src/resources/templates/import.ts b/src/resources/templates/import.ts new file mode 100644 index 0000000..66c5f87 --- /dev/null +++ b/src/resources/templates/import.ts @@ -0,0 +1,118 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; + +/** + * Template management — list, retrieve, generate, import, export, and convert designs. + */ +export class Import extends APIResource { + /** + * Import an existing template from HTML or an image (URL or base64) and return the + * resulting Unlayer design JSON. No template DB entry is created. + */ + create(params: ImportCreateParams, options?: RequestOptions): APIPromise { + const { projectId, ...body } = params; + return this._client.post('/v3/templates/import', { query: { projectId }, body, ...options }); + } +} + +/** + * Successfully imported template + */ +export interface ImportCreateResponse { + id?: string; + + model?: string; + + output?: ImportCreateResponse.Output; + + provider?: string; + + usage?: ImportCreateResponse.Usage; +} + +export namespace ImportCreateResponse { + export interface Output { + blockType?: string; + + /** + * Imported design data + */ + data?: { [key: string]: unknown }; + + type?: string; + } + + export interface Usage { + cachedInputTokens?: number; + + inputTokens?: number; + + outputTokens?: number; + + reasoningTokens?: number; + + totalTokens?: number; + } +} + +export interface ImportCreateParams { + /** + * Body param: Display mode for the imported design + */ + displayMode: 'email' | 'web' | 'popup' | 'document'; + + /** + * Body param: Array of input parts. Must contain exactly one "html" or "image" + * part; may also contain one or more "text" parts with optional instructions. + */ + input: Array; + + /** + * Query param: The project ID (required for PAT auth, auto-resolved for API key + * auth) + */ + projectId?: string; + + /** + * Body param: AI model to use, in provider/model format. Optional — defaults to + * anthropic/claude-opus-4-6. + */ + model?: 'anthropic/claude-opus-4-6' | 'openai/gpt-5.2'; +} + +export namespace ImportCreateParams { + export interface Input { + /** + * The type of input part. "html" or "image" carries the source content; "text" + * carries optional instructions to apply during import. + */ + type: 'html' | 'image' | 'text'; + + /** + * Base64 image data URL, e.g. "data:image/png;base64,…" (for type: "image") + */ + data?: string; + + /** + * HTML string to import (for type: "html") + */ + html?: string; + + /** + * Optional natural-language instructions to apply during import (for type: "text") + */ + text?: string; + + /** + * Image URL to import (for type: "image") + */ + url?: string; + } +} + +export declare namespace Import { + export { type ImportCreateResponse as ImportCreateResponse, type ImportCreateParams as ImportCreateParams }; +} diff --git a/src/resources/templates/index.ts b/src/resources/templates/index.ts new file mode 100644 index 0000000..b4f6e4e --- /dev/null +++ b/src/resources/templates/index.ts @@ -0,0 +1,22 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export { + ConvertFullToSimple, + type ConvertFullToSimpleCreateResponse, + type ConvertFullToSimpleCreateParams, +} from './convert-full-to-simple'; +export { + ConvertSimpleToFull, + type ConvertSimpleToFullCreateResponse, + type ConvertSimpleToFullCreateParams, +} from './convert-simple-to-full'; +export { Generate, type GenerateCreateResponse, type GenerateCreateParams } from './generate'; +export { Import, type ImportCreateResponse, type ImportCreateParams } from './import'; +export { + Templates, + type TemplateRetrieveResponse, + type TemplateListResponse, + type TemplateRetrieveParams, + type TemplateListParams, + type TemplateListResponsesCursorPage, +} from './templates'; diff --git a/src/resources/templates/templates.ts b/src/resources/templates/templates.ts new file mode 100644 index 0000000..fd15c00 --- /dev/null +++ b/src/resources/templates/templates.ts @@ -0,0 +1,165 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import * as ConvertFullToSimpleAPI from './convert-full-to-simple'; +import { + ConvertFullToSimple, + ConvertFullToSimpleCreateParams, + ConvertFullToSimpleCreateResponse, +} from './convert-full-to-simple'; +import * as ConvertSimpleToFullAPI from './convert-simple-to-full'; +import { + ConvertSimpleToFull, + ConvertSimpleToFullCreateParams, + ConvertSimpleToFullCreateResponse, +} from './convert-simple-to-full'; +import * as GenerateAPI from './generate'; +import { Generate, GenerateCreateParams, GenerateCreateResponse } from './generate'; +import * as ImportAPI from './import'; +import { Import, ImportCreateParams, ImportCreateResponse } from './import'; +import { APIPromise } from '../../core/api-promise'; +import { CursorPage, type CursorPageParams, PagePromise } from '../../core/pagination'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +/** + * Template management — list, retrieve, generate, import, export, and convert designs. + */ +export class Templates extends APIResource { + convertFullToSimple: ConvertFullToSimpleAPI.ConvertFullToSimple = + new ConvertFullToSimpleAPI.ConvertFullToSimple(this._client); + convertSimpleToFull: ConvertSimpleToFullAPI.ConvertSimpleToFull = + new ConvertSimpleToFullAPI.ConvertSimpleToFull(this._client); + generate: GenerateAPI.Generate = new GenerateAPI.Generate(this._client); + import: ImportAPI.Import = new ImportAPI.Import(this._client); + + /** + * Get template by ID. + */ + retrieve( + id: string, + query: TemplateRetrieveParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.get(path`/v3/templates/${id}`, { query, ...options }); + } + + /** + * List templates with cursor-based pagination. Returns templates in descending + * order by update time. + */ + list( + query: TemplateListParams | null | undefined = {}, + options?: RequestOptions, + ): PagePromise { + return this._client.getAPIList('/v3/templates', CursorPage, { query, ...options }); + } +} + +export type TemplateListResponsesCursorPage = CursorPage; + +export interface TemplateRetrieveResponse { + data?: TemplateRetrieveResponse.Data; +} + +export namespace TemplateRetrieveResponse { + export interface Data { + id?: string; + + createdAt?: string; + + design?: { [key: string]: unknown }; + + displayMode?: 'email' | 'web' | 'document'; + + html?: string | null; + + name?: string; + + updatedAt?: string; + } +} + +export interface TemplateListResponse { + /** + * Template ID + */ + id?: string; + + createdAt?: string; + + /** + * Template type/display mode + */ + displayMode?: 'email' | 'web' | 'document'; + + /** + * Template name + */ + name?: string; + + updatedAt?: string; +} + +export interface TemplateRetrieveParams { + /** + * The project ID (required for PAT auth, auto-resolved for API key auth) + */ + projectId?: string; +} + +export interface TemplateListParams extends CursorPageParams { + /** + * Filter by template type + */ + displayMode?: 'email' | 'web' | 'document'; + + /** + * Filter by name (case-insensitive search) + */ + name?: string; + + /** + * The project ID to list templates for + */ + projectId?: string; +} + +Templates.ConvertFullToSimple = ConvertFullToSimple; +Templates.ConvertSimpleToFull = ConvertSimpleToFull; +Templates.Generate = Generate; +Templates.Import = Import; + +export declare namespace Templates { + export { + type TemplateRetrieveResponse as TemplateRetrieveResponse, + type TemplateListResponse as TemplateListResponse, + type TemplateListResponsesCursorPage as TemplateListResponsesCursorPage, + type TemplateRetrieveParams as TemplateRetrieveParams, + type TemplateListParams as TemplateListParams, + }; + + export { + ConvertFullToSimple as ConvertFullToSimple, + type ConvertFullToSimpleCreateResponse as ConvertFullToSimpleCreateResponse, + type ConvertFullToSimpleCreateParams as ConvertFullToSimpleCreateParams, + }; + + export { + ConvertSimpleToFull as ConvertSimpleToFull, + type ConvertSimpleToFullCreateResponse as ConvertSimpleToFullCreateResponse, + type ConvertSimpleToFullCreateParams as ConvertSimpleToFullCreateParams, + }; + + export { + Generate as Generate, + type GenerateCreateResponse as GenerateCreateResponse, + type GenerateCreateParams as GenerateCreateParams, + }; + + export { + Import as Import, + type ImportCreateResponse as ImportCreateResponse, + type ImportCreateParams as ImportCreateParams, + }; +} diff --git a/tests/api-resources/convert/full-to-simple.test.ts b/tests/api-resources/templates/convert-full-to-simple.test.ts similarity index 80% rename from tests/api-resources/convert/full-to-simple.test.ts rename to tests/api-resources/templates/convert-full-to-simple.test.ts index 831b256..4936e57 100644 --- a/tests/api-resources/convert/full-to-simple.test.ts +++ b/tests/api-resources/templates/convert-full-to-simple.test.ts @@ -7,9 +7,9 @@ const client = new Unlayer({ baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', }); -describe('resource fullToSimple', () => { +describe('resource convertFullToSimple', () => { test('create: only required params', async () => { - const responsePromise = client.convert.fullToSimple.create({ design: { body: { foo: 'bar' } } }); + const responsePromise = client.templates.convertFullToSimple.create({ design: { body: { foo: 'bar' } } }); const rawResponse = await responsePromise.asResponse(); expect(rawResponse).toBeInstanceOf(Response); const response = await responsePromise; @@ -20,7 +20,7 @@ describe('resource fullToSimple', () => { }); test('create: required and optional params', async () => { - const response = await client.convert.fullToSimple.create({ + const response = await client.templates.convertFullToSimple.create({ design: { body: { foo: 'bar' }, counters: { foo: 'bar' }, diff --git a/tests/api-resources/convert/simple-to-full.test.ts b/tests/api-resources/templates/convert-simple-to-full.test.ts similarity index 81% rename from tests/api-resources/convert/simple-to-full.test.ts rename to tests/api-resources/templates/convert-simple-to-full.test.ts index a5f33bd..4c80e0d 100644 --- a/tests/api-resources/convert/simple-to-full.test.ts +++ b/tests/api-resources/templates/convert-simple-to-full.test.ts @@ -7,9 +7,9 @@ const client = new Unlayer({ baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', }); -describe('resource simpleToFull', () => { +describe('resource convertSimpleToFull', () => { test('create: only required params', async () => { - const responsePromise = client.convert.simpleToFull.create({ design: { body: { foo: 'bar' } } }); + const responsePromise = client.templates.convertSimpleToFull.create({ design: { body: { foo: 'bar' } } }); const rawResponse = await responsePromise.asResponse(); expect(rawResponse).toBeInstanceOf(Response); const response = await responsePromise; @@ -20,7 +20,7 @@ describe('resource simpleToFull', () => { }); test('create: required and optional params', async () => { - const response = await client.convert.simpleToFull.create({ + const response = await client.templates.convertSimpleToFull.create({ design: { body: { foo: 'bar' }, _conversion: { data: 'data', version: 0 }, diff --git a/tests/api-resources/ai/generate.test.ts b/tests/api-resources/templates/generate.test.ts similarity index 92% rename from tests/api-resources/ai/generate.test.ts rename to tests/api-resources/templates/generate.test.ts index 603a967..2e38f11 100644 --- a/tests/api-resources/ai/generate.test.ts +++ b/tests/api-resources/templates/generate.test.ts @@ -9,7 +9,7 @@ const client = new Unlayer({ describe('resource generate', () => { test('create: only required params', async () => { - const responsePromise = client.ai.generate.create({ + const responsePromise = client.templates.generate.create({ displayMode: 'email', input: [{ type: 'text' }], output: { blockType: 'template', type: 'json' }, @@ -24,7 +24,7 @@ describe('resource generate', () => { }); test('create: required and optional params', async () => { - const response = await client.ai.generate.create({ + const response = await client.templates.generate.create({ displayMode: 'email', input: [ { diff --git a/tests/api-resources/templates/import.test.ts b/tests/api-resources/templates/import.test.ts new file mode 100644 index 0000000..7ac14b8 --- /dev/null +++ b/tests/api-resources/templates/import.test.ts @@ -0,0 +1,41 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource import', () => { + test('create: only required params', async () => { + const responsePromise = client.templates.import.create({ + displayMode: 'email', + input: [{ type: 'html' }], + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('create: required and optional params', async () => { + const response = await client.templates.import.create({ + displayMode: 'email', + input: [ + { + type: 'html', + data: 'data', + html: 'html', + text: 'text', + url: 'url', + }, + ], + projectId: 'projectId', + model: 'anthropic/claude-opus-4-6', + }); + }); +}); diff --git a/tests/api-resources/templates.test.ts b/tests/api-resources/templates/templates.test.ts similarity index 100% rename from tests/api-resources/templates.test.ts rename to tests/api-resources/templates/templates.test.ts From 2096590a872261e1d9dfe6b1f70f513cb25d78cd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 25 May 2026 11:49:59 +0000 Subject: [PATCH 35/54] feat(api): api update --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 00842e3..06fc108 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1215,9 +1215,9 @@ baseline-browser-mapping@^2.9.0: integrity sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg== brace-expansion@^2.0.2: - version "2.1.0" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.0.tgz#4f41a41190216ee36067ec381526fe9539c4f0ae" - integrity sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w== + version "2.1.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.1.tgz#c68b1c4111c76aae3a6fba55d496cee10c39dad8" + integrity sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA== dependencies: balanced-match "^1.0.0" From 83ea9936cd068e51773fc25d5b87beb8caa673d0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 02:22:52 +0000 Subject: [PATCH 36/54] feat(api): api update --- .stats.yml | 8 +- api.md | 1 + src/resources/templates/generate.ts | 219 +++++++++++------- src/resources/templates/import.ts | 9 +- .../api-resources/templates/generate.test.ts | 53 +++-- tests/api-resources/templates/import.test.ts | 2 +- 6 files changed, 188 insertions(+), 104 deletions(-) diff --git a/.stats.yml b/.stats.yml index f94ad81..eb0d57f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 9 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-e87049219505c250c95423461c7fb7aa280a3238f98577973c687c1030da26bf.yml -openapi_spec_hash: 613060b45d55c1ab27b70973e4402dfd -config_hash: 2029dabcdae1b263de41e1a890ee90d8 +configured_endpoints: 10 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-35699cec89167aa9ce539f8008695911611f8bdf923234ed701ee3dbc0c5bcd2.yml +openapi_spec_hash: 2ec4eef9500ac0007e1740f431835931 +config_hash: 20e7fbba9d423291aaf676f6a629dcaf diff --git a/api.md b/api.md index 9eef13a..9e7e057 100644 --- a/api.md +++ b/api.md @@ -49,6 +49,7 @@ Types: Methods: - client.templates.generate.create({ ...params }) -> GenerateCreateResponse +- client.templates.generate.retrieve() -> void ## Import diff --git a/src/resources/templates/generate.ts b/src/resources/templates/generate.ts index e5f73b6..a5b06ca 100644 --- a/src/resources/templates/generate.ts +++ b/src/resources/templates/generate.ts @@ -2,55 +2,102 @@ import { APIResource } from '../../core/resource'; import { APIPromise } from '../../core/api-promise'; +import { buildHeaders } from '../../internal/headers'; import { RequestOptions } from '../../internal/request-options'; -/** - * Template management — list, retrieve, generate, import, export, and convert designs. - */ export class Generate extends APIResource { /** - * Generate, modify, or import an Unlayer design using AI. Provide typed input - * parts to describe what to generate. + * Generate or modify an Unlayer design using AI. Send the conversation as + * `messages` (today only the last user message is consumed; earlier turns are + * accepted as chat history) and describe the target with `output.kind` + + * `output.displayMode`. Pass the current canvas state in `context` (full design + * JSON + selection pointer) to modify an existing design. Only `anthropic` and + * `openai` models are supported. To import existing HTML or an image instead, use + * POST /v3/templates/import. */ create(params: GenerateCreateParams, options?: RequestOptions): APIPromise { const { projectId, ...body } = params; return this._client.post('/v3/templates/generate', { query: { projectId }, body, ...options }); } + + retrieve(options?: RequestOptions): APIPromise { + return this._client.get('/v3/templates/generate', { + ...options, + headers: buildHeaders([{ Accept: '*/*' }, options?.headers]), + }); + } } /** - * Successfully generated design + * The generated (or modified) design plus model metadata and optional usage + * metadata. */ export interface GenerateCreateResponse { /** - * AI response ID + * Provider response id for the generation turn. */ id?: string; - model?: string; + /** + * The provider + model that actually produced the output (may differ from the + * requested model after failover). + */ + model?: GenerateCreateResponse.Model; + /** + * The generated output for the requested block. + */ output?: GenerateCreateResponse.Output; - provider?: string; - + /** + * Aggregate token usage for the turn when exposed by the caller. Builder copilot + * endpoints expose it only in local/dev/QA and omit it in staging/production. + */ usage?: GenerateCreateResponse.Usage; } export namespace GenerateCreateResponse { - export interface Output { - blockType?: string; + /** + * The provider + model that actually produced the output (may differ from the + * requested model after failover). + */ + export interface Model { + /** + * Resolved model id, e.g. "claude-opus-4-7". + */ + id?: string; /** - * Generated design data + * e.g. "anthropic", "openai". + */ + provider?: string; + } + + /** + * The generated output for the requested block. + */ + export interface Output { + /** + * The generated design JSON, scoped to the requested kind (the full design for + * template/page/body; the row/column/content/element for narrower kinds). */ data?: { [key: string]: unknown }; - type?: string; + /** + * Echoes the requested `output.kind`. + */ + kind?: string; } + /** + * Aggregate token usage for the turn when exposed by the caller. Builder copilot + * endpoints expose it only in local/dev/QA and omit it in staging/production. + */ export interface Usage { cachedInputTokens?: number; + estimatedCostMicroUsd?: number; + inputTokens?: number; outputTokens?: number; @@ -63,17 +110,16 @@ export namespace GenerateCreateResponse { export interface GenerateCreateParams { /** - * Body param: Display mode for the design - */ - displayMode: 'email' | 'web' | 'popup' | 'document'; - - /** - * Body param: Array of typed input parts (max 50) + * Body param: Conversation messages in chronological order, capped at 10 messages. + * The last `user` message is the prompt for this turn; any earlier + * `user`/`assistant` text turns are forwarded to the model as prior chat context. + * A `user` message may carry a predefined prompt action via `metadata.action.id` + * (e.g. SPELLING, REPHRASE). */ - input: Array; + messages: Array; /** - * Body param: What to generate + * Body param */ output: GenerateCreateParams.Output; @@ -84,91 +130,92 @@ export interface GenerateCreateParams { projectId?: string; /** - * Body param: Editor environment context + * Body param */ context?: GenerateCreateParams.Context; /** - * Body param: AI model to use, in provider/model format. Optional — defaults to - * anthropic/claude-opus-4-6. + * Body param: Reserved for future server-side conversation memory. */ - model?: 'anthropic/claude-opus-4-6' | 'openai/gpt-5.2'; + conversationId?: string; + + /** + * Body param: BCP-47 fallback locale for AI status messages. + */ + locale?: string; + + /** + * Body param: AI model in "provider/id" form, e.g. "anthropic/claude-opus-4-7". + * Optional — server resolves a default per output kind. + */ + model?: string; } export namespace GenerateCreateParams { - export interface Input { - /** - * The type of input part - */ - type: 'text' | 'prompt' | 'json' | 'html' | 'image'; + export interface Message { + content: Array; - /** - * Predefined prompt ID: SPELLING, EXPAND, SUMMARIZE, REPHRASE, FRIENDLY, FORMAL - * (for type: "prompt") - */ - id?: string; + role: 'user' | 'assistant' | 'system'; - /** - * Block type of the design data (for type: "json") - */ - blockType?: string; + metadata?: Message.Metadata; + } - /** - * Existing design data (object, for type: "json") or base64 image data (string, - * for type: "image") - */ - data?: { [key: string]: unknown } | string; + export namespace Message { + export interface Content { + type: 'text' | 'image' | 'file'; - /** - * HTML string to import (for type: "html") - */ - html?: string; + file?: Content.File; - /** - * Design schema version (for type: "json") - */ - schemaVersion?: number; + /** + * URL or data URL of the image + */ + image?: string; - /** - * Natural language prompt (for type: "text") - */ - text?: string; + text?: string; + } - /** - * Image URL to import (for type: "image") - */ - url?: string; + export namespace Content { + export interface File { + url: string; + + mediaType?: string; + + [k: string]: unknown; + } + } + + export interface Metadata { + action?: Metadata.Action; + + [k: string]: unknown; + } + + export namespace Metadata { + export interface Action { + id: string; + + [k: string]: unknown; + } + } } - /** - * What to generate - */ export interface Output { - /** - * The type of design block to generate - */ - blockType: 'template' | 'page' | 'body' | 'content' | 'row' | 'column'; + displayMode: 'email' | 'web' | 'popup' | 'document'; - /** - * Output format — currently only "json" is supported - */ - type: 'json'; + kind: 'template' | 'page' | 'body' | 'header' | 'footer' | 'row' | 'column' | 'content' | 'text'; + + schemaVersion?: number; } - /** - * Editor environment context - */ export interface Context { - /** - * Filter content types available in the generated design - */ availableTools?: Array; - /** - * Custom tool declarations with their options - */ customTools?: Array; + fullDesign?: { [key: string]: unknown } | null; + + selection?: Context.Selection | null; + [k: string]: unknown; } @@ -180,6 +227,16 @@ export namespace GenerateCreateParams { [k: string]: unknown; } + + export interface Selection { + id: string | number; + + collection: 'pages' | 'bodies' | 'rows' | 'columns' | 'contents' | 'headers' | 'footers'; + + value?: string; + + [k: string]: unknown; + } } } diff --git a/src/resources/templates/import.ts b/src/resources/templates/import.ts index 66c5f87..1060f2c 100644 --- a/src/resources/templates/import.ts +++ b/src/resources/templates/import.ts @@ -77,10 +77,13 @@ export interface ImportCreateParams { projectId?: string; /** - * Body param: AI model to use, in provider/model format. Optional — defaults to - * anthropic/claude-opus-4-6. + * Body param: AI model to use. Accepts a provider/model string (e.g. + * "anthropic/claude-opus-4-7", "openai/gpt-5.5"), a bare provider ("anthropic", + * "openai") which uses that provider's default model, or a bare model id + * ("claude-opus-4-7", "gpt-5.5") with the provider inferred from the name. + * Optional — defaults to anthropic/claude-opus-4-7. */ - model?: 'anthropic/claude-opus-4-6' | 'openai/gpt-5.2'; + model?: string; } export namespace ImportCreateParams { diff --git a/tests/api-resources/templates/generate.test.ts b/tests/api-resources/templates/generate.test.ts index 2e38f11..c6526f4 100644 --- a/tests/api-resources/templates/generate.test.ts +++ b/tests/api-resources/templates/generate.test.ts @@ -10,9 +10,8 @@ const client = new Unlayer({ describe('resource generate', () => { test('create: only required params', async () => { const responsePromise = client.templates.generate.create({ - displayMode: 'email', - input: [{ type: 'text' }], - output: { blockType: 'template', type: 'json' }, + messages: [{ content: [{ type: 'text' }], role: 'user' }], + output: { displayMode: 'email', kind: 'template' }, }); const rawResponse = await responsePromise.asResponse(); expect(rawResponse).toBeInstanceOf(Response); @@ -25,20 +24,25 @@ describe('resource generate', () => { test('create: required and optional params', async () => { const response = await client.templates.generate.create({ - displayMode: 'email', - input: [ + messages: [ { - type: 'text', - id: 'id', - blockType: 'blockType', - data: { foo: 'bar' }, - html: 'html', - schemaVersion: 0, - text: 'text', - url: 'url', + content: [ + { + type: 'text', + file: { url: 'url', mediaType: 'mediaType' }, + image: 'image', + text: 'text', + }, + ], + role: 'user', + metadata: { action: { id: 'id' } }, }, ], - output: { blockType: 'template', type: 'json' }, + output: { + displayMode: 'email', + kind: 'template', + schemaVersion: 0, + }, projectId: 'projectId', context: { availableTools: ['string'], @@ -48,8 +52,27 @@ describe('resource generate', () => { slug: 'slug', }, ], + fullDesign: { foo: 'bar' }, + selection: { + id: 'string', + collection: 'pages', + value: 'value', + }, }, - model: 'anthropic/claude-opus-4-6', + conversationId: 'conversationId', + locale: 'locale', + model: 'model', }); }); + + test('retrieve', async () => { + const responsePromise = client.templates.generate.retrieve(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); }); diff --git a/tests/api-resources/templates/import.test.ts b/tests/api-resources/templates/import.test.ts index 7ac14b8..6f66884 100644 --- a/tests/api-resources/templates/import.test.ts +++ b/tests/api-resources/templates/import.test.ts @@ -35,7 +35,7 @@ describe('resource import', () => { }, ], projectId: 'projectId', - model: 'anthropic/claude-opus-4-6', + model: 'model', }); }); }); From 8716a278e99b36d3492f4086b50945300df6ce10 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 02:16:03 +0000 Subject: [PATCH 37/54] fix(client): send content-type header for requests with an omitted optional body --- src/client.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/client.ts b/src/client.ts index 676adad..ad86390 100644 --- a/src/client.ts +++ b/src/client.ts @@ -748,11 +748,19 @@ export class Unlayer { return () => controller.abort(); } - private buildBody({ options: { body, headers: rawHeaders } }: { options: FinalRequestOptions }): { + private buildBody({ options }: { options: FinalRequestOptions }): { bodyHeaders: HeadersLike; body: BodyInit | undefined; } { + const { body, headers: rawHeaders } = options; if (!body) { + // A resource method always passes a `body` key when its operation defines a + // request body, even if the caller omitted an optional body param. Keep the + // content-type for those, and only elide it for operations with no body at + // all (e.g. GET/DELETE). + if (body == null && 'body' in options) { + return this.#encoder({ body, headers: buildHeaders([rawHeaders]) }); + } return { bodyHeaders: undefined, body: undefined }; } const headers = buildHeaders([rawHeaders]); From 1afb639343df8b5252556c6b445c0cbe93a25c0a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 24 Jun 2026 05:37:24 +0000 Subject: [PATCH 38/54] feat(api): api update --- .stats.yml | 4 ++-- src/resources/templates/generate.ts | 12 ++++++++++-- src/resources/templates/import.ts | 9 ++++++++- tests/api-resources/templates/generate.test.ts | 1 + tests/api-resources/templates/import.test.ts | 1 + 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index eb0d57f..13a7855 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 10 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-35699cec89167aa9ce539f8008695911611f8bdf923234ed701ee3dbc0c5bcd2.yml -openapi_spec_hash: 2ec4eef9500ac0007e1740f431835931 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-091234302d1c0907a6e2c646ad31f2757e832985758314af9171390851ffc12f.yml +openapi_spec_hash: bbac170e82fb6bb60e0db638457623d1 config_hash: 20e7fbba9d423291aaf676f6a629dcaf diff --git a/src/resources/templates/generate.ts b/src/resources/templates/generate.ts index a5b06ca..d5e60bf 100644 --- a/src/resources/templates/generate.ts +++ b/src/resources/templates/generate.ts @@ -139,14 +139,22 @@ export interface GenerateCreateParams { */ conversationId?: string; + /** + * Body param: Transient-outage fallback controls. Omit to use Unlayer defaults + * only when no model is pinned; true always uses Unlayer defaults; false disables + * the outage tail; an ordered array replaces the default provider/model strings. + */ + fallbackModels?: boolean | Array; + /** * Body param: BCP-47 fallback locale for AI status messages. */ locale?: string; /** - * Body param: AI model in "provider/id" form, e.g. "anthropic/claude-opus-4-7". - * Optional — server resolves a default per output kind. + * Body param: Preferred AI model in "provider/id" form, e.g. + * "anthropic/claude-opus-4-7". Optional — server resolves a default per output + * kind. */ model?: string; } diff --git a/src/resources/templates/import.ts b/src/resources/templates/import.ts index 1060f2c..4e00541 100644 --- a/src/resources/templates/import.ts +++ b/src/resources/templates/import.ts @@ -77,7 +77,14 @@ export interface ImportCreateParams { projectId?: string; /** - * Body param: AI model to use. Accepts a provider/model string (e.g. + * Body param: Transient-outage fallback controls. Omit to use Unlayer defaults + * only when no model is pinned; true always uses Unlayer defaults; false disables + * the outage tail; an ordered array replaces the default provider/model strings. + */ + fallbackModels?: boolean | Array; + + /** + * Body param: Preferred AI model. Accepts a provider/model string (e.g. * "anthropic/claude-opus-4-7", "openai/gpt-5.5"), a bare provider ("anthropic", * "openai") which uses that provider's default model, or a bare model id * ("claude-opus-4-7", "gpt-5.5") with the provider inferred from the name. diff --git a/tests/api-resources/templates/generate.test.ts b/tests/api-resources/templates/generate.test.ts index c6526f4..e383821 100644 --- a/tests/api-resources/templates/generate.test.ts +++ b/tests/api-resources/templates/generate.test.ts @@ -60,6 +60,7 @@ describe('resource generate', () => { }, }, conversationId: 'conversationId', + fallbackModels: true, locale: 'locale', model: 'model', }); diff --git a/tests/api-resources/templates/import.test.ts b/tests/api-resources/templates/import.test.ts index 6f66884..2101af0 100644 --- a/tests/api-resources/templates/import.test.ts +++ b/tests/api-resources/templates/import.test.ts @@ -35,6 +35,7 @@ describe('resource import', () => { }, ], projectId: 'projectId', + fallbackModels: true, model: 'model', }); }); From 45521fd4de645cfc1c68a8ea83bd4a3d11120c5e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 24 Jun 2026 07:02:55 +0000 Subject: [PATCH 39/54] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 13a7855..6f038da 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 10 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-091234302d1c0907a6e2c646ad31f2757e832985758314af9171390851ffc12f.yml -openapi_spec_hash: bbac170e82fb6bb60e0db638457623d1 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-3d1f50d8fdb57c0bbafaa16aeffc061ee7532ed5a9823bacbce1cc0916c008b9.yml +openapi_spec_hash: 97d36fb19154cc936e9ef2558965e290 config_hash: 20e7fbba9d423291aaf676f6a629dcaf From 471570b1df590071e3179e179a740c58667b8467 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2026 18:53:16 +0000 Subject: [PATCH 40/54] feat(api): api update --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 06fc108..38236e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1215,9 +1215,9 @@ baseline-browser-mapping@^2.9.0: integrity sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg== brace-expansion@^2.0.2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.1.tgz#c68b1c4111c76aae3a6fba55d496cee10c39dad8" - integrity sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA== + version "2.1.2" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.2.tgz#0bba2271feb7d458b0d31ad13625aaa4754431e2" + integrity sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA== dependencies: balanced-match "^1.0.0" From b1896db3e0aa25efddf62c77b66faf1bd3271a55 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2026 18:42:46 +0000 Subject: [PATCH 41/54] feat(api): api update --- .stats.yml | 8 +- api.md | 40 ++++++ src/resources/templates/export-html.ts | 111 +++++++++++++++++ src/resources/templates/export-image.ts | 117 ++++++++++++++++++ src/resources/templates/export-pdf.ts | 107 ++++++++++++++++ src/resources/templates/export-zip.ts | 97 +++++++++++++++ src/resources/templates/index.ts | 4 + src/resources/templates/templates.ts | 40 ++++++ .../templates/export-html.test.ts | 38 ++++++ .../templates/export-image.test.ts | 42 +++++++ .../templates/export-pdf.test.ts | 40 ++++++ .../templates/export-zip.test.ts | 38 ++++++ 12 files changed, 678 insertions(+), 4 deletions(-) create mode 100644 src/resources/templates/export-html.ts create mode 100644 src/resources/templates/export-image.ts create mode 100644 src/resources/templates/export-pdf.ts create mode 100644 src/resources/templates/export-zip.ts create mode 100644 tests/api-resources/templates/export-html.test.ts create mode 100644 tests/api-resources/templates/export-image.test.ts create mode 100644 tests/api-resources/templates/export-pdf.test.ts create mode 100644 tests/api-resources/templates/export-zip.test.ts diff --git a/.stats.yml b/.stats.yml index 6f038da..2e4bbbe 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 10 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-3d1f50d8fdb57c0bbafaa16aeffc061ee7532ed5a9823bacbce1cc0916c008b9.yml -openapi_spec_hash: 97d36fb19154cc936e9ef2558965e290 -config_hash: 20e7fbba9d423291aaf676f6a629dcaf +configured_endpoints: 14 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-8f62b635191810fa318afdeaabbbb0276b0e1f85f18b5ff2ddaad9a18241f047.yml +openapi_spec_hash: e80a61e80827f2a822db46f4ba82c15d +config_hash: 2949daec69cb6f5d34ae232544245952 diff --git a/api.md b/api.md index 9e7e057..95b16ff 100644 --- a/api.md +++ b/api.md @@ -40,6 +40,46 @@ Methods: - client.templates.convertSimpleToFull.create({ ...params }) -> ConvertSimpleToFullCreateResponse +## ExportHTML + +Types: + +- ExportHTMLCreateResponse + +Methods: + +- client.templates.exportHTML.create({ ...params }) -> ExportHTMLCreateResponse + +## ExportImage + +Types: + +- ExportImageCreateResponse + +Methods: + +- client.templates.exportImage.create({ ...params }) -> ExportImageCreateResponse + +## ExportPdf + +Types: + +- ExportPdfCreateResponse + +Methods: + +- client.templates.exportPdf.create({ ...params }) -> ExportPdfCreateResponse + +## ExportZip + +Types: + +- ExportZipCreateResponse + +Methods: + +- client.templates.exportZip.create({ ...params }) -> ExportZipCreateResponse + ## Generate Types: diff --git a/src/resources/templates/export-html.ts b/src/resources/templates/export-html.ts new file mode 100644 index 0000000..a6329af --- /dev/null +++ b/src/resources/templates/export-html.ts @@ -0,0 +1,111 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; + +export class ExportHTML extends APIResource { + /** + * Export a design as rendered HTML. + */ + create(params: ExportHTMLCreateParams, options?: RequestOptions): APIPromise { + const { projectId, ...body } = params; + return this._client.post('/v3/templates/export/html', { query: { projectId }, body, ...options }); + } +} + +export interface ExportHTMLCreateResponse { + data?: ExportHTMLCreateResponse.Data; + + success?: boolean; +} + +export namespace ExportHTMLCreateResponse { + export interface Data { + chunks?: Data.Chunks; + + html?: string; + } + + export namespace Data { + export interface Chunks { + body?: string; + + css?: string; + + fonts?: Array; + + js?: string; + } + } +} + +export interface ExportHTMLCreateParams { + /** + * Body param: Unlayer design JSON + */ + design: unknown; + + /** + * Query param: The project ID (required for PAT auth, auto-resolved for API key + * auth) + */ + projectId?: string; + + /** + * Body param + */ + customJS?: string | Array; + + /** + * Body param + */ + designTags?: unknown; + + /** + * Body param + */ + designTagsConfig?: unknown; + + /** + * Body param + */ + displayMode?: 'email' | 'web' | 'popup' | 'document'; + + /** + * Body param + */ + editorVersion?: string; + + /** + * Body param + */ + language?: string; + + /** + * Body param + */ + languages?: Array; + + /** + * Body param + */ + mergeTags?: unknown; + + /** + * Body param + */ + mergeTagsSchema?: unknown; + + /** + * Body param + */ + safeHtml?: boolean; +} + +export declare namespace ExportHTML { + export { + type ExportHTMLCreateResponse as ExportHTMLCreateResponse, + type ExportHTMLCreateParams as ExportHTMLCreateParams, + }; +} diff --git a/src/resources/templates/export-image.ts b/src/resources/templates/export-image.ts new file mode 100644 index 0000000..d6b0046 --- /dev/null +++ b/src/resources/templates/export-image.ts @@ -0,0 +1,117 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; + +export class ExportImage extends APIResource { + /** + * Export a design as a PNG image. + */ + create(params: ExportImageCreateParams, options?: RequestOptions): APIPromise { + const { projectId, ...body } = params; + return this._client.post('/v3/templates/export/image', { query: { projectId }, body, ...options }); + } +} + +export interface ExportImageCreateResponse { + data?: ExportImageCreateResponse.Data; + + success?: boolean; +} + +export namespace ExportImageCreateResponse { + export interface Data { + url?: string; + } +} + +export interface ExportImageCreateParams { + /** + * Body param: Unlayer design JSON + */ + design: unknown; + + /** + * Query param: The project ID (required for PAT auth, auto-resolved for API key + * auth) + */ + projectId?: string; + + /** + * Body param + */ + customJS?: string | Array; + + /** + * Body param + */ + designTags?: unknown; + + /** + * Body param + */ + designTagsConfig?: unknown; + + /** + * Body param + */ + deviceScaleFactor?: number; + + /** + * Body param + */ + displayMode?: 'email' | 'web' | 'popup' | 'document'; + + /** + * Body param + */ + editorVersion?: string; + + /** + * Body param + */ + fullPage?: boolean; + + /** + * Body param + */ + height?: number; + + /** + * Body param + */ + language?: string; + + /** + * Body param + */ + languages?: Array; + + /** + * Body param + */ + mergeTags?: unknown; + + /** + * Body param + */ + mergeTagsSchema?: unknown; + + /** + * Body param + */ + safeHtml?: boolean; + + /** + * Body param + */ + width?: number; +} + +export declare namespace ExportImage { + export { + type ExportImageCreateResponse as ExportImageCreateResponse, + type ExportImageCreateParams as ExportImageCreateParams, + }; +} diff --git a/src/resources/templates/export-pdf.ts b/src/resources/templates/export-pdf.ts new file mode 100644 index 0000000..597d1f7 --- /dev/null +++ b/src/resources/templates/export-pdf.ts @@ -0,0 +1,107 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; + +export class ExportPdf extends APIResource { + /** + * Export a design as a PDF document. + */ + create(params: ExportPdfCreateParams, options?: RequestOptions): APIPromise { + const { projectId, ...body } = params; + return this._client.post('/v3/templates/export/pdf', { query: { projectId }, body, ...options }); + } +} + +export interface ExportPdfCreateResponse { + data?: ExportPdfCreateResponse.Data; + + success?: boolean; +} + +export namespace ExportPdfCreateResponse { + export interface Data { + url?: string; + } +} + +export interface ExportPdfCreateParams { + /** + * Body param: Unlayer design JSON + */ + design: unknown; + + /** + * Query param: The project ID (required for PAT auth, auto-resolved for API key + * auth) + */ + projectId?: string; + + /** + * Body param + */ + contentWidth?: number | 'full'; + + /** + * Body param + */ + customJS?: string | Array; + + /** + * Body param + */ + designTags?: unknown; + + /** + * Body param + */ + designTagsConfig?: unknown; + + /** + * Body param + */ + displayMode?: 'email' | 'web' | 'popup' | 'document'; + + /** + * Body param + */ + editorVersion?: string; + + /** + * Body param + */ + language?: string; + + /** + * Body param + */ + languages?: Array; + + /** + * Body param + */ + mergeTags?: unknown; + + /** + * Body param + */ + mergeTagsSchema?: unknown; + + /** + * Body param + */ + pageSize?: 'Letter' | 'Legal' | 'Tabloid' | 'Ledger' | 'A0' | 'A1' | 'A2' | 'A3' | 'A4' | 'A5' | 'A6'; + + /** + * Body param + */ + safeHtml?: boolean; +} + +export declare namespace ExportPdf { + export { + type ExportPdfCreateResponse as ExportPdfCreateResponse, + type ExportPdfCreateParams as ExportPdfCreateParams, + }; +} diff --git a/src/resources/templates/export-zip.ts b/src/resources/templates/export-zip.ts new file mode 100644 index 0000000..00f07a6 --- /dev/null +++ b/src/resources/templates/export-zip.ts @@ -0,0 +1,97 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; + +export class ExportZip extends APIResource { + /** + * Export a design as a ZIP archive containing HTML and assets. + */ + create(params: ExportZipCreateParams, options?: RequestOptions): APIPromise { + const { projectId, ...body } = params; + return this._client.post('/v3/templates/export/zip', { query: { projectId }, body, ...options }); + } +} + +export interface ExportZipCreateResponse { + data?: ExportZipCreateResponse.Data; + + success?: boolean; +} + +export namespace ExportZipCreateResponse { + export interface Data { + url?: string; + } +} + +export interface ExportZipCreateParams { + /** + * Body param: Unlayer design JSON + */ + design: unknown; + + /** + * Query param: The project ID (required for PAT auth, auto-resolved for API key + * auth) + */ + projectId?: string; + + /** + * Body param + */ + customJS?: string | Array; + + /** + * Body param + */ + designTags?: unknown; + + /** + * Body param + */ + designTagsConfig?: unknown; + + /** + * Body param + */ + displayMode?: 'email' | 'web' | 'popup' | 'document'; + + /** + * Body param + */ + editorVersion?: string; + + /** + * Body param + */ + language?: string; + + /** + * Body param + */ + languages?: Array; + + /** + * Body param + */ + mergeTags?: unknown; + + /** + * Body param + */ + mergeTagsSchema?: unknown; + + /** + * Body param + */ + safeHtml?: boolean; +} + +export declare namespace ExportZip { + export { + type ExportZipCreateResponse as ExportZipCreateResponse, + type ExportZipCreateParams as ExportZipCreateParams, + }; +} diff --git a/src/resources/templates/index.ts b/src/resources/templates/index.ts index b4f6e4e..c07698b 100644 --- a/src/resources/templates/index.ts +++ b/src/resources/templates/index.ts @@ -10,6 +10,10 @@ export { type ConvertSimpleToFullCreateResponse, type ConvertSimpleToFullCreateParams, } from './convert-simple-to-full'; +export { ExportHTML, type ExportHTMLCreateResponse, type ExportHTMLCreateParams } from './export-html'; +export { ExportImage, type ExportImageCreateResponse, type ExportImageCreateParams } from './export-image'; +export { ExportPdf, type ExportPdfCreateResponse, type ExportPdfCreateParams } from './export-pdf'; +export { ExportZip, type ExportZipCreateResponse, type ExportZipCreateParams } from './export-zip'; export { Generate, type GenerateCreateResponse, type GenerateCreateParams } from './generate'; export { Import, type ImportCreateResponse, type ImportCreateParams } from './import'; export { diff --git a/src/resources/templates/templates.ts b/src/resources/templates/templates.ts index fd15c00..96b914f 100644 --- a/src/resources/templates/templates.ts +++ b/src/resources/templates/templates.ts @@ -13,6 +13,14 @@ import { ConvertSimpleToFullCreateParams, ConvertSimpleToFullCreateResponse, } from './convert-simple-to-full'; +import * as ExportHTMLAPI from './export-html'; +import { ExportHTML, ExportHTMLCreateParams, ExportHTMLCreateResponse } from './export-html'; +import * as ExportImageAPI from './export-image'; +import { ExportImage, ExportImageCreateParams, ExportImageCreateResponse } from './export-image'; +import * as ExportPdfAPI from './export-pdf'; +import { ExportPdf, ExportPdfCreateParams, ExportPdfCreateResponse } from './export-pdf'; +import * as ExportZipAPI from './export-zip'; +import { ExportZip, ExportZipCreateParams, ExportZipCreateResponse } from './export-zip'; import * as GenerateAPI from './generate'; import { Generate, GenerateCreateParams, GenerateCreateResponse } from './generate'; import * as ImportAPI from './import'; @@ -30,6 +38,10 @@ export class Templates extends APIResource { new ConvertFullToSimpleAPI.ConvertFullToSimple(this._client); convertSimpleToFull: ConvertSimpleToFullAPI.ConvertSimpleToFull = new ConvertSimpleToFullAPI.ConvertSimpleToFull(this._client); + exportHTML: ExportHTMLAPI.ExportHTML = new ExportHTMLAPI.ExportHTML(this._client); + exportImage: ExportImageAPI.ExportImage = new ExportImageAPI.ExportImage(this._client); + exportPdf: ExportPdfAPI.ExportPdf = new ExportPdfAPI.ExportPdf(this._client); + exportZip: ExportZipAPI.ExportZip = new ExportZipAPI.ExportZip(this._client); generate: GenerateAPI.Generate = new GenerateAPI.Generate(this._client); import: ImportAPI.Import = new ImportAPI.Import(this._client); @@ -127,6 +139,10 @@ export interface TemplateListParams extends CursorPageParams { Templates.ConvertFullToSimple = ConvertFullToSimple; Templates.ConvertSimpleToFull = ConvertSimpleToFull; +Templates.ExportHTML = ExportHTML; +Templates.ExportImage = ExportImage; +Templates.ExportPdf = ExportPdf; +Templates.ExportZip = ExportZip; Templates.Generate = Generate; Templates.Import = Import; @@ -151,6 +167,30 @@ export declare namespace Templates { type ConvertSimpleToFullCreateParams as ConvertSimpleToFullCreateParams, }; + export { + ExportHTML as ExportHTML, + type ExportHTMLCreateResponse as ExportHTMLCreateResponse, + type ExportHTMLCreateParams as ExportHTMLCreateParams, + }; + + export { + ExportImage as ExportImage, + type ExportImageCreateResponse as ExportImageCreateResponse, + type ExportImageCreateParams as ExportImageCreateParams, + }; + + export { + ExportPdf as ExportPdf, + type ExportPdfCreateResponse as ExportPdfCreateResponse, + type ExportPdfCreateParams as ExportPdfCreateParams, + }; + + export { + ExportZip as ExportZip, + type ExportZipCreateResponse as ExportZipCreateResponse, + type ExportZipCreateParams as ExportZipCreateParams, + }; + export { Generate as Generate, type GenerateCreateResponse as GenerateCreateResponse, diff --git a/tests/api-resources/templates/export-html.test.ts b/tests/api-resources/templates/export-html.test.ts new file mode 100644 index 0000000..f12a201 --- /dev/null +++ b/tests/api-resources/templates/export-html.test.ts @@ -0,0 +1,38 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource exportHTML', () => { + test('create: only required params', async () => { + const responsePromise = client.templates.exportHTML.create({ design: {} }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('create: required and optional params', async () => { + const response = await client.templates.exportHTML.create({ + design: {}, + projectId: 'projectId', + customJS: 'string', + designTags: {}, + designTagsConfig: {}, + displayMode: 'email', + editorVersion: 'editorVersion', + language: 'language', + languages: ['string'], + mergeTags: {}, + mergeTagsSchema: {}, + safeHtml: true, + }); + }); +}); diff --git a/tests/api-resources/templates/export-image.test.ts b/tests/api-resources/templates/export-image.test.ts new file mode 100644 index 0000000..69dbd4d --- /dev/null +++ b/tests/api-resources/templates/export-image.test.ts @@ -0,0 +1,42 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource exportImage', () => { + test('create: only required params', async () => { + const responsePromise = client.templates.exportImage.create({ design: {} }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('create: required and optional params', async () => { + const response = await client.templates.exportImage.create({ + design: {}, + projectId: 'projectId', + customJS: 'string', + designTags: {}, + designTagsConfig: {}, + deviceScaleFactor: 0, + displayMode: 'email', + editorVersion: 'editorVersion', + fullPage: true, + height: 0, + language: 'language', + languages: ['string'], + mergeTags: {}, + mergeTagsSchema: {}, + safeHtml: true, + width: 0, + }); + }); +}); diff --git a/tests/api-resources/templates/export-pdf.test.ts b/tests/api-resources/templates/export-pdf.test.ts new file mode 100644 index 0000000..8415fd7 --- /dev/null +++ b/tests/api-resources/templates/export-pdf.test.ts @@ -0,0 +1,40 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource exportPdf', () => { + test('create: only required params', async () => { + const responsePromise = client.templates.exportPdf.create({ design: {} }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('create: required and optional params', async () => { + const response = await client.templates.exportPdf.create({ + design: {}, + projectId: 'projectId', + contentWidth: 'full', + customJS: 'string', + designTags: {}, + designTagsConfig: {}, + displayMode: 'email', + editorVersion: 'editorVersion', + language: 'language', + languages: ['string'], + mergeTags: {}, + mergeTagsSchema: {}, + pageSize: 'Letter', + safeHtml: true, + }); + }); +}); diff --git a/tests/api-resources/templates/export-zip.test.ts b/tests/api-resources/templates/export-zip.test.ts new file mode 100644 index 0000000..3f4366c --- /dev/null +++ b/tests/api-resources/templates/export-zip.test.ts @@ -0,0 +1,38 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource exportZip', () => { + test('create: only required params', async () => { + const responsePromise = client.templates.exportZip.create({ design: {} }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('create: required and optional params', async () => { + const response = await client.templates.exportZip.create({ + design: {}, + projectId: 'projectId', + customJS: 'string', + designTags: {}, + designTagsConfig: {}, + displayMode: 'email', + editorVersion: 'editorVersion', + language: 'language', + languages: ['string'], + mergeTags: {}, + mergeTagsSchema: {}, + safeHtml: true, + }); + }); +}); From ba79f382f9b86e18978decac75e2b2aa20f87e74 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 11 Jul 2026 02:21:41 +0000 Subject: [PATCH 42/54] fix(ci): bump @arethetypeswrong/cli to ^0.18.0 and run CI workflows on Node 24 --- .github/workflows/ci.yml | 6 ++-- .github/workflows/publish-npm.yml | 2 +- package.json | 2 +- yarn.lock | 51 +++++++++++++++++++------------ 4 files changed, 37 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05b5c45..0579bd7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: - name: Set up Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: - node-version: '20' + node-version: '24' - name: Bootstrap run: ./scripts/bootstrap @@ -48,7 +48,7 @@ jobs: - name: Set up Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: - node-version: '20' + node-version: '24' - name: Bootstrap run: ./scripts/bootstrap @@ -85,7 +85,7 @@ jobs: - name: Set up Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: - node-version: '20' + node-version: '24' - name: Bootstrap run: ./scripts/bootstrap diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index 59442f8..17e1064 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -19,7 +19,7 @@ jobs: - name: Set up Node uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1 with: - node-version: '20' + node-version: '24' - name: Install dependencies run: | diff --git a/package.json b/package.json index a9085e4..3544a8f 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ }, "dependencies": {}, "devDependencies": { - "@arethetypeswrong/cli": "^0.17.0", + "@arethetypeswrong/cli": "^0.18.0", "@swc/core": "^1.3.102", "@swc/jest": "^0.2.29", "@types/jest": "^29.4.0", diff --git a/yarn.lock b/yarn.lock index 38236e6..443cc05 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12,12 +12,12 @@ resolved "https://registry.yarnpkg.com/@andrewbranch/untar.js/-/untar.js-1.0.3.tgz#ba9494f85eb83017c5c855763969caf1d0adea00" integrity sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw== -"@arethetypeswrong/cli@^0.17.0": - version "0.17.0" - resolved "https://registry.yarnpkg.com/@arethetypeswrong/cli/-/cli-0.17.0.tgz#f97f10926b3f9f9eb5117550242d2e06c25cadac" - integrity sha512-xSMW7bfzVWpYw5JFgZqBXqr6PdR0/REmn3DkxCES5N0JTcB0CVgbIynJCvKBFmXaPc3hzmmTrb7+yPDRoOSZdA== +"@arethetypeswrong/cli@^0.18.0": + version "0.18.4" + resolved "https://registry.yarnpkg.com/@arethetypeswrong/cli/-/cli-0.18.4.tgz#c31f54f3b0d0e0f3256ab3edb6530beeb5e64b4b" + integrity sha512-kNWo6LTzGAuLYPpJ7Sgo63whSUeeSuKMlYx6IBgzs4ONEG807gW4hSSENvpeCHzO2H2wIzG5EFl0OKBbqGBAyA== dependencies: - "@arethetypeswrong/core" "0.17.0" + "@arethetypeswrong/core" "0.18.4" chalk "^4.1.2" cli-table3 "^0.6.3" commander "^10.0.1" @@ -25,15 +25,16 @@ marked-terminal "^7.1.0" semver "^7.5.4" -"@arethetypeswrong/core@0.17.0": - version "0.17.0" - resolved "https://registry.yarnpkg.com/@arethetypeswrong/core/-/core-0.17.0.tgz#abb3b5f425056d37193644c2a2de4aecf866b76b" - integrity sha512-FHyhFizXNetigTVsIhqXKGYLpazPS5YNojEPpZEUcBPt9wVvoEbNIvG+hybuBR+pjlRcbyuqhukHZm1fr+bDgA== +"@arethetypeswrong/core@0.18.4": + version "0.18.4" + resolved "https://registry.yarnpkg.com/@arethetypeswrong/core/-/core-0.18.4.tgz#24edea3d651dea7d32bf6f1cc9ee9a00db39de49" + integrity sha512-M5F0ePyN6h2Z6XxRiyIPqjGbltotXLjR0CKA0uKspsDu0QmgTNYvRb4RSQPMUs2ZXZHCCYpbaZbFbYOXLxCjUA== dependencies: "@andrewbranch/untar.js" "^1.0.3" + "@loaderkit/resolve" "^1.0.2" cjs-module-lexer "^1.2.3" - fflate "^0.8.2" - lru-cache "^10.4.3" + fflate "^0.8.3" + lru-cache "^11.0.1" semver "^7.5.4" typescript "5.6.1-rc" validate-npm-package-name "^5.0.0" @@ -306,6 +307,11 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@braidai/lang@^1.0.0": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@braidai/lang/-/lang-1.1.2.tgz#65bc2bc1db6d00e153b95ac7006f4573e289e9be" + integrity sha512-qBcknbBufNHlui137Hft8xauQMTZDKdophmLFv05r2eNmdIv/MlPuP4TdUknHG68UdWLgVZwgxVe735HzJNIwA== + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -688,6 +694,13 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@loaderkit/resolve@^1.0.2": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@loaderkit/resolve/-/resolve-1.0.6.tgz#8d45341e688faecc25b3ae919c0f45d94c4e26c9" + integrity sha512-G8FdIoF5CypfwmD9rl8BXod5HDn8JqB0CCNBXDTaRZ+yRYhARrrSToX1zg1zy9jX3zLqigsELwhT4gNtkdQAUg== + dependencies: + "@braidai/lang" "^1.0.0" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1696,10 +1709,10 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -fflate@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" - integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== +fflate@^0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.3.tgz#bc27d8eb30343d4d512abb03480202ce65d825fc" + integrity sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA== file-entry-cache@^8.0.0: version "8.0.0" @@ -2490,10 +2503,10 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lru-cache@^10.4.3: - version "10.4.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" - integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== +lru-cache@^11.0.1: + version "11.5.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.5.2.tgz#00e16665c90c620fba14a3c368732a976493f760" + integrity sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g== lru-cache@^5.1.1: version "5.1.1" From 0e5b3fb7ac418c69d4c1cec1ed09654241f75f2f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 18 Jul 2026 02:14:28 +0000 Subject: [PATCH 43/54] feat(stlc): configurable CI runner and private-production-repo support in workflow templates --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0579bd7..a15001b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: lint: timeout-minutes: 10 name: lint - runs-on: ${{ github.repository == 'stainless-sdks/unlayer-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -37,7 +37,7 @@ jobs: build: timeout-minutes: 5 name: build - runs-on: ${{ github.repository == 'stainless-sdks/unlayer-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') permissions: contents: read @@ -77,7 +77,7 @@ jobs: test: timeout-minutes: 10 name: test - runs-on: ${{ github.repository == 'stainless-sdks/unlayer-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 From 0550aaa172a7aa5b2ded7ff3f9b15e77e1533858 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:12:56 +0000 Subject: [PATCH 44/54] feat(api): api update --- .stats.yml | 8 +-- api.md | 22 +++++++ src/client.ts | 18 ++++++ src/resources/editor-sessions.ts | 58 ++++++++++++++++++ src/resources/index.ts | 6 ++ src/resources/me.ts | 3 + src/resources/me/index.ts | 8 +++ src/resources/me/me.ts | 19 ++++++ src/resources/me/subscription.ts | 67 +++++++++++++++++++++ tests/api-resources/editor-sessions.test.ts | 29 +++++++++ tests/api-resources/me/subscription.test.ts | 28 +++++++++ 11 files changed, 262 insertions(+), 4 deletions(-) create mode 100644 src/resources/editor-sessions.ts create mode 100644 src/resources/me.ts create mode 100644 src/resources/me/index.ts create mode 100644 src/resources/me/me.ts create mode 100644 src/resources/me/subscription.ts create mode 100644 tests/api-resources/editor-sessions.test.ts create mode 100644 tests/api-resources/me/subscription.test.ts diff --git a/.stats.yml b/.stats.yml index 2e4bbbe..61fd7e3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 14 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-8f62b635191810fa318afdeaabbbb0276b0e1f85f18b5ff2ddaad9a18241f047.yml -openapi_spec_hash: e80a61e80827f2a822db46f4ba82c15d -config_hash: 2949daec69cb6f5d34ae232544245952 +configured_endpoints: 16 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-bccf7c265777474ad23af36d8613e0c738575733ba2335e42129def19d09c021.yml +openapi_spec_hash: 28687f1755511828bc8323d621eba251 +config_hash: de0fdd9f4e2afbb6886dfabb1e7306fa diff --git a/api.md b/api.md index 95b16ff..10cecef 100644 --- a/api.md +++ b/api.md @@ -1,3 +1,25 @@ +# EditorSessions + +Types: + +- EditorSessionCreateResponse + +Methods: + +- client.editorSessions.create({ ...params }) -> EditorSessionCreateResponse + +# Me + +## Subscription + +Types: + +- SubscriptionRetrieveResponse + +Methods: + +- client.me.subscription.retrieve({ ...params }) -> SubscriptionRetrieveResponse + # Projects Types: diff --git a/src/client.ts b/src/client.ts index ad86390..60c6975 100644 --- a/src/client.ts +++ b/src/client.ts @@ -19,8 +19,14 @@ import { AbstractPage, type CursorPageParams, CursorPageResponse } from './core/ import * as Uploads from './core/uploads'; import * as API from './resources/index'; import { APIPromise } from './core/api-promise'; +import { + EditorSessionCreateParams, + EditorSessionCreateResponse, + EditorSessions, +} from './resources/editor-sessions'; import { ProjectRetrieveResponse, Projects } from './resources/projects'; import { WorkspaceListResponse, WorkspaceRetrieveResponse, Workspaces } from './resources/workspaces'; +import { Me } from './resources/me/me'; import { TemplateListParams, TemplateListResponse, @@ -820,6 +826,8 @@ export class Unlayer { static toFile = Uploads.toFile; + editorSessions: API.EditorSessions = new API.EditorSessions(this); + me: API.Me = new API.Me(this); /** * Project details and configuration. */ @@ -834,6 +842,8 @@ export class Unlayer { workspaces: API.Workspaces = new API.Workspaces(this); } +Unlayer.EditorSessions = EditorSessions; +Unlayer.Me = Me; Unlayer.Projects = Projects; Unlayer.Templates = Templates; Unlayer.Workspaces = Workspaces; @@ -844,6 +854,14 @@ export declare namespace Unlayer { export import CursorPage = Pagination.CursorPage; export { type CursorPageParams as CursorPageParams, type CursorPageResponse as CursorPageResponse }; + export { + EditorSessions as EditorSessions, + type EditorSessionCreateResponse as EditorSessionCreateResponse, + type EditorSessionCreateParams as EditorSessionCreateParams, + }; + + export { Me as Me }; + export { Projects as Projects, type ProjectRetrieveResponse as ProjectRetrieveResponse }; export { diff --git a/src/resources/editor-sessions.ts b/src/resources/editor-sessions.ts new file mode 100644 index 0000000..dd297aa --- /dev/null +++ b/src/resources/editor-sessions.ts @@ -0,0 +1,58 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../core/resource'; +import { APIPromise } from '../core/api-promise'; +import { RequestOptions } from '../internal/request-options'; + +export class EditorSessions extends APIResource { + /** + * Create an ephemeral, no-DB editor session for a design and return a hosted + * editor URL the user can open to edit it in the real Unlayer editor. + */ + create( + params: EditorSessionCreateParams, + options?: RequestOptions, + ): APIPromise { + const { projectId, ...body } = params; + return this._client.post('/v3/editor-sessions', { query: { projectId }, body, ...options }); + } +} + +export interface EditorSessionCreateResponse { + data?: EditorSessionCreateResponse.Data; +} + +export namespace EditorSessionCreateResponse { + export interface Data { + token?: string; + + editorUrl?: string; + + expiresAt?: string; + } +} + +export interface EditorSessionCreateParams { + /** + * Body param: Design JSON to load into the editor. + */ + design: { [key: string]: unknown }; + + /** + * Query param: The project ID (required for PAT auth, auto-resolved for API key + * auth) + */ + projectId?: string; + + /** + * Body param: Editor display mode. Defaults to email. + */ + displayMode?: 'email' | 'web' | 'popup' | 'document'; +} + +export declare namespace EditorSessions { + export { + type EditorSessionCreateResponse as EditorSessionCreateResponse, + type EditorSessionCreateParams as EditorSessionCreateParams, + }; +} diff --git a/src/resources/index.ts b/src/resources/index.ts index 6ca716c..8b2cd41 100644 --- a/src/resources/index.ts +++ b/src/resources/index.ts @@ -1,5 +1,11 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +export { + EditorSessions, + type EditorSessionCreateResponse, + type EditorSessionCreateParams, +} from './editor-sessions'; +export { Me } from './me/me'; export { Projects, type ProjectRetrieveResponse } from './projects'; export { Templates, diff --git a/src/resources/me.ts b/src/resources/me.ts new file mode 100644 index 0000000..54b12df --- /dev/null +++ b/src/resources/me.ts @@ -0,0 +1,3 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export * from './me/index'; diff --git a/src/resources/me/index.ts b/src/resources/me/index.ts new file mode 100644 index 0000000..f62121d --- /dev/null +++ b/src/resources/me/index.ts @@ -0,0 +1,8 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export { Me } from './me'; +export { + Subscription, + type SubscriptionRetrieveResponse, + type SubscriptionRetrieveParams, +} from './subscription'; diff --git a/src/resources/me/me.ts b/src/resources/me/me.ts new file mode 100644 index 0000000..5a001fe --- /dev/null +++ b/src/resources/me/me.ts @@ -0,0 +1,19 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import * as SubscriptionAPI from './subscription'; +import { Subscription, SubscriptionRetrieveParams, SubscriptionRetrieveResponse } from './subscription'; + +export class Me extends APIResource { + subscription: SubscriptionAPI.Subscription = new SubscriptionAPI.Subscription(this._client); +} + +Me.Subscription = Subscription; + +export declare namespace Me { + export { + Subscription as Subscription, + type SubscriptionRetrieveResponse as SubscriptionRetrieveResponse, + type SubscriptionRetrieveParams as SubscriptionRetrieveParams, + }; +} diff --git a/src/resources/me/subscription.ts b/src/resources/me/subscription.ts new file mode 100644 index 0000000..bac4e56 --- /dev/null +++ b/src/resources/me/subscription.ts @@ -0,0 +1,67 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; + +export class Subscription extends APIResource { + /** + * Get the current plan, feature availability, and limits for a project. Used to + * answer "can I do X" / "what plan do I need" questions with ground-truth data + * instead of guessing. + */ + retrieve( + query: SubscriptionRetrieveParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.get('/v3/me/subscription', { query, ...options }); + } +} + +export interface SubscriptionRetrieveResponse { + data?: SubscriptionRetrieveResponse.Data; +} + +export namespace SubscriptionRetrieveResponse { + export interface Data { + expiresAt?: string | null; + + features?: Array; + + limits?: Array; + + planName?: string | null; + + status?: string | null; + } + + export namespace Data { + export interface Feature { + available?: boolean; + + name?: string; + } + + export interface Limit { + name?: string; + + unit?: string; + + value?: number; + } + } +} + +export interface SubscriptionRetrieveParams { + /** + * The project ID (required for PAT auth, auto-resolved for API key auth) + */ + projectId?: string; +} + +export declare namespace Subscription { + export { + type SubscriptionRetrieveResponse as SubscriptionRetrieveResponse, + type SubscriptionRetrieveParams as SubscriptionRetrieveParams, + }; +} diff --git a/tests/api-resources/editor-sessions.test.ts b/tests/api-resources/editor-sessions.test.ts new file mode 100644 index 0000000..aaea1a4 --- /dev/null +++ b/tests/api-resources/editor-sessions.test.ts @@ -0,0 +1,29 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource editorSessions', () => { + test('create: only required params', async () => { + const responsePromise = client.editorSessions.create({ design: { foo: 'bar' } }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('create: required and optional params', async () => { + const response = await client.editorSessions.create({ + design: { foo: 'bar' }, + projectId: 'projectId', + displayMode: 'email', + }); + }); +}); diff --git a/tests/api-resources/me/subscription.test.ts b/tests/api-resources/me/subscription.test.ts new file mode 100644 index 0000000..6898f7c --- /dev/null +++ b/tests/api-resources/me/subscription.test.ts @@ -0,0 +1,28 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource subscription', () => { + test('retrieve', async () => { + const responsePromise = client.me.subscription.retrieve(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('retrieve: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.me.subscription.retrieve({ projectId: 'projectId' }, { path: '/_stainless_unknown_path' }), + ).rejects.toThrow(Unlayer.NotFoundError); + }); +}); From cfb84d3b217d5d40803307a7cb5bb6f6435f0fca Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 22 Jul 2026 07:55:57 +0000 Subject: [PATCH 45/54] feat(api): api update --- .stats.yml | 4 ++-- src/resources/templates/export-html.ts | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 61fd7e3..b7f1d1a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-bccf7c265777474ad23af36d8613e0c738575733ba2335e42129def19d09c021.yml -openapi_spec_hash: 28687f1755511828bc8323d621eba251 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-aa551455ecbd91da3298aa03a688686374e19650fb3fce54f437fb4086eef3ba.yml +openapi_spec_hash: c44982974f0272a5fa34de8b2d8d759d config_hash: de0fdd9f4e2afbb6886dfabb1e7306fa diff --git a/src/resources/templates/export-html.ts b/src/resources/templates/export-html.ts index a6329af..7803e53 100644 --- a/src/resources/templates/export-html.ts +++ b/src/resources/templates/export-html.ts @@ -22,8 +22,12 @@ export interface ExportHTMLCreateResponse { export namespace ExportHTMLCreateResponse { export interface Data { + amp?: { [key: string]: unknown }; + chunks?: Data.Chunks; + design?: { [key: string]: unknown }; + html?: string; } @@ -36,6 +40,8 @@ export namespace ExportHTMLCreateResponse { fonts?: Array; js?: string; + + tags?: Array; } } } From 39f84a74814c12d4435850400db6e88bf16868a2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 22 Jul 2026 17:25:13 +0000 Subject: [PATCH 46/54] feat(api): api update --- .stats.yml | 8 +- api.md | 76 ++++++++- src/client.ts | 2 +- src/resources/index.ts | 2 +- src/resources/projects.ts | 59 +------ .../ai-credits-settings-rotate-secret.ts | 31 ++++ src/resources/projects/ai-credits-settings.ts | 85 ++++++++++ src/resources/projects/ai-credits-usage.ts | 109 ++++++++++++ .../ai-credits-webhooks-deliveries.ts | 84 ++++++++++ .../ai-credits-webhooks-deliveriesattempts.ts | 73 ++++++++ .../ai-credits-webhooks-deliveriesretry.ts | 46 +++++ src/resources/projects/ai-credits.ts | 47 ++++++ src/resources/projects/index.ts | 34 ++++ src/resources/projects/projects.ts | 158 ++++++++++++++++++ .../ai-credits-settings-rotate-secret.test.ts | 21 +++ .../projects/ai-credits-settings.test.ts | 47 ++++++ .../projects/ai-credits-usage.test.ts | 41 +++++ .../ai-credits-webhooks-deliveries.test.ts | 37 ++++ ...redits-webhooks-deliveriesattempts.test.ts | 31 ++++ ...i-credits-webhooks-deliveriesretry.test.ts | 29 ++++ .../api-resources/projects/ai-credits.test.ts | 21 +++ .../{ => projects}/projects.test.ts | 0 22 files changed, 975 insertions(+), 66 deletions(-) create mode 100644 src/resources/projects/ai-credits-settings-rotate-secret.ts create mode 100644 src/resources/projects/ai-credits-settings.ts create mode 100644 src/resources/projects/ai-credits-usage.ts create mode 100644 src/resources/projects/ai-credits-webhooks-deliveries.ts create mode 100644 src/resources/projects/ai-credits-webhooks-deliveriesattempts.ts create mode 100644 src/resources/projects/ai-credits-webhooks-deliveriesretry.ts create mode 100644 src/resources/projects/ai-credits.ts create mode 100644 src/resources/projects/index.ts create mode 100644 src/resources/projects/projects.ts create mode 100644 tests/api-resources/projects/ai-credits-settings-rotate-secret.test.ts create mode 100644 tests/api-resources/projects/ai-credits-settings.test.ts create mode 100644 tests/api-resources/projects/ai-credits-usage.test.ts create mode 100644 tests/api-resources/projects/ai-credits-webhooks-deliveries.test.ts create mode 100644 tests/api-resources/projects/ai-credits-webhooks-deliveriesattempts.test.ts create mode 100644 tests/api-resources/projects/ai-credits-webhooks-deliveriesretry.test.ts create mode 100644 tests/api-resources/projects/ai-credits.test.ts rename tests/api-resources/{ => projects}/projects.test.ts (100%) diff --git a/.stats.yml b/.stats.yml index b7f1d1a..568730e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-aa551455ecbd91da3298aa03a688686374e19650fb3fce54f437fb4086eef3ba.yml -openapi_spec_hash: c44982974f0272a5fa34de8b2d8d759d -config_hash: de0fdd9f4e2afbb6886dfabb1e7306fa +configured_endpoints: 24 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-b42187e1b2fff52630a33946829067dcc76dd842a7fb85d826ee9ccc4f44175d.yml +openapi_spec_hash: 7f0c95f3bb404716e0a77878c0c21b91 +config_hash: ee438ad5d5e9b8446d21fc7cb50eff95 diff --git a/api.md b/api.md index 10cecef..b795cb7 100644 --- a/api.md +++ b/api.md @@ -24,11 +24,83 @@ Methods: Types: -- ProjectRetrieveResponse +- ProjectRetrieveResponse Methods: -- client.projects.retrieve(id) -> ProjectRetrieveResponse +- client.projects.retrieve(id) -> ProjectRetrieveResponse + +## AICredits + +Types: + +- AICreditRetrieveResponse + +Methods: + +- client.projects.aiCredits.retrieve(id) -> AICreditRetrieveResponse + +## AICreditsSettings + +Types: + +- AICreditsSettingRetrieveResponse +- AICreditsSettingUpdateResponse + +Methods: + +- client.projects.aiCreditsSettings.retrieve(id) -> AICreditsSettingRetrieveResponse +- client.projects.aiCreditsSettings.update(id, { ...params }) -> AICreditsSettingUpdateResponse + +## AICreditsSettingsRotateSecret + +Types: + +- AICreditsSettingsRotateSecretCreateResponse + +Methods: + +- client.projects.aiCreditsSettingsRotateSecret.create(id) -> AICreditsSettingsRotateSecretCreateResponse + +## AICreditsUsage + +Types: + +- AICreditsUsageRetrieveResponse + +Methods: + +- client.projects.aiCreditsUsage.retrieve(id, { ...params }) -> AICreditsUsageRetrieveResponse + +## AICreditsWebhooksDeliveries + +Types: + +- AICreditsWebhooksDeliveryRetrieveResponse + +Methods: + +- client.projects.aiCreditsWebhooksDeliveries.retrieve(id, { ...params }) -> AICreditsWebhooksDeliveryRetrieveResponse + +## AICreditsWebhooksDeliveriesattempts + +Types: + +- AICreditsWebhooksDeliveriesattemptRetrieveResponse + +Methods: + +- client.projects.aiCreditsWebhooksDeliveriesattempts.retrieve(deliveryID, { ...params }) -> AICreditsWebhooksDeliveriesattemptRetrieveResponse + +## AICreditsWebhooksDeliveriesretry + +Types: + +- AICreditsWebhooksDeliveriesretryCreateResponse + +Methods: + +- client.projects.aiCreditsWebhooksDeliveriesretry.create(deliveryID, { ...params }) -> AICreditsWebhooksDeliveriesretryCreateResponse # Templates diff --git a/src/client.ts b/src/client.ts index 60c6975..5440607 100644 --- a/src/client.ts +++ b/src/client.ts @@ -24,9 +24,9 @@ import { EditorSessionCreateResponse, EditorSessions, } from './resources/editor-sessions'; -import { ProjectRetrieveResponse, Projects } from './resources/projects'; import { WorkspaceListResponse, WorkspaceRetrieveResponse, Workspaces } from './resources/workspaces'; import { Me } from './resources/me/me'; +import { ProjectRetrieveResponse, Projects } from './resources/projects/projects'; import { TemplateListParams, TemplateListResponse, diff --git a/src/resources/index.ts b/src/resources/index.ts index 8b2cd41..c4ba5a4 100644 --- a/src/resources/index.ts +++ b/src/resources/index.ts @@ -6,7 +6,7 @@ export { type EditorSessionCreateParams, } from './editor-sessions'; export { Me } from './me/me'; -export { Projects, type ProjectRetrieveResponse } from './projects'; +export { Projects, type ProjectRetrieveResponse } from './projects/projects'; export { Templates, type TemplateRetrieveResponse, diff --git a/src/resources/projects.ts b/src/resources/projects.ts index 0d15122..f9985fc 100644 --- a/src/resources/projects.ts +++ b/src/resources/projects.ts @@ -1,60 +1,3 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { APIResource } from '../core/resource'; -import { APIPromise } from '../core/api-promise'; -import { RequestOptions } from '../internal/request-options'; -import { path } from '../internal/utils/path'; - -/** - * Project details and configuration. - */ -export class Projects extends APIResource { - /** - * Get project details by ID. - */ - retrieve(id: string, options?: RequestOptions): APIPromise { - return this._client.get(path`/v3/projects/${id}`, options); - } -} - -export interface ProjectRetrieveResponse { - data?: ProjectRetrieveResponse.Data; -} - -export namespace ProjectRetrieveResponse { - export interface Data { - /** - * The project ID. - */ - id?: number; - - /** - * When the project was created. - */ - createdAt?: string; - - /** - * The project name. - */ - name?: string; - - /** - * The project status. - */ - status?: string; - - workspace?: Data.Workspace; - } - - export namespace Data { - export interface Workspace { - id?: number; - - name?: string; - } - } -} - -export declare namespace Projects { - export { type ProjectRetrieveResponse as ProjectRetrieveResponse }; -} +export * from './projects/index'; diff --git a/src/resources/projects/ai-credits-settings-rotate-secret.ts b/src/resources/projects/ai-credits-settings-rotate-secret.ts new file mode 100644 index 0000000..b23ff6d --- /dev/null +++ b/src/resources/projects/ai-credits-settings-rotate-secret.ts @@ -0,0 +1,31 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +/** + * AI credit balance, usage breakdown, and webhook/alert settings. Credits are pooled per workspace; settings are per project. + */ +export class AICreditsSettingsRotateSecret extends APIResource { + /** + * Generates a new HMAC signing secret for the project and returns it exactly once. + * The previous secret stops working immediately, so update your webhook + * verification before rotating. Requires a webhook URL to be configured first. + */ + create(id: string, options?: RequestOptions): APIPromise { + return this._client.post(path`/v3/projects/${id}/ai-credits/settings/rotate-secret`, options); + } +} + +export interface AICreditsSettingsRotateSecretCreateResponse { + /** + * The new HMAC signing secret. Shown only once. + */ + signing_secret?: string; +} + +export declare namespace AICreditsSettingsRotateSecret { + export { type AICreditsSettingsRotateSecretCreateResponse as AICreditsSettingsRotateSecretCreateResponse }; +} diff --git a/src/resources/projects/ai-credits-settings.ts b/src/resources/projects/ai-credits-settings.ts new file mode 100644 index 0000000..d6bc6ef --- /dev/null +++ b/src/resources/projects/ai-credits-settings.ts @@ -0,0 +1,85 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +/** + * AI credit balance, usage breakdown, and webhook/alert settings. Credits are pooled per workspace; settings are per project. + */ +export class AICreditsSettings extends APIResource { + /** + * Returns a project's AI credit exhaustion behavior, alert thresholds, and webhook + * endpoint. The signing secret is never returned — only whether one exists + * (`has_signing_secret`). + */ + retrieve(id: string, options?: RequestOptions): APIPromise { + return this._client.get(path`/v3/projects/${id}/ai-credits/settings`, options); + } + + /** + * Configures AI credit exhaustion behavior, usage alert thresholds, and the + * webhook endpoint for a project. The HMAC signing secret is generated the first + * time a webhook URL is set and returned exactly once in the response — store it + * securely; it is never shown again. + */ + update( + id: string, + body: AICreditsSettingUpdateParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.put(path`/v3/projects/${id}/ai-credits/settings`, { body, ...options }); + } +} + +export interface AICreditsSettingRetrieveResponse { + exhaustion_behavior?: string; + + has_signing_secret?: boolean; + + threshold_alerts?: Array; + + webhook_url?: string | null; +} + +export interface AICreditsSettingUpdateResponse { + exhaustion_behavior?: string; + + has_signing_secret?: boolean; + + /** + * The HMAC signing secret. Returned ONLY on the response that first generates it. + */ + signing_secret?: string; + + threshold_alerts?: Array; + + webhook_url?: string | null; +} + +export interface AICreditsSettingUpdateParams { + /** + * What the editor does when the credit balance is exhausted. + */ + exhaustion_behavior?: 'disable' | 'show_error'; + + /** + * Usage percentages (1-100) at which a threshold_reached webhook fires, once per + * crossing per period. + */ + threshold_alerts?: Array; + + /** + * HTTPS endpoint that receives AI credit webhooks. + */ + webhook_url?: string | null; +} + +export declare namespace AICreditsSettings { + export { + type AICreditsSettingRetrieveResponse as AICreditsSettingRetrieveResponse, + type AICreditsSettingUpdateResponse as AICreditsSettingUpdateResponse, + type AICreditsSettingUpdateParams as AICreditsSettingUpdateParams, + }; +} diff --git a/src/resources/projects/ai-credits-usage.ts b/src/resources/projects/ai-credits-usage.ts new file mode 100644 index 0000000..38bec77 --- /dev/null +++ b/src/resources/projects/ai-credits-usage.ts @@ -0,0 +1,109 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +/** + * AI credit balance, usage breakdown, and webhook/alert settings. Credits are pooled per workspace; settings are per project. + */ +export class AICreditsUsage extends APIResource { + /** + * Returns AI credit consumption for the project, broken down by end user and + * feature type. Filterable by date range, end user, and feature type. Defaults to + * the current billing period. Only credit counts are returned; token counts, model + * names, and costs are never exposed. Per-end-user attribution requires the + * partner to pass `endUserId` on editor initialization. + */ + retrieve( + id: string, + query: AICreditsUsageRetrieveParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.get(path`/v3/projects/${id}/ai-credits/usage`, { query, ...options }); + } +} + +export interface AICreditsUsageRetrieveResponse { + breakdown?: Array; + + /** + * Number of breakdown rows matching the filter (ignores paging). + */ + total?: number; + + /** + * Total AI credits used across the full filtered range (not just the returned + * page). + */ + total_credits_used?: number; +} + +export namespace AICreditsUsageRetrieveResponse { + export interface Breakdown { + /** + * AI credits used by this end user and feature type. + */ + credits?: number; + + /** + * The end user id, or null for unattributed usage. + */ + end_user_id?: string | null; + + /** + * The partner-facing feature type. + */ + feature_type?: 'full_template_gen' | 'block_edit' | 'html_import' | 'image_import' | 'image_generation'; + } +} + +export interface AICreditsUsageRetrieveParams { + /** + * End date (inclusive), YYYY-MM-DD. + */ + end?: string; + + /** + * Filter to a single end user id. + */ + end_user_id?: string; + + /** + * Filter to a single feature type. + */ + feature_type?: 'full_template_gen' | 'block_edit' | 'html_import' | 'image_import' | 'image_generation'; + + /** + * Max breakdown rows to return (1-1000). + */ + limit?: number; + + /** + * Number of breakdown rows to skip (pagination). + */ + offset?: number; + + /** + * Sort direction. Defaults to desc (highest credits first). + */ + order?: 'asc' | 'desc'; + + /** + * Field the breakdown is ordered by. Defaults to credits. + */ + sort?: 'credits' | 'end_user_id' | 'feature_type'; + + /** + * Start date (inclusive), YYYY-MM-DD. + */ + start?: string; +} + +export declare namespace AICreditsUsage { + export { + type AICreditsUsageRetrieveResponse as AICreditsUsageRetrieveResponse, + type AICreditsUsageRetrieveParams as AICreditsUsageRetrieveParams, + }; +} diff --git a/src/resources/projects/ai-credits-webhooks-deliveries.ts b/src/resources/projects/ai-credits-webhooks-deliveries.ts new file mode 100644 index 0000000..7202cf1 --- /dev/null +++ b/src/resources/projects/ai-credits-webhooks-deliveries.ts @@ -0,0 +1,84 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +/** + * AI credit balance, usage breakdown, and webhook/alert settings. Credits are pooled per workspace; settings are per project. + */ +export class AICreditsWebhooksDeliveries extends APIResource { + /** + * Returns the webhook delivery history for the project, newest first — the event, + * delivery status, attempt count, and last response code for each. Use it to spot + * failed deliveries and drive the retry endpoint. Payloads expose credits only. + */ + retrieve( + id: string, + query: AICreditsWebhooksDeliveryRetrieveParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.get(path`/v3/projects/${id}/ai-credits/webhooks/deliveries`, { query, ...options }); + } +} + +export interface AICreditsWebhooksDeliveryRetrieveResponse { + deliveries?: Array; + + /** + * Total deliveries matching the filter (ignores limit/offset). + */ + total?: number; +} + +export namespace AICreditsWebhooksDeliveryRetrieveResponse { + export interface Delivery { + id?: string; + + attempts?: number; + + created_at?: string; + + delivered_at?: string | null; + + end_user_id?: string | null; + + event?: string; + + last_status_code?: number | null; + + payload?: { [key: string]: unknown }; + + status?: 'pending' | 'delivered' | 'failed'; + } +} + +export interface AICreditsWebhooksDeliveryRetrieveParams { + /** + * Filter to a single event type. + */ + event?: 'ai.credits.usage_recorded' | 'ai.credits.threshold_reached' | 'ai.credits.exhausted'; + + /** + * Max deliveries to return (1-100). + */ + limit?: number; + + /** + * Number of deliveries to skip (pagination). + */ + offset?: number; + + /** + * Filter to a single delivery status. + */ + status?: 'pending' | 'delivered' | 'failed'; +} + +export declare namespace AICreditsWebhooksDeliveries { + export { + type AICreditsWebhooksDeliveryRetrieveResponse as AICreditsWebhooksDeliveryRetrieveResponse, + type AICreditsWebhooksDeliveryRetrieveParams as AICreditsWebhooksDeliveryRetrieveParams, + }; +} diff --git a/src/resources/projects/ai-credits-webhooks-deliveriesattempts.ts b/src/resources/projects/ai-credits-webhooks-deliveriesattempts.ts new file mode 100644 index 0000000..7605382 --- /dev/null +++ b/src/resources/projects/ai-credits-webhooks-deliveriesattempts.ts @@ -0,0 +1,73 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +/** + * AI credit balance, usage breakdown, and webhook/alert settings. Credits are pooled per workspace; settings are per project. + */ +export class AICreditsWebhooksDeliveriesattempts extends APIResource { + /** + * Returns the per-attempt history for a single delivery, newest attempt first — + * the response code, error, and time of each POST (including automatic retries). + * Returns 404 if the delivery is not found for this project. + */ + retrieve( + deliveryID: string, + params: AICreditsWebhooksDeliveriesattemptRetrieveParams, + options?: RequestOptions, + ): APIPromise { + const { id, ...query } = params; + return this._client.get(path`/v3/projects/${id}/ai-credits/webhooks/deliveries/${deliveryID}/attempts`, { + query, + ...options, + }); + } +} + +export interface AICreditsWebhooksDeliveriesattemptRetrieveResponse { + attempts?: Array; + + /** + * Total attempts for the delivery (ignores limit/offset). + */ + total?: number; +} + +export namespace AICreditsWebhooksDeliveriesattemptRetrieveResponse { + export interface Attempt { + attempt?: number; + + attempted_at?: string; + + error?: string | null; + + status_code?: number | null; + } +} + +export interface AICreditsWebhooksDeliveriesattemptRetrieveParams { + /** + * Path param: The project ID + */ + id: string; + + /** + * Query param: Max attempts to return (1-100). + */ + limit?: number; + + /** + * Query param: Number of attempts to skip (pagination). + */ + offset?: number; +} + +export declare namespace AICreditsWebhooksDeliveriesattempts { + export { + type AICreditsWebhooksDeliveriesattemptRetrieveResponse as AICreditsWebhooksDeliveriesattemptRetrieveResponse, + type AICreditsWebhooksDeliveriesattemptRetrieveParams as AICreditsWebhooksDeliveriesattemptRetrieveParams, + }; +} diff --git a/src/resources/projects/ai-credits-webhooks-deliveriesretry.ts b/src/resources/projects/ai-credits-webhooks-deliveriesretry.ts new file mode 100644 index 0000000..931522d --- /dev/null +++ b/src/resources/projects/ai-credits-webhooks-deliveriesretry.ts @@ -0,0 +1,46 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +/** + * AI credit balance, usage breakdown, and webhook/alert settings. Credits are pooled per workspace; settings are per project. + */ +export class AICreditsWebhooksDeliveriesretry extends APIResource { + /** + * Re-queues a single previously-failed (or pending) webhook delivery for another + * attempt. Returns 404 if the delivery is not found for this project, and 409 if + * it was already delivered. + */ + create( + deliveryID: string, + params: AICreditsWebhooksDeliveriesretryCreateParams, + options?: RequestOptions, + ): APIPromise { + const { id } = params; + return this._client.post( + path`/v3/projects/${id}/ai-credits/webhooks/deliveries/${deliveryID}/retry`, + options, + ); + } +} + +export interface AICreditsWebhooksDeliveriesretryCreateResponse { + status?: 'requeued'; +} + +export interface AICreditsWebhooksDeliveriesretryCreateParams { + /** + * The project ID + */ + id: string; +} + +export declare namespace AICreditsWebhooksDeliveriesretry { + export { + type AICreditsWebhooksDeliveriesretryCreateResponse as AICreditsWebhooksDeliveriesretryCreateResponse, + type AICreditsWebhooksDeliveriesretryCreateParams as AICreditsWebhooksDeliveriesretryCreateParams, + }; +} diff --git a/src/resources/projects/ai-credits.ts b/src/resources/projects/ai-credits.ts new file mode 100644 index 0000000..1c2d220 --- /dev/null +++ b/src/resources/projects/ai-credits.ts @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +/** + * AI credit balance, usage breakdown, and webhook/alert settings. Credits are pooled per workspace; settings are per project. + */ +export class AICredits extends APIResource { + /** + * Returns the current AI credit balance for the project. Credits are pooled per + * workspace — every project in a workspace shares one balance. Only credit counts + * are returned; token counts, model names, and costs are never exposed. + */ + retrieve(id: string, options?: RequestOptions): APIPromise { + return this._client.get(path`/v3/projects/${id}/ai-credits`, options); + } +} + +export interface AICreditRetrieveResponse { + /** + * AI credits remaining in the current period. + */ + credits_remaining?: number; + + /** + * Total AI credits available for the current period. + */ + credits_total?: number; + + /** + * AI credits consumed so far in the current period. + */ + credits_used?: number; + + /** + * When the current credit period resets, or null if there is no active billing + * period — including once a subscription is cancelled or its term has ended. + */ + reset_date?: string | null; +} + +export declare namespace AICredits { + export { type AICreditRetrieveResponse as AICreditRetrieveResponse }; +} diff --git a/src/resources/projects/index.ts b/src/resources/projects/index.ts new file mode 100644 index 0000000..5a8e575 --- /dev/null +++ b/src/resources/projects/index.ts @@ -0,0 +1,34 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export { AICredits, type AICreditRetrieveResponse } from './ai-credits'; +export { + AICreditsSettings, + type AICreditsSettingRetrieveResponse, + type AICreditsSettingUpdateResponse, + type AICreditsSettingUpdateParams, +} from './ai-credits-settings'; +export { + AICreditsSettingsRotateSecret, + type AICreditsSettingsRotateSecretCreateResponse, +} from './ai-credits-settings-rotate-secret'; +export { + AICreditsUsage, + type AICreditsUsageRetrieveResponse, + type AICreditsUsageRetrieveParams, +} from './ai-credits-usage'; +export { + AICreditsWebhooksDeliveries, + type AICreditsWebhooksDeliveryRetrieveResponse, + type AICreditsWebhooksDeliveryRetrieveParams, +} from './ai-credits-webhooks-deliveries'; +export { + AICreditsWebhooksDeliveriesattempts, + type AICreditsWebhooksDeliveriesattemptRetrieveResponse, + type AICreditsWebhooksDeliveriesattemptRetrieveParams, +} from './ai-credits-webhooks-deliveriesattempts'; +export { + AICreditsWebhooksDeliveriesretry, + type AICreditsWebhooksDeliveriesretryCreateResponse, + type AICreditsWebhooksDeliveriesretryCreateParams, +} from './ai-credits-webhooks-deliveriesretry'; +export { Projects, type ProjectRetrieveResponse } from './projects'; diff --git a/src/resources/projects/projects.ts b/src/resources/projects/projects.ts new file mode 100644 index 0000000..2a5de9e --- /dev/null +++ b/src/resources/projects/projects.ts @@ -0,0 +1,158 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import * as AICreditsAPI from './ai-credits'; +import { AICreditRetrieveResponse, AICredits } from './ai-credits'; +import * as AICreditsSettingsAPI from './ai-credits-settings'; +import { + AICreditsSettingRetrieveResponse, + AICreditsSettingUpdateParams, + AICreditsSettingUpdateResponse, + AICreditsSettings, +} from './ai-credits-settings'; +import * as AICreditsSettingsRotateSecretAPI from './ai-credits-settings-rotate-secret'; +import { + AICreditsSettingsRotateSecret, + AICreditsSettingsRotateSecretCreateResponse, +} from './ai-credits-settings-rotate-secret'; +import * as AICreditsUsageAPI from './ai-credits-usage'; +import { + AICreditsUsage, + AICreditsUsageRetrieveParams, + AICreditsUsageRetrieveResponse, +} from './ai-credits-usage'; +import * as AICreditsWebhooksDeliveriesAPI from './ai-credits-webhooks-deliveries'; +import { + AICreditsWebhooksDeliveries, + AICreditsWebhooksDeliveryRetrieveParams, + AICreditsWebhooksDeliveryRetrieveResponse, +} from './ai-credits-webhooks-deliveries'; +import * as AICreditsWebhooksDeliveriesattemptsAPI from './ai-credits-webhooks-deliveriesattempts'; +import { + AICreditsWebhooksDeliveriesattemptRetrieveParams, + AICreditsWebhooksDeliveriesattemptRetrieveResponse, + AICreditsWebhooksDeliveriesattempts, +} from './ai-credits-webhooks-deliveriesattempts'; +import * as AICreditsWebhooksDeliveriesretryAPI from './ai-credits-webhooks-deliveriesretry'; +import { + AICreditsWebhooksDeliveriesretry, + AICreditsWebhooksDeliveriesretryCreateParams, + AICreditsWebhooksDeliveriesretryCreateResponse, +} from './ai-credits-webhooks-deliveriesretry'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +/** + * Project details and configuration. + */ +export class Projects extends APIResource { + aiCredits: AICreditsAPI.AICredits = new AICreditsAPI.AICredits(this._client); + aiCreditsSettings: AICreditsSettingsAPI.AICreditsSettings = new AICreditsSettingsAPI.AICreditsSettings( + this._client, + ); + aiCreditsSettingsRotateSecret: AICreditsSettingsRotateSecretAPI.AICreditsSettingsRotateSecret = + new AICreditsSettingsRotateSecretAPI.AICreditsSettingsRotateSecret(this._client); + aiCreditsUsage: AICreditsUsageAPI.AICreditsUsage = new AICreditsUsageAPI.AICreditsUsage(this._client); + aiCreditsWebhooksDeliveries: AICreditsWebhooksDeliveriesAPI.AICreditsWebhooksDeliveries = + new AICreditsWebhooksDeliveriesAPI.AICreditsWebhooksDeliveries(this._client); + aiCreditsWebhooksDeliveriesattempts: AICreditsWebhooksDeliveriesattemptsAPI.AICreditsWebhooksDeliveriesattempts = + new AICreditsWebhooksDeliveriesattemptsAPI.AICreditsWebhooksDeliveriesattempts(this._client); + aiCreditsWebhooksDeliveriesretry: AICreditsWebhooksDeliveriesretryAPI.AICreditsWebhooksDeliveriesretry = + new AICreditsWebhooksDeliveriesretryAPI.AICreditsWebhooksDeliveriesretry(this._client); + + /** + * Get project details by ID. + */ + retrieve(id: string, options?: RequestOptions): APIPromise { + return this._client.get(path`/v3/projects/${id}`, options); + } +} + +export interface ProjectRetrieveResponse { + data?: ProjectRetrieveResponse.Data; +} + +export namespace ProjectRetrieveResponse { + export interface Data { + /** + * The project ID. + */ + id?: number; + + /** + * When the project was created. + */ + createdAt?: string; + + /** + * The project name. + */ + name?: string; + + /** + * The project status. + */ + status?: string; + + workspace?: Data.Workspace; + } + + export namespace Data { + export interface Workspace { + id?: number; + + name?: string; + } + } +} + +Projects.AICredits = AICredits; +Projects.AICreditsSettings = AICreditsSettings; +Projects.AICreditsSettingsRotateSecret = AICreditsSettingsRotateSecret; +Projects.AICreditsUsage = AICreditsUsage; +Projects.AICreditsWebhooksDeliveries = AICreditsWebhooksDeliveries; +Projects.AICreditsWebhooksDeliveriesattempts = AICreditsWebhooksDeliveriesattempts; +Projects.AICreditsWebhooksDeliveriesretry = AICreditsWebhooksDeliveriesretry; + +export declare namespace Projects { + export { type ProjectRetrieveResponse as ProjectRetrieveResponse }; + + export { AICredits as AICredits, type AICreditRetrieveResponse as AICreditRetrieveResponse }; + + export { + AICreditsSettings as AICreditsSettings, + type AICreditsSettingRetrieveResponse as AICreditsSettingRetrieveResponse, + type AICreditsSettingUpdateResponse as AICreditsSettingUpdateResponse, + type AICreditsSettingUpdateParams as AICreditsSettingUpdateParams, + }; + + export { + AICreditsSettingsRotateSecret as AICreditsSettingsRotateSecret, + type AICreditsSettingsRotateSecretCreateResponse as AICreditsSettingsRotateSecretCreateResponse, + }; + + export { + AICreditsUsage as AICreditsUsage, + type AICreditsUsageRetrieveResponse as AICreditsUsageRetrieveResponse, + type AICreditsUsageRetrieveParams as AICreditsUsageRetrieveParams, + }; + + export { + AICreditsWebhooksDeliveries as AICreditsWebhooksDeliveries, + type AICreditsWebhooksDeliveryRetrieveResponse as AICreditsWebhooksDeliveryRetrieveResponse, + type AICreditsWebhooksDeliveryRetrieveParams as AICreditsWebhooksDeliveryRetrieveParams, + }; + + export { + AICreditsWebhooksDeliveriesattempts as AICreditsWebhooksDeliveriesattempts, + type AICreditsWebhooksDeliveriesattemptRetrieveResponse as AICreditsWebhooksDeliveriesattemptRetrieveResponse, + type AICreditsWebhooksDeliveriesattemptRetrieveParams as AICreditsWebhooksDeliveriesattemptRetrieveParams, + }; + + export { + AICreditsWebhooksDeliveriesretry as AICreditsWebhooksDeliveriesretry, + type AICreditsWebhooksDeliveriesretryCreateResponse as AICreditsWebhooksDeliveriesretryCreateResponse, + type AICreditsWebhooksDeliveriesretryCreateParams as AICreditsWebhooksDeliveriesretryCreateParams, + }; +} diff --git a/tests/api-resources/projects/ai-credits-settings-rotate-secret.test.ts b/tests/api-resources/projects/ai-credits-settings-rotate-secret.test.ts new file mode 100644 index 0000000..bff10b9 --- /dev/null +++ b/tests/api-resources/projects/ai-credits-settings-rotate-secret.test.ts @@ -0,0 +1,21 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource aiCreditsSettingsRotateSecret', () => { + test('create', async () => { + const responsePromise = client.projects.aiCreditsSettingsRotateSecret.create('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); +}); diff --git a/tests/api-resources/projects/ai-credits-settings.test.ts b/tests/api-resources/projects/ai-credits-settings.test.ts new file mode 100644 index 0000000..a23a269 --- /dev/null +++ b/tests/api-resources/projects/ai-credits-settings.test.ts @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource aiCreditsSettings', () => { + test('retrieve', async () => { + const responsePromise = client.projects.aiCreditsSettings.retrieve('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('update', async () => { + const responsePromise = client.projects.aiCreditsSettings.update('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('update: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.projects.aiCreditsSettings.update( + 'id', + { + exhaustion_behavior: 'disable', + threshold_alerts: [1], + webhook_url: 'https://example.com', + }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(Unlayer.NotFoundError); + }); +}); diff --git a/tests/api-resources/projects/ai-credits-usage.test.ts b/tests/api-resources/projects/ai-credits-usage.test.ts new file mode 100644 index 0000000..58cde36 --- /dev/null +++ b/tests/api-resources/projects/ai-credits-usage.test.ts @@ -0,0 +1,41 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource aiCreditsUsage', () => { + test('retrieve', async () => { + const responsePromise = client.projects.aiCreditsUsage.retrieve('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('retrieve: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.projects.aiCreditsUsage.retrieve( + 'id', + { + end: '7321-69-10', + end_user_id: 'end_user_id', + feature_type: 'full_template_gen', + limit: 1, + offset: 0, + order: 'asc', + sort: 'credits', + start: '7321-69-10', + }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(Unlayer.NotFoundError); + }); +}); diff --git a/tests/api-resources/projects/ai-credits-webhooks-deliveries.test.ts b/tests/api-resources/projects/ai-credits-webhooks-deliveries.test.ts new file mode 100644 index 0000000..17178f1 --- /dev/null +++ b/tests/api-resources/projects/ai-credits-webhooks-deliveries.test.ts @@ -0,0 +1,37 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource aiCreditsWebhooksDeliveries', () => { + test('retrieve', async () => { + const responsePromise = client.projects.aiCreditsWebhooksDeliveries.retrieve('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('retrieve: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.projects.aiCreditsWebhooksDeliveries.retrieve( + 'id', + { + event: 'ai.credits.usage_recorded', + limit: 1, + offset: 0, + status: 'pending', + }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(Unlayer.NotFoundError); + }); +}); diff --git a/tests/api-resources/projects/ai-credits-webhooks-deliveriesattempts.test.ts b/tests/api-resources/projects/ai-credits-webhooks-deliveriesattempts.test.ts new file mode 100644 index 0000000..bacd5f8 --- /dev/null +++ b/tests/api-resources/projects/ai-credits-webhooks-deliveriesattempts.test.ts @@ -0,0 +1,31 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource aiCreditsWebhooksDeliveriesattempts', () => { + test('retrieve: only required params', async () => { + const responsePromise = client.projects.aiCreditsWebhooksDeliveriesattempts.retrieve('deliveryId', { + id: 'id', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('retrieve: required and optional params', async () => { + const response = await client.projects.aiCreditsWebhooksDeliveriesattempts.retrieve('deliveryId', { + id: 'id', + limit: 1, + offset: 0, + }); + }); +}); diff --git a/tests/api-resources/projects/ai-credits-webhooks-deliveriesretry.test.ts b/tests/api-resources/projects/ai-credits-webhooks-deliveriesretry.test.ts new file mode 100644 index 0000000..2c1e805 --- /dev/null +++ b/tests/api-resources/projects/ai-credits-webhooks-deliveriesretry.test.ts @@ -0,0 +1,29 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource aiCreditsWebhooksDeliveriesretry', () => { + test('create: only required params', async () => { + const responsePromise = client.projects.aiCreditsWebhooksDeliveriesretry.create('deliveryId', { + id: 'id', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('create: required and optional params', async () => { + const response = await client.projects.aiCreditsWebhooksDeliveriesretry.create('deliveryId', { + id: 'id', + }); + }); +}); diff --git a/tests/api-resources/projects/ai-credits.test.ts b/tests/api-resources/projects/ai-credits.test.ts new file mode 100644 index 0000000..377cc15 --- /dev/null +++ b/tests/api-resources/projects/ai-credits.test.ts @@ -0,0 +1,21 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource aiCredits', () => { + test('retrieve', async () => { + const responsePromise = client.projects.aiCredits.retrieve('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); +}); diff --git a/tests/api-resources/projects.test.ts b/tests/api-resources/projects/projects.test.ts similarity index 100% rename from tests/api-resources/projects.test.ts rename to tests/api-resources/projects/projects.test.ts From 2b13ee9e263cacd8d791eb6de9c2fe88f7328a99 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:05:13 +0000 Subject: [PATCH 47/54] feat(api): api update --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 443cc05..957aa46 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1228,9 +1228,9 @@ baseline-browser-mapping@^2.9.0: integrity sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg== brace-expansion@^2.0.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.2.tgz#0bba2271feb7d458b0d31ad13625aaa4754431e2" - integrity sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA== + version "2.1.3" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.3.tgz#1bf69aacdf6a4380ca17c284d9f928d4aa6401bc" + integrity sha512-DRdx5neNsG/QXbniLFWi2YmC/68oeOOmKz6zOjVk6ZS1ZLXgLIKqVEc6hWsmkjBbgii0SwaBTcJ5XKj5gzY/4A== dependencies: balanced-match "^1.0.0" From c97d13de152cd021801d74ed4753c8a3b9da78e6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 02:11:07 +0000 Subject: [PATCH 48/54] fix(stlc): stop hand-edited CI workflows from blocking seals and builds Editing the generated CI/release workflows in an SDK repo is supported: stlc writes them once and then leaves them under the repo's ownership. Two safety checks did not account for that and could refuse to seal custom code, or refuse to build, over workflow edits that were never at risk of being overwritten. The seal refusal could not be cleared by rebuilding. Sealing is also now recorded in place rather than under a new filename each time, so an interrupted build can no longer leave a workspace with no record of its sealed custom code, and sealing one branch no longer discards the record for another. --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 957aa46..f16b683 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1228,9 +1228,9 @@ baseline-browser-mapping@^2.9.0: integrity sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg== brace-expansion@^2.0.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.3.tgz#1bf69aacdf6a4380ca17c284d9f928d4aa6401bc" - integrity sha512-DRdx5neNsG/QXbniLFWi2YmC/68oeOOmKz6zOjVk6ZS1ZLXgLIKqVEc6hWsmkjBbgii0SwaBTcJ5XKj5gzY/4A== + version "2.1.4" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.4.tgz#589dab11c0018d0366be64cd8bf12c8dbecc8326" + integrity sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg== dependencies: balanced-match "^1.0.0" From 8623c2748b9a625986cd5f605f76f7064a4f0c72 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 2 Aug 2026 08:03:13 +0000 Subject: [PATCH 49/54] feat(api): api update --- .stats.yml | 8 +- api.md | 160 +++++++ src/client.ts | 61 +++ src/resources/domains.ts | 3 + src/resources/domains/domains.ts | 160 +++++++ src/resources/domains/index.ts | 11 + src/resources/domains/verify.ts | 51 +++ src/resources/emails.ts | 3 + src/resources/emails/emails.ts | 414 ++++++++++++++++++ src/resources/emails/events.ts | 38 ++ src/resources/emails/index.ts | 34 ++ src/resources/emails/render.ts | 49 +++ src/resources/emails/settings.ts | 86 ++++ src/resources/emails/stats.ts | 120 +++++ src/resources/emails/suppressions-check.ts | 49 +++ src/resources/emails/suppressions.ts | 126 ++++++ src/resources/emails/template.ts | 201 +++++++++ src/resources/index.ts | 26 ++ .../templates/convert-full-to-simple.ts | 6 + .../templates/convert-simple-to-full.ts | 6 + src/resources/templates/index.ts | 2 + src/resources/templates/schema.ts | 44 ++ src/resources/templates/templates.ts | 16 + src/resources/templates/validate.ts | 132 ++++++ src/resources/webhooks.ts | 3 + src/resources/webhooks/index.ts | 13 + src/resources/webhooks/rotate-secret.ts | 37 ++ src/resources/webhooks/webhooks.ts | 255 +++++++++++ tests/api-resources/domains/domains.test.ts | 58 +++ tests/api-resources/domains/verify.test.ts | 21 + tests/api-resources/emails/emails.test.ts | 90 ++++ tests/api-resources/emails/events.test.ts | 21 + tests/api-resources/emails/render.test.ts | 28 ++ tests/api-resources/emails/settings.test.ts | 42 ++ tests/api-resources/emails/stats.test.ts | 35 ++ .../emails/suppressions-check.test.ts | 28 ++ .../api-resources/emails/suppressions.test.ts | 65 +++ tests/api-resources/emails/template.test.ts | 49 +++ tests/api-resources/templates/schema.test.ts | 31 ++ .../api-resources/templates/validate.test.ts | 40 ++ .../webhooks/rotate-secret.test.ts | 21 + tests/api-resources/webhooks/webhooks.test.ts | 88 ++++ 42 files changed, 2727 insertions(+), 4 deletions(-) create mode 100644 src/resources/domains.ts create mode 100644 src/resources/domains/domains.ts create mode 100644 src/resources/domains/index.ts create mode 100644 src/resources/domains/verify.ts create mode 100644 src/resources/emails.ts create mode 100644 src/resources/emails/emails.ts create mode 100644 src/resources/emails/events.ts create mode 100644 src/resources/emails/index.ts create mode 100644 src/resources/emails/render.ts create mode 100644 src/resources/emails/settings.ts create mode 100644 src/resources/emails/stats.ts create mode 100644 src/resources/emails/suppressions-check.ts create mode 100644 src/resources/emails/suppressions.ts create mode 100644 src/resources/emails/template.ts create mode 100644 src/resources/templates/schema.ts create mode 100644 src/resources/templates/validate.ts create mode 100644 src/resources/webhooks.ts create mode 100644 src/resources/webhooks/index.ts create mode 100644 src/resources/webhooks/rotate-secret.ts create mode 100644 src/resources/webhooks/webhooks.ts create mode 100644 tests/api-resources/domains/domains.test.ts create mode 100644 tests/api-resources/domains/verify.test.ts create mode 100644 tests/api-resources/emails/emails.test.ts create mode 100644 tests/api-resources/emails/events.test.ts create mode 100644 tests/api-resources/emails/render.test.ts create mode 100644 tests/api-resources/emails/settings.test.ts create mode 100644 tests/api-resources/emails/stats.test.ts create mode 100644 tests/api-resources/emails/suppressions-check.test.ts create mode 100644 tests/api-resources/emails/suppressions.test.ts create mode 100644 tests/api-resources/emails/template.test.ts create mode 100644 tests/api-resources/templates/schema.test.ts create mode 100644 tests/api-resources/templates/validate.test.ts create mode 100644 tests/api-resources/webhooks/rotate-secret.test.ts create mode 100644 tests/api-resources/webhooks/webhooks.test.ts diff --git a/.stats.yml b/.stats.yml index 568730e..b5c4710 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 24 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-b42187e1b2fff52630a33946829067dcc76dd842a7fb85d826ee9ccc4f44175d.yml -openapi_spec_hash: 7f0c95f3bb404716e0a77878c0c21b91 -config_hash: ee438ad5d5e9b8446d21fc7cb50eff95 +configured_endpoints: 50 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-112356d364a4c9b7cf7f8d272937732568d750f9574ecc0c07516eca112cdddf.yml +openapi_spec_hash: 283e15c9bc02c4d7c69189873b1da9d4 +config_hash: c65b47a2f20400d392a50837c0a10945 diff --git a/api.md b/api.md index b795cb7..66d02c4 100644 --- a/api.md +++ b/api.md @@ -1,3 +1,29 @@ +# Domains + +Types: + +- DomainCreateResponse +- DomainRetrieveResponse +- DomainListResponse +- DomainDeleteResponse + +Methods: + +- client.domains.create({ ...params }) -> DomainCreateResponse +- client.domains.retrieve(id) -> DomainRetrieveResponse +- client.domains.list() -> DomainListResponse +- client.domains.delete(id) -> DomainDeleteResponse + +## Verify + +Types: + +- VerifyCreateResponse + +Methods: + +- client.domains.verify.create(id) -> VerifyCreateResponse + # EditorSessions Types: @@ -8,6 +34,96 @@ Methods: - client.editorSessions.create({ ...params }) -> EditorSessionCreateResponse +# Emails + +Types: + +- EmailCreateResponse +- EmailRetrieveResponse +- EmailListResponse + +Methods: + +- client.emails.create({ ...params }) -> EmailCreateResponse +- client.emails.retrieve(id) -> EmailRetrieveResponse +- client.emails.list({ ...params }) -> EmailListResponse + +## Events + +Types: + +- EventRetrieveResponse + +Methods: + +- client.emails.events.retrieve(id) -> EventRetrieveResponse + +## Render + +Types: + +- RenderCreateResponse + +Methods: + +- client.emails.render.create({ ...params }) -> RenderCreateResponse + +## Settings + +Types: + +- SettingRetrieveResponse +- SettingUpdateResponse + +Methods: + +- client.emails.settings.retrieve() -> SettingRetrieveResponse +- client.emails.settings.update({ ...params }) -> SettingUpdateResponse + +## Stats + +Types: + +- StatRetrieveResponse + +Methods: + +- client.emails.stats.retrieve({ ...params }) -> StatRetrieveResponse + +## Suppressions + +Types: + +- SuppressionCreateResponse +- SuppressionRetrieveResponse +- SuppressionDeleteResponse + +Methods: + +- client.emails.suppressions.create({ ...params }) -> SuppressionCreateResponse +- client.emails.suppressions.retrieve({ ...params }) -> SuppressionRetrieveResponse +- client.emails.suppressions.delete({ ...params }) -> SuppressionDeleteResponse + +## SuppressionsCheck + +Types: + +- SuppressionsCheckRetrieveResponse + +Methods: + +- client.emails.suppressionsCheck.retrieve({ ...params }) -> SuppressionsCheckRetrieveResponse + +## Template + +Types: + +- TemplateCreateResponse + +Methods: + +- client.emails.template.create({ ...params }) -> TemplateCreateResponse + # Me ## Subscription @@ -195,6 +311,50 @@ Methods: - client.templates.import.create({ ...params }) -> ImportCreateResponse +## Schema + +Methods: + +- client.templates.schema.retrieve({ ...params }) -> void + +## Validate + +Types: + +- ValidateCreateResponse + +Methods: + +- client.templates.validate.create({ ...params }) -> ValidateCreateResponse + +# Webhooks + +Types: + +- WebhookCreateResponse +- WebhookRetrieveResponse +- WebhookUpdateResponse +- WebhookListResponse +- WebhookDeleteResponse + +Methods: + +- client.webhooks.create({ ...params }) -> WebhookCreateResponse +- client.webhooks.retrieve(id) -> WebhookRetrieveResponse +- client.webhooks.update(id, { ...params }) -> WebhookUpdateResponse +- client.webhooks.list() -> WebhookListResponse +- client.webhooks.delete(id) -> WebhookDeleteResponse + +## RotateSecret + +Types: + +- RotateSecretCreateResponse + +Methods: + +- client.webhooks.rotateSecret.create(id) -> RotateSecretCreateResponse + # Workspaces Types: diff --git a/src/client.ts b/src/client.ts index 5440607..be8e975 100644 --- a/src/client.ts +++ b/src/client.ts @@ -25,6 +25,22 @@ import { EditorSessions, } from './resources/editor-sessions'; import { WorkspaceListResponse, WorkspaceRetrieveResponse, Workspaces } from './resources/workspaces'; +import { + DomainCreateParams, + DomainCreateResponse, + DomainDeleteResponse, + DomainListResponse, + DomainRetrieveResponse, + Domains, +} from './resources/domains/domains'; +import { + EmailCreateParams, + EmailCreateResponse, + EmailListParams, + EmailListResponse, + EmailRetrieveResponse, + Emails, +} from './resources/emails/emails'; import { Me } from './resources/me/me'; import { ProjectRetrieveResponse, Projects } from './resources/projects/projects'; import { @@ -35,6 +51,16 @@ import { TemplateRetrieveResponse, Templates, } from './resources/templates/templates'; +import { + WebhookCreateParams, + WebhookCreateResponse, + WebhookDeleteResponse, + WebhookListResponse, + WebhookRetrieveResponse, + WebhookUpdateParams, + WebhookUpdateResponse, + Webhooks, +} from './resources/webhooks/webhooks'; import { type Fetch } from './internal/builtin-types'; import { HeadersLike, NullableHeaders, buildHeaders } from './internal/headers'; import { FinalRequestOptions, RequestOptions } from './internal/request-options'; @@ -826,7 +852,9 @@ export class Unlayer { static toFile = Uploads.toFile; + domains: API.Domains = new API.Domains(this); editorSessions: API.EditorSessions = new API.EditorSessions(this); + emails: API.Emails = new API.Emails(this); me: API.Me = new API.Me(this); /** * Project details and configuration. @@ -836,16 +864,20 @@ export class Unlayer { * Template management — list, retrieve, generate, import, export, and convert designs. */ templates: API.Templates = new API.Templates(this); + webhooks: API.Webhooks = new API.Webhooks(this); /** * Workspace access and management. */ workspaces: API.Workspaces = new API.Workspaces(this); } +Unlayer.Domains = Domains; Unlayer.EditorSessions = EditorSessions; +Unlayer.Emails = Emails; Unlayer.Me = Me; Unlayer.Projects = Projects; Unlayer.Templates = Templates; +Unlayer.Webhooks = Webhooks; Unlayer.Workspaces = Workspaces; export declare namespace Unlayer { @@ -854,12 +886,30 @@ export declare namespace Unlayer { export import CursorPage = Pagination.CursorPage; export { type CursorPageParams as CursorPageParams, type CursorPageResponse as CursorPageResponse }; + export { + Domains as Domains, + type DomainCreateResponse as DomainCreateResponse, + type DomainRetrieveResponse as DomainRetrieveResponse, + type DomainListResponse as DomainListResponse, + type DomainDeleteResponse as DomainDeleteResponse, + type DomainCreateParams as DomainCreateParams, + }; + export { EditorSessions as EditorSessions, type EditorSessionCreateResponse as EditorSessionCreateResponse, type EditorSessionCreateParams as EditorSessionCreateParams, }; + export { + Emails as Emails, + type EmailCreateResponse as EmailCreateResponse, + type EmailRetrieveResponse as EmailRetrieveResponse, + type EmailListResponse as EmailListResponse, + type EmailCreateParams as EmailCreateParams, + type EmailListParams as EmailListParams, + }; + export { Me as Me }; export { Projects as Projects, type ProjectRetrieveResponse as ProjectRetrieveResponse }; @@ -873,6 +923,17 @@ export declare namespace Unlayer { type TemplateListParams as TemplateListParams, }; + export { + Webhooks as Webhooks, + type WebhookCreateResponse as WebhookCreateResponse, + type WebhookRetrieveResponse as WebhookRetrieveResponse, + type WebhookUpdateResponse as WebhookUpdateResponse, + type WebhookListResponse as WebhookListResponse, + type WebhookDeleteResponse as WebhookDeleteResponse, + type WebhookCreateParams as WebhookCreateParams, + type WebhookUpdateParams as WebhookUpdateParams, + }; + export { Workspaces as Workspaces, type WorkspaceRetrieveResponse as WorkspaceRetrieveResponse, diff --git a/src/resources/domains.ts b/src/resources/domains.ts new file mode 100644 index 0000000..5c8cf7e --- /dev/null +++ b/src/resources/domains.ts @@ -0,0 +1,3 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export * from './domains/index'; diff --git a/src/resources/domains/domains.ts b/src/resources/domains/domains.ts new file mode 100644 index 0000000..bbba212 --- /dev/null +++ b/src/resources/domains/domains.ts @@ -0,0 +1,160 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import * as VerifyAPI from './verify'; +import { Verify, VerifyCreateResponse } from './verify'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +export class Domains extends APIResource { + verify: VerifyAPI.Verify = new VerifyAPI.Verify(this._client); + + /** + * Register a sender domain shared by every Developer Email API project in the + * workspace. Requires a personal access token belonging to a workspace owner or + * admin. Verification requires the workspace-specific TXT record and the returned + * SES DKIM records. + */ + create(body: DomainCreateParams, options?: RequestOptions): APIPromise { + return this._client.post('/v3/domains', { body, ...options }); + } + + /** + * Get the ownership TXT challenge and SES DKIM records for a sender domain shared + * by every Developer Email API project in the workspace. Requires a personal + * access token belonging to a workspace owner or admin. + */ + retrieve(id: string, options?: RequestOptions): APIPromise { + return this._client.get(path`/v3/domains/${id}`, options); + } + + /** + * List sender domains shared by every Developer Email API project in the + * workspace. Requires a personal access token belonging to a workspace owner or + * admin; project API keys cannot manage domains. + */ + list(options?: RequestOptions): APIPromise { + return this._client.get('/v3/domains', options); + } + + /** + * Delete a sender domain shared by every Developer Email API project in the + * workspace. Requires a personal access token belonging to a workspace owner or + * admin. The SES identity remains so a later reconciler can clean it up safely. + */ + delete(id: string, options?: RequestOptions): APIPromise { + return this._client.delete(path`/v3/domains/${id}`, options); + } +} + +export interface DomainCreateResponse { + data: DomainCreateResponse.Data; +} + +export namespace DomainCreateResponse { + export interface Data { + id?: number; + + createdAt?: string; + + dkimTokens?: Array; + + dnsRecords?: Array; + + domain?: string; + + status?: 'pending' | 'verified' | 'failed'; + } + + export namespace Data { + export interface DNSRecord { + name?: string; + + purpose?: string; + + type?: string; + + value?: string; + } + } +} + +export interface DomainRetrieveResponse { + data: DomainRetrieveResponse.Data; +} + +export namespace DomainRetrieveResponse { + export interface Data { + id?: number; + + createdAt?: string; + + dkimTokens?: Array; + + dnsRecords?: Array; + + domain?: string; + + status?: string; + } + + export namespace Data { + export interface DNSRecord { + name?: string; + + purpose?: string; + + type?: string; + + value?: string; + } + } +} + +export interface DomainListResponse { + data: Array; +} + +export namespace DomainListResponse { + export interface Data { + id?: number; + + createdAt?: string; + + domain?: string; + + status?: 'pending' | 'verified' | 'failed'; + } +} + +export interface DomainDeleteResponse { + data?: DomainDeleteResponse.Data; +} + +export namespace DomainDeleteResponse { + export interface Data { + success?: boolean; + } +} + +export interface DomainCreateParams { + /** + * Domain name to register, such as example.com. + */ + domain: string; +} + +Domains.Verify = Verify; + +export declare namespace Domains { + export { + type DomainCreateResponse as DomainCreateResponse, + type DomainRetrieveResponse as DomainRetrieveResponse, + type DomainListResponse as DomainListResponse, + type DomainDeleteResponse as DomainDeleteResponse, + type DomainCreateParams as DomainCreateParams, + }; + + export { Verify as Verify, type VerifyCreateResponse as VerifyCreateResponse }; +} diff --git a/src/resources/domains/index.ts b/src/resources/domains/index.ts new file mode 100644 index 0000000..fae2293 --- /dev/null +++ b/src/resources/domains/index.ts @@ -0,0 +1,11 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export { + Domains, + type DomainCreateResponse, + type DomainRetrieveResponse, + type DomainListResponse, + type DomainDeleteResponse, + type DomainCreateParams, +} from './domains'; +export { Verify, type VerifyCreateResponse } from './verify'; diff --git a/src/resources/domains/verify.ts b/src/resources/domains/verify.ts new file mode 100644 index 0000000..c11fe69 --- /dev/null +++ b/src/resources/domains/verify.ts @@ -0,0 +1,51 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +export class Verify extends APIResource { + /** + * Verify the ownership TXT challenge and SES DKIM identity for a sender domain + * shared by every Developer Email API project in the workspace. Requires a + * personal access token belonging to a workspace owner or admin. + */ + create(id: string, options?: RequestOptions): APIPromise { + return this._client.post(path`/v3/domains/${id}/verify`, options); + } +} + +export interface VerifyCreateResponse { + data: VerifyCreateResponse.Data; +} + +export namespace VerifyCreateResponse { + export interface Data { + id?: number; + + dkim?: Data.Dkim; + + domain?: string; + + ownership?: Data.Ownership; + + status?: string; + } + + export namespace Data { + export interface Dkim { + status?: string; + + tokens?: Array; + } + + export interface Ownership { + verified?: boolean; + } + } +} + +export declare namespace Verify { + export { type VerifyCreateResponse as VerifyCreateResponse }; +} diff --git a/src/resources/emails.ts b/src/resources/emails.ts new file mode 100644 index 0000000..bd0ec59 --- /dev/null +++ b/src/resources/emails.ts @@ -0,0 +1,3 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export * from './emails/index'; diff --git a/src/resources/emails/emails.ts b/src/resources/emails/emails.ts new file mode 100644 index 0000000..ffecad2 --- /dev/null +++ b/src/resources/emails/emails.ts @@ -0,0 +1,414 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import * as EventsAPI from './events'; +import { EventRetrieveResponse, Events } from './events'; +import * as RenderAPI from './render'; +import { Render, RenderCreateParams, RenderCreateResponse } from './render'; +import * as SettingsAPI from './settings'; +import { SettingRetrieveResponse, SettingUpdateParams, SettingUpdateResponse, Settings } from './settings'; +import * as StatsAPI from './stats'; +import { StatRetrieveParams, StatRetrieveResponse, Stats } from './stats'; +import * as SuppressionsAPI from './suppressions'; +import { + SuppressionCreateParams, + SuppressionCreateResponse, + SuppressionDeleteParams, + SuppressionDeleteResponse, + SuppressionRetrieveParams, + SuppressionRetrieveResponse, + Suppressions, +} from './suppressions'; +import * as SuppressionsCheckAPI from './suppressions-check'; +import { + SuppressionsCheck, + SuppressionsCheckRetrieveParams, + SuppressionsCheckRetrieveResponse, +} from './suppressions-check'; +import * as TemplateAPI from './template'; +import { Template, TemplateCreateParams, TemplateCreateResponse } from './template'; +import { APIPromise } from '../../core/api-promise'; +import { buildHeaders } from '../../internal/headers'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +export class Emails extends APIResource { + events: EventsAPI.Events = new EventsAPI.Events(this._client); + render: RenderAPI.Render = new RenderAPI.Render(this._client); + settings: SettingsAPI.Settings = new SettingsAPI.Settings(this._client); + stats: StatsAPI.Stats = new StatsAPI.Stats(this._client); + suppressions: SuppressionsAPI.Suppressions = new SuppressionsAPI.Suppressions(this._client); + suppressionsCheck: SuppressionsCheckAPI.SuppressionsCheck = new SuppressionsCheckAPI.SuppressionsCheck( + this._client, + ); + template: TemplateAPI.Template = new TemplateAPI.Template(this._client); + + /** + * Send a transactional email with raw HTML content. The sender domain must be + * verified in the project workspace; verified sender domains are shared by every + * Developer Email API project in that workspace. + */ + create(params: EmailCreateParams, options?: RequestOptions): APIPromise { + const { 'idempotency-key': idempotencyKey, ...body } = params; + return this._client.post('/v3/emails', { + body, + ...options, + headers: buildHeaders([ + { ...(idempotencyKey != null ? { 'idempotency-key': idempotencyKey } : undefined) }, + options?.headers, + ]), + }); + } + + /** + * Retrieve details of a sent email, including its current delivery status, during + * the rolling 90-day history window. Expired emails return 404. + */ + retrieve(id: string, options?: RequestOptions): APIPromise { + return this._client.get(path`/v3/emails/${id}`, options); + } + + /** + * List emails sent from this project within the rolling 90-day history window. + * Without a status filter, results and date bounds use acceptance time. With a + * status filter, results and date bounds use the time each email entered that + * status. + */ + list( + query: EmailListParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.get('/v3/emails', { query, ...options }); + } +} + +/** + * Email accepted and queued for delivery + */ +export interface EmailCreateResponse { + data: EmailCreateResponse.Data; +} + +export namespace EmailCreateResponse { + export interface Data { + /** + * Unique email ID (UUID). Use this with GET /v3/emails/:id to retrieve delivery + * status and events. + */ + id?: string; + + /** + * When the email was accepted and queued for delivery (ISO-8601). + */ + createdAt?: string; + + /** + * The sender address the email was sent from, either a plain email or "Name + * " format. + */ + from?: string; + + /** + * Usually "queued" for a fresh send. An idempotent replay of a previously accepted + * request returns that email's current status instead. Use webhooks or GET + * /v3/emails/:id for live delivery status. + */ + status?: 'queued' | 'sending' | 'sent' | 'delivered' | 'bounced' | 'complained' | 'failed'; + + /** + * The subject line of the email that was sent. + */ + subject?: string; + + /** + * The single accepted recipient address. + */ + to?: Array; + } +} + +export interface EmailRetrieveResponse { + data: EmailRetrieveResponse.Data; +} + +export namespace EmailRetrieveResponse { + export interface Data { + id?: string; + + bcc?: Array | null; + + cc?: Array | null; + + createdAt?: string; + + failureReason?: string | null; + + from?: string; + + status?: string; + + subject?: string | null; + + tags?: { [key: string]: string } | null; + + to?: unknown; + } +} + +export interface EmailListResponse { + data: Array; + + /** + * Whether there are more results after this page + */ + has_more: boolean; + + /** + * Cursor for the next page. Null if no more results. + */ + next_cursor?: string | null; +} + +export namespace EmailListResponse { + export interface Data { + id?: string; + + createdAt?: string; + + from?: string; + + status?: string; + + /** + * When the email entered its current status. For a newly queued email, this equals + * createdAt. + */ + statusUpdatedAt?: string; + + subject?: string | null; + + to?: unknown; + } +} + +export interface EmailCreateParams { + /** + * Body param: Sender email address or "Name " format. Domain must be + * verified. + */ + from: string; + + /** + * Body param: HTML content of the email + */ + html: string; + + /** + * Body param: Email subject line + */ + subject: string; + + /** + * Body param: Exactly one recipient. Each request creates one independently + * tracked delivery. + */ + to: Array; + + /** + * Body param: File attachments. Max 10 files per email, max 5 MB total payload + * size (including headers and base64 overhead). + */ + attachments?: Array; + + /** + * Body param: BCC is not supported by this endpoint. + */ + bcc?: Array; + + /** + * Body param: CC is not supported by this endpoint. + */ + cc?: Array; + + /** + * Body param: Custom email headers. Up to 9 printable-ASCII X-\* headers are + * allowed (e.g. {"X-Entity-Ref-ID": "abc123"}). Header names may contain up to 126 + * characters and each name plus value may contain up to 996 characters. + */ + headers?: { [key: string]: string }; + + /** + * Body param: Reply-To email address + */ + replyTo?: string; + + /** + * Body param: Key-value tags for categorizing the email (e.g. {"campaign": + * "welcome"}). Max 10 tags. Keys (1-64 chars) and values (up to 256 chars) may + * only contain letters, numbers, underscores, and hyphens (the Amazon SES + * message-tag character set). + */ + tags?: { [key: string]: string }; + + /** + * Body param: Plain text version of the email. If provided, a + * multipart/alternative message is sent. + */ + text?: string; + + /** + * Header param: Unique key for idempotent sends (max 255 characters). If provided, + * duplicate requests within 24 hours return the cached response. + */ + 'idempotency-key'?: string; +} + +export namespace EmailCreateParams { + export interface Attachment { + /** + * Base64-encoded file content. Whitespace and MIME line wrapping are removed + * before validation; invalid base64 is rejected with a 400 error. + */ + content: string; + + /** + * MIME type of the attachment. Required; must be one of the allowed types. + */ + contentType: + | 'application/pdf' + | 'application/zip' + | 'application/json' + | 'application/xml' + | 'application/csv' + | 'application/msword' + | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' + | 'application/vnd.ms-excel' + | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' + | 'application/vnd.ms-powerpoint' + | 'application/vnd.openxmlformats-officedocument.presentationml.presentation' + | 'text/plain' + | 'text/html' + | 'text/csv' + | 'text/xml' + | 'text/calendar' + | 'image/png' + | 'image/jpeg' + | 'image/gif' + | 'image/webp' + | 'image/svg+xml' + | 'audio/mpeg' + | 'audio/wav' + | 'video/mp4'; + + /** + * The filename as it will appear to the recipient. Line breaks are rejected; + * quotes are stripped before it is written into the message. + */ + filename: string; + } +} + +export interface EmailListParams { + /** + * Pagination cursor from previous response + */ + cursor?: string; + + /** + * Start date (ISO date). Bounds acceptance time normally, or status transition + * time when status is supplied. + */ + from?: string; + + /** + * Number of emails to return (1-100) + */ + limit?: number; + + /** + * Project ID (auto-resolved for API key auth) + */ + projectId?: string; + + /** + * Search recipient addresses and subjects by case-sensitive substring + */ + search?: string; + + /** + * Filter by email delivery status + */ + status?: 'queued' | 'sending' | 'sent' | 'delivered' | 'bounced' | 'complained' | 'failed'; + + /** + * Filter by tag in "key=value" format (e.g. "campaign=welcome") + */ + tag?: string; + + /** + * End date (ISO date). Bounds acceptance time normally, or status transition time + * when status is supplied. + */ + to?: string; +} + +Emails.Events = Events; +Emails.Render = Render; +Emails.Settings = Settings; +Emails.Stats = Stats; +Emails.Suppressions = Suppressions; +Emails.SuppressionsCheck = SuppressionsCheck; +Emails.Template = Template; + +export declare namespace Emails { + export { + type EmailCreateResponse as EmailCreateResponse, + type EmailRetrieveResponse as EmailRetrieveResponse, + type EmailListResponse as EmailListResponse, + type EmailCreateParams as EmailCreateParams, + type EmailListParams as EmailListParams, + }; + + export { Events as Events, type EventRetrieveResponse as EventRetrieveResponse }; + + export { + Render as Render, + type RenderCreateResponse as RenderCreateResponse, + type RenderCreateParams as RenderCreateParams, + }; + + export { + Settings as Settings, + type SettingRetrieveResponse as SettingRetrieveResponse, + type SettingUpdateResponse as SettingUpdateResponse, + type SettingUpdateParams as SettingUpdateParams, + }; + + export { + Stats as Stats, + type StatRetrieveResponse as StatRetrieveResponse, + type StatRetrieveParams as StatRetrieveParams, + }; + + export { + Suppressions as Suppressions, + type SuppressionCreateResponse as SuppressionCreateResponse, + type SuppressionRetrieveResponse as SuppressionRetrieveResponse, + type SuppressionDeleteResponse as SuppressionDeleteResponse, + type SuppressionCreateParams as SuppressionCreateParams, + type SuppressionRetrieveParams as SuppressionRetrieveParams, + type SuppressionDeleteParams as SuppressionDeleteParams, + }; + + export { + SuppressionsCheck as SuppressionsCheck, + type SuppressionsCheckRetrieveResponse as SuppressionsCheckRetrieveResponse, + type SuppressionsCheckRetrieveParams as SuppressionsCheckRetrieveParams, + }; + + export { + Template as Template, + type TemplateCreateResponse as TemplateCreateResponse, + type TemplateCreateParams as TemplateCreateParams, + }; +} diff --git a/src/resources/emails/events.ts b/src/resources/emails/events.ts new file mode 100644 index 0000000..3fe709a --- /dev/null +++ b/src/resources/emails/events.ts @@ -0,0 +1,38 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +export class Events extends APIResource { + /** + * Retrieve the operational event timeline for a sent email, showing send, + * delivery, bounce, and complaint events in chronological order during the rolling + * 90-day history window. Expired emails return 404. + */ + retrieve(id: string, options?: RequestOptions): APIPromise { + return this._client.get(path`/v3/emails/${id}/events`, options); + } +} + +export interface EventRetrieveResponse { + data: Array; +} + +export namespace EventRetrieveResponse { + export interface Data { + metadata?: { [key: string]: unknown } | null; + + timestamp?: string; + + /** + * Event type (send, delivery, bounce, complaint) + */ + type?: string; + } +} + +export declare namespace Events { + export { type EventRetrieveResponse as EventRetrieveResponse }; +} diff --git a/src/resources/emails/index.ts b/src/resources/emails/index.ts new file mode 100644 index 0000000..c0b2b7a --- /dev/null +++ b/src/resources/emails/index.ts @@ -0,0 +1,34 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export { + Emails, + type EmailCreateResponse, + type EmailRetrieveResponse, + type EmailListResponse, + type EmailCreateParams, + type EmailListParams, +} from './emails'; +export { Events, type EventRetrieveResponse } from './events'; +export { Render, type RenderCreateResponse, type RenderCreateParams } from './render'; +export { + Settings, + type SettingRetrieveResponse, + type SettingUpdateResponse, + type SettingUpdateParams, +} from './settings'; +export { Stats, type StatRetrieveResponse, type StatRetrieveParams } from './stats'; +export { + Suppressions, + type SuppressionCreateResponse, + type SuppressionRetrieveResponse, + type SuppressionDeleteResponse, + type SuppressionCreateParams, + type SuppressionRetrieveParams, + type SuppressionDeleteParams, +} from './suppressions'; +export { + SuppressionsCheck, + type SuppressionsCheckRetrieveResponse, + type SuppressionsCheckRetrieveParams, +} from './suppressions-check'; +export { Template, type TemplateCreateResponse, type TemplateCreateParams } from './template'; diff --git a/src/resources/emails/render.ts b/src/resources/emails/render.ts new file mode 100644 index 0000000..e1ea2c2 --- /dev/null +++ b/src/resources/emails/render.ts @@ -0,0 +1,49 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; + +export class Render extends APIResource { + /** + * Render a saved email template with optional merge variables. Returns the final + * HTML without sending. Useful for previewing emails before sending. + */ + create(body: RenderCreateParams, options?: RequestOptions): APIPromise { + return this._client.post('/v3/emails/render', { body, ...options }); + } +} + +export interface RenderCreateResponse { + data: RenderCreateResponse.Data; +} + +export namespace RenderCreateResponse { + export interface Data { + /** + * Rendered HTML content + */ + html?: string; + + /** + * Template name (can be used as default subject) + */ + subject?: string | null; + } +} + +export interface RenderCreateParams { + /** + * Template ID to render + */ + templateId: string; + + /** + * Merge variables to substitute. Use {{key}} syntax in your template. + */ + variables?: { [key: string]: string }; +} + +export declare namespace Render { + export { type RenderCreateResponse as RenderCreateResponse, type RenderCreateParams as RenderCreateParams }; +} diff --git a/src/resources/emails/settings.ts b/src/resources/emails/settings.ts new file mode 100644 index 0000000..dd07f31 --- /dev/null +++ b/src/resources/emails/settings.ts @@ -0,0 +1,86 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; + +export class Settings extends APIResource { + /** + * Get the email sender settings for this project. + */ + retrieve(options?: RequestOptions): APIPromise { + return this._client.get('/v3/emails/settings', options); + } + + /** + * Update the email sending configuration for this project. Only include the fields + * you want to change. + */ + update( + body: SettingUpdateParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.patch('/v3/emails/settings', { body, ...options }); + } +} + +export interface SettingRetrieveResponse { + data: SettingRetrieveResponse.Data; +} + +export namespace SettingRetrieveResponse { + export interface Data { + /** + * When the settings row was first created. + */ + createdAt?: string; + + /** + * Default sender display name + */ + defaultFromName?: string; + + /** + * When the settings were last updated. + */ + updatedAt?: string; + } +} + +export interface SettingUpdateResponse { + data: SettingUpdateResponse.Data; +} + +export namespace SettingUpdateResponse { + export interface Data { + /** + * When the settings row was first created. + */ + createdAt?: string; + + /** + * Default sender display name + */ + defaultFromName?: string; + + /** + * When the settings were last updated. + */ + updatedAt?: string; + } +} + +export interface SettingUpdateParams { + /** + * Default sender display name + */ + defaultFromName?: string; +} + +export declare namespace Settings { + export { + type SettingRetrieveResponse as SettingRetrieveResponse, + type SettingUpdateResponse as SettingUpdateResponse, + type SettingUpdateParams as SettingUpdateParams, + }; +} diff --git a/src/resources/emails/stats.ts b/src/resources/emails/stats.ts new file mode 100644 index 0000000..eb0f0e1 --- /dev/null +++ b/src/resources/emails/stats.ts @@ -0,0 +1,120 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; + +export class Stats extends APIResource { + /** + * Get aggregated email delivery statistics for a project. Returns totals or daily + * breakdown for the specified period. Statistics are asynchronous and may lag by + * about one hour. + */ + retrieve( + query: StatRetrieveParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.get('/v3/emails/stats', { query, ...options }); + } +} + +/** + * Email statistics. Shape depends on the `groupBy` query parameter: an aggregated + * totals object by default, or a daily breakdown array when groupBy=day. + */ +export interface StatRetrieveResponse { + /** + * Aggregated totals for the requested period (default response). + */ + data: StatRetrieveResponse.UnionMember0 | Array; +} + +export namespace StatRetrieveResponse { + /** + * Aggregated totals for the requested period (default response). + */ + export interface UnionMember0 { + /** + * Number of emails that were bounced by the recipient mail server. + */ + bounced?: number; + + /** + * Bounced / sent as a percentage (0-100, 2 decimal places). + */ + bounceRate?: number; + + /** + * Number of spam complaint events received. + */ + complained?: number; + + /** + * Number of successfully delivered emails. + */ + delivered?: number; + + /** + * Delivered / sent as a percentage (0-100, 2 decimal places). + */ + deliveryRate?: number; + + /** + * The period these stats cover. + */ + period?: '7d' | '30d' | '90d'; + + /** + * Total emails sent (one per recipient). + */ + sent?: number; + } + + export interface UnionMember1 { + /** + * Emails bounced on this day. + */ + bounced?: number; + + /** + * Spam complaints received for this send cohort. + */ + complained?: number; + + /** + * The email send-cohort day in YYYY-MM-DD format. + */ + date?: string; + + /** + * Emails from this send cohort that were delivered. + */ + delivered?: number; + + /** + * Emails sent on this day. + */ + sent?: number; + } +} + +export interface StatRetrieveParams { + /** + * Group results by day for chart data + */ + groupBy?: 'day'; + + /** + * Time period for stats + */ + period?: '7d' | '30d' | '90d'; + + /** + * Project ID (auto-resolved for API key auth) + */ + projectId?: string; +} + +export declare namespace Stats { + export { type StatRetrieveResponse as StatRetrieveResponse, type StatRetrieveParams as StatRetrieveParams }; +} diff --git a/src/resources/emails/suppressions-check.ts b/src/resources/emails/suppressions-check.ts new file mode 100644 index 0000000..22a5cf2 --- /dev/null +++ b/src/resources/emails/suppressions-check.ts @@ -0,0 +1,49 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; + +export class SuppressionsCheck extends APIResource { + /** + * Look up a specific email address to see if it is currently on the suppression + * list. + */ + retrieve( + query: SuppressionsCheckRetrieveParams, + options?: RequestOptions, + ): APIPromise { + return this._client.get('/v3/emails/suppressions/check', { query, ...options }); + } +} + +export interface SuppressionsCheckRetrieveResponse { + data: SuppressionsCheckRetrieveResponse.Data; +} + +export namespace SuppressionsCheckRetrieveResponse { + export interface Data { + email?: string; + + suppressed?: boolean; + } +} + +export interface SuppressionsCheckRetrieveParams { + /** + * Email address to check + */ + email: string; + + /** + * Project ID (auto-resolved for API key auth) + */ + projectId?: string; +} + +export declare namespace SuppressionsCheck { + export { + type SuppressionsCheckRetrieveResponse as SuppressionsCheckRetrieveResponse, + type SuppressionsCheckRetrieveParams as SuppressionsCheckRetrieveParams, + }; +} diff --git a/src/resources/emails/suppressions.ts b/src/resources/emails/suppressions.ts new file mode 100644 index 0000000..96a9efc --- /dev/null +++ b/src/resources/emails/suppressions.ts @@ -0,0 +1,126 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; + +export class Suppressions extends APIResource { + /** + * Manually add an email address to the suppression list. Future sends to this + * address will be blocked. + */ + create(body: SuppressionCreateParams, options?: RequestOptions): APIPromise { + return this._client.post('/v3/emails/suppressions', { body, ...options }); + } + + /** + * List all email addresses suppressed for this project due to bounces, complaints, + * or manual suppression. Cursor-paginated. + */ + retrieve( + query: SuppressionRetrieveParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.get('/v3/emails/suppressions', { query, ...options }); + } + + /** + * Remove an email address from the suppression list so it can receive emails + * again. + */ + delete(params: SuppressionDeleteParams, options?: RequestOptions): APIPromise { + const { email, projectId } = params; + return this._client.delete('/v3/emails/suppressions', { query: { email, projectId }, ...options }); + } +} + +export interface SuppressionCreateResponse { + data: SuppressionCreateResponse.Data; +} + +export namespace SuppressionCreateResponse { + export interface Data { + createdAt?: string; + + email?: string; + + reason?: string; + } +} + +export interface SuppressionRetrieveResponse { + data: Array; + + has_more: boolean; + + next_cursor?: string | null; +} + +export namespace SuppressionRetrieveResponse { + export interface Data { + createdAt?: string; + + email?: string; + + reason?: 'hard_bounce' | 'complaint' | 'manual' | 'unsubscribe'; + } +} + +export interface SuppressionDeleteResponse { + data: SuppressionDeleteResponse.Data; +} + +export namespace SuppressionDeleteResponse { + export interface Data { + email?: string; + + removed?: boolean; + } +} + +export interface SuppressionCreateParams { + /** + * Email address to suppress + */ + email: string; +} + +export interface SuppressionRetrieveParams { + /** + * Pagination cursor from a previous response. Omit to start from the beginning. + */ + cursor?: string; + + /** + * Max number of results (1-200) + */ + limit?: number; + + /** + * Project ID (auto-resolved for API key auth) + */ + projectId?: string; +} + +export interface SuppressionDeleteParams { + /** + * Email address to unsuppress + */ + email: string; + + /** + * Project ID (auto-resolved for API key auth) + */ + projectId?: string; +} + +export declare namespace Suppressions { + export { + type SuppressionCreateResponse as SuppressionCreateResponse, + type SuppressionRetrieveResponse as SuppressionRetrieveResponse, + type SuppressionDeleteResponse as SuppressionDeleteResponse, + type SuppressionCreateParams as SuppressionCreateParams, + type SuppressionRetrieveParams as SuppressionRetrieveParams, + type SuppressionDeleteParams as SuppressionDeleteParams, + }; +} diff --git a/src/resources/emails/template.ts b/src/resources/emails/template.ts new file mode 100644 index 0000000..fd8906f --- /dev/null +++ b/src/resources/emails/template.ts @@ -0,0 +1,201 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { buildHeaders } from '../../internal/headers'; +import { RequestOptions } from '../../internal/request-options'; + +export class Template extends APIResource { + /** + * Send a transactional email by rendering a saved template with optional merge + * variables. The template must have rendered HTML (saved at least once in the + * editor). The sender domain must be verified in the project workspace; verified + * sender domains are shared by every Developer Email API project in that + * workspace. + */ + create(params: TemplateCreateParams, options?: RequestOptions): APIPromise { + const { 'idempotency-key': idempotencyKey, ...body } = params; + return this._client.post('/v3/emails/template', { + body, + ...options, + headers: buildHeaders([ + { ...(idempotencyKey != null ? { 'idempotency-key': idempotencyKey } : undefined) }, + options?.headers, + ]), + }); + } +} + +/** + * Email accepted and queued for delivery + */ +export interface TemplateCreateResponse { + data: TemplateCreateResponse.Data; +} + +export namespace TemplateCreateResponse { + export interface Data { + /** + * Unique email ID (UUID). Use this with GET /v3/emails/:id to retrieve delivery + * status and events. + */ + id?: string; + + /** + * When the email was accepted and queued for delivery (ISO-8601). + */ + createdAt?: string; + + /** + * The sender address the email was sent from. + */ + from?: string; + + /** + * Usually "queued" for a fresh send. An idempotent replay of a previously accepted + * request returns that email's current status instead. Use webhooks or GET + * /v3/emails/:id for live delivery status. + */ + status?: 'queued' | 'sending' | 'sent' | 'delivered' | 'bounced' | 'complained' | 'failed'; + + /** + * The resolved subject line after merge variables were applied. + */ + subject?: string; + + /** + * The single accepted recipient address. + */ + to?: Array; + } +} + +export interface TemplateCreateParams { + /** + * Body param: Sender email address or "Name " format. Domain must be + * verified. + */ + from: string; + + /** + * Body param: Template ID to use for the email body + */ + templateId: string; + + /** + * Body param: Exactly one recipient. Each request creates one independently + * tracked delivery. + */ + to: Array; + + /** + * Body param: File attachments. Max 10 files per email, max 5 MB total payload + * size. + */ + attachments?: Array; + + /** + * Body param: BCC is not supported by this endpoint. + */ + bcc?: Array; + + /** + * Body param: CC is not supported by this endpoint. + */ + cc?: Array; + + /** + * Body param: Custom email headers. Up to 9 printable-ASCII X-\* headers are + * allowed. Header names may contain up to 126 characters and each name plus value + * may contain up to 996 characters. + */ + headers?: { [key: string]: string }; + + /** + * Body param: Reply-To email address + */ + replyTo?: string; + + /** + * Body param: Email subject line. Supports {{variable}} merge syntax. Defaults to + * template name if omitted. + */ + subject?: string; + + /** + * Body param: Key-value tags for categorizing the email (e.g. {"campaign": + * "welcome"}). Max 10 tags. Keys (1-64 chars) and values (up to 256 chars) may + * only contain letters, numbers, underscores, and hyphens (the Amazon SES + * message-tag character set). + */ + tags?: { [key: string]: string }; + + /** + * Body param: Plain text version of the email. Supports {{variable}} merge syntax. + */ + text?: string; + + /** + * Body param: Merge variables to substitute in the template and subject. Use + * {{key}} syntax in your template. + */ + variables?: { [key: string]: string }; + + /** + * Header param: Unique key for idempotent sends (max 255 characters). Duplicate + * requests within 24 hours return the cached response. + */ + 'idempotency-key'?: string; +} + +export namespace TemplateCreateParams { + export interface Attachment { + /** + * Base64-encoded file content. Whitespace and MIME line wrapping are removed + * before validation; invalid base64 is rejected with a 400 error. + */ + content: string; + + /** + * MIME type of the attachment. Required; must be one of the allowed types. + */ + contentType: + | 'application/pdf' + | 'application/zip' + | 'application/json' + | 'application/xml' + | 'application/csv' + | 'application/msword' + | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' + | 'application/vnd.ms-excel' + | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' + | 'application/vnd.ms-powerpoint' + | 'application/vnd.openxmlformats-officedocument.presentationml.presentation' + | 'text/plain' + | 'text/html' + | 'text/csv' + | 'text/xml' + | 'text/calendar' + | 'image/png' + | 'image/jpeg' + | 'image/gif' + | 'image/webp' + | 'image/svg+xml' + | 'audio/mpeg' + | 'audio/wav' + | 'video/mp4'; + + /** + * The filename as it will appear to the recipient. Line breaks are rejected; + * quotes are stripped before it is written into the message. + */ + filename: string; + } +} + +export declare namespace Template { + export { + type TemplateCreateResponse as TemplateCreateResponse, + type TemplateCreateParams as TemplateCreateParams, + }; +} diff --git a/src/resources/index.ts b/src/resources/index.ts index c4ba5a4..6fadd48 100644 --- a/src/resources/index.ts +++ b/src/resources/index.ts @@ -1,10 +1,26 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +export { + Domains, + type DomainCreateResponse, + type DomainRetrieveResponse, + type DomainListResponse, + type DomainDeleteResponse, + type DomainCreateParams, +} from './domains/domains'; export { EditorSessions, type EditorSessionCreateResponse, type EditorSessionCreateParams, } from './editor-sessions'; +export { + Emails, + type EmailCreateResponse, + type EmailRetrieveResponse, + type EmailListResponse, + type EmailCreateParams, + type EmailListParams, +} from './emails/emails'; export { Me } from './me/me'; export { Projects, type ProjectRetrieveResponse } from './projects/projects'; export { @@ -15,4 +31,14 @@ export { type TemplateListParams, type TemplateListResponsesCursorPage, } from './templates/templates'; +export { + Webhooks, + type WebhookCreateResponse, + type WebhookRetrieveResponse, + type WebhookUpdateResponse, + type WebhookListResponse, + type WebhookDeleteResponse, + type WebhookCreateParams, + type WebhookUpdateParams, +} from './webhooks/webhooks'; export { Workspaces, type WorkspaceRetrieveResponse, type WorkspaceListResponse } from './workspaces'; diff --git a/src/resources/templates/convert-full-to-simple.ts b/src/resources/templates/convert-full-to-simple.ts index c8b3e8c..e5a3c08 100644 --- a/src/resources/templates/convert-full-to-simple.ts +++ b/src/resources/templates/convert-full-to-simple.ts @@ -34,6 +34,12 @@ export namespace ConvertFullToSimpleCreateResponse { export interface ConvertFullToSimpleCreateParams { design: ConvertFullToSimpleCreateParams.Design; + /** + * Display mode of the design (email, web, document, popup). Defaults to "email", + * matching /v3/templates/validate. Mode-specific repairs apply during conversion + * (email caps contentWidth at 900px, for example), so pass the design's actual + * mode — a web design converted under the email default can be altered. + */ displayMode?: 'email' | 'web' | 'popup' | 'document'; /** diff --git a/src/resources/templates/convert-simple-to-full.ts b/src/resources/templates/convert-simple-to-full.ts index c1c0af0..0e848d5 100644 --- a/src/resources/templates/convert-simple-to-full.ts +++ b/src/resources/templates/convert-simple-to-full.ts @@ -34,6 +34,12 @@ export namespace ConvertSimpleToFullCreateResponse { export interface ConvertSimpleToFullCreateParams { design: ConvertSimpleToFullCreateParams.Design; + /** + * Display mode of the design (email, web, document, popup). Defaults to "email", + * matching /v3/templates/validate. Mode-specific repairs apply during conversion + * (email caps contentWidth at 900px, for example), so pass the design's actual + * mode — a web design converted under the email default can be altered. + */ displayMode?: 'email' | 'web' | 'popup' | 'document'; includeDefaultValues?: boolean; diff --git a/src/resources/templates/index.ts b/src/resources/templates/index.ts index c07698b..62ba98f 100644 --- a/src/resources/templates/index.ts +++ b/src/resources/templates/index.ts @@ -16,6 +16,7 @@ export { ExportPdf, type ExportPdfCreateResponse, type ExportPdfCreateParams } f export { ExportZip, type ExportZipCreateResponse, type ExportZipCreateParams } from './export-zip'; export { Generate, type GenerateCreateResponse, type GenerateCreateParams } from './generate'; export { Import, type ImportCreateResponse, type ImportCreateParams } from './import'; +export { Schema, type SchemaRetrieveParams } from './schema'; export { Templates, type TemplateRetrieveResponse, @@ -24,3 +25,4 @@ export { type TemplateListParams, type TemplateListResponsesCursorPage, } from './templates'; +export { Validate, type ValidateCreateResponse, type ValidateCreateParams } from './validate'; diff --git a/src/resources/templates/schema.ts b/src/resources/templates/schema.ts new file mode 100644 index 0000000..d6d4bce --- /dev/null +++ b/src/resources/templates/schema.ts @@ -0,0 +1,44 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { buildHeaders } from '../../internal/headers'; +import { RequestOptions } from '../../internal/request-options'; + +/** + * Template management — list, retrieve, generate, import, export, and convert designs. + */ +export class Schema extends APIResource { + /** + * Returns the canonical design schema as a standard JSON Schema document — the + * exact schema POST /v3/templates/validate checks against, ready to plug into any + * JSON Schema validator or editor tooling. Serves the Full schema by default; pass + * simple=true for the compact Simple schema. No authentication required. Responses + * carry a strong ETag and long-lived cache headers; send If-None-Match to + * revalidate for free. + */ + retrieve(query: SchemaRetrieveParams | null | undefined = {}, options?: RequestOptions): APIPromise { + return this._client.get('/v3/templates/schema', { + query, + ...options, + headers: buildHeaders([{ Accept: '*/*' }, options?.headers]), + }); + } +} + +export interface SchemaRetrieveParams { + /** + * Display mode whose rules the schema describes (email, web, document, popup). + * Defaults to "email". + */ + displayMode?: 'email' | 'web' | 'popup' | 'document'; + + /** + * When true, returns the Simple schema instead of the Full schema. + */ + simple?: boolean; +} + +export declare namespace Schema { + export { type SchemaRetrieveParams as SchemaRetrieveParams }; +} diff --git a/src/resources/templates/templates.ts b/src/resources/templates/templates.ts index 96b914f..45f8f0f 100644 --- a/src/resources/templates/templates.ts +++ b/src/resources/templates/templates.ts @@ -25,6 +25,10 @@ import * as GenerateAPI from './generate'; import { Generate, GenerateCreateParams, GenerateCreateResponse } from './generate'; import * as ImportAPI from './import'; import { Import, ImportCreateParams, ImportCreateResponse } from './import'; +import * as SchemaAPI from './schema'; +import { Schema, SchemaRetrieveParams } from './schema'; +import * as ValidateAPI from './validate'; +import { Validate, ValidateCreateParams, ValidateCreateResponse } from './validate'; import { APIPromise } from '../../core/api-promise'; import { CursorPage, type CursorPageParams, PagePromise } from '../../core/pagination'; import { RequestOptions } from '../../internal/request-options'; @@ -44,6 +48,8 @@ export class Templates extends APIResource { exportZip: ExportZipAPI.ExportZip = new ExportZipAPI.ExportZip(this._client); generate: GenerateAPI.Generate = new GenerateAPI.Generate(this._client); import: ImportAPI.Import = new ImportAPI.Import(this._client); + schema: SchemaAPI.Schema = new SchemaAPI.Schema(this._client); + validate: ValidateAPI.Validate = new ValidateAPI.Validate(this._client); /** * Get template by ID. @@ -145,6 +151,8 @@ Templates.ExportPdf = ExportPdf; Templates.ExportZip = ExportZip; Templates.Generate = Generate; Templates.Import = Import; +Templates.Schema = Schema; +Templates.Validate = Validate; export declare namespace Templates { export { @@ -202,4 +210,12 @@ export declare namespace Templates { type ImportCreateResponse as ImportCreateResponse, type ImportCreateParams as ImportCreateParams, }; + + export { Schema as Schema, type SchemaRetrieveParams as SchemaRetrieveParams }; + + export { + Validate as Validate, + type ValidateCreateResponse as ValidateCreateResponse, + type ValidateCreateParams as ValidateCreateParams, + }; } diff --git a/src/resources/templates/validate.ts b/src/resources/templates/validate.ts new file mode 100644 index 0000000..66832dc --- /dev/null +++ b/src/resources/templates/validate.ts @@ -0,0 +1,132 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; + +/** + * Template management — list, retrieve, generate, import, export, and convert designs. + */ +export class Validate extends APIResource { + /** + * Validate a design JSON against the Unlayer design schema. Returns { success: + * true, data: { valid: true } } when the payload conforms; otherwise data is { + * valid: false, errors: [...] } with descriptive issues. Every checked design gets + * HTTP 200 — `data.valid` is the source of truth, not the status code. Only + * malformed requests (e.g. a missing design field or an unknown displayMode) fail + * request validation with 400 VALIDATION_ERROR. + */ + create(body: ValidateCreateParams, options?: RequestOptions): APIPromise { + return this._client.post('/v3/templates/validate', { body, ...options }); + } +} + +export interface ValidateCreateResponse { + data: ValidateCreateResponse.Data; + + success: true; +} + +export namespace ValidateCreateResponse { + export interface Data { + valid: boolean; + + /** + * Total number of issues found; greater than errors.length when the list was + * capped. + */ + errorCount?: number; + + /** + * Populated when valid is false, capped at 100 entries. Each issue carries the + * dotted path to the offending field, a human-readable message, and the underlying + * Zod issue code. + */ + errors?: Array; + + /** + * Present when the design was upgraded from an older schemaVersion before + * validation; carries the original version number. + */ + migratedFrom?: number; + } + + export namespace Data { + export interface Error { + code: string; + + message: string; + + path: string; + } + } +} + +export interface ValidateCreateParams { + /** + * The design JSON to validate. + */ + design: { [key: string]: unknown }; + + /** + * Custom tool declarations, in the same shape passed to unlayer.registerTool. When + * provided, blocks matching a declared tool have their values checked against the + * tool's declared options (wrong types are reported at their exact path). Blocks + * of undeclared tools keep envelope-only validation. + */ + customTools?: Array; + + /** + * Display mode for the design (email, web, document, popup). Some validation rules + * differ per mode. Defaults to "email" — without a default, options from every + * mode would apply at once, the strictest possible check, and real editor-saved + * designs could be reported invalid. + */ + displayMode?: 'email' | 'web' | 'popup' | 'document'; + + /** + * When true (default), a full-form design with an older schemaVersion is upgraded + * to the current schema before validating — matching how the editor and the + * convert endpoints treat stored designs. Designs without a schemaVersion predate + * versioning and are fully migrated the same way. Set to false to check strict + * conformance with the current schema version. Designs with a newer schemaVersion + * than this API knows are validated as-if-current. + */ + migrate?: boolean; + + /** + * Which form of the schema to validate against. Defaults to "full". + */ + schema?: 'full' | 'simple'; +} + +export namespace ValidateCreateParams { + export interface CustomTool { + options: { [key: string]: CustomTool.Options }; + + slug: string; + + label?: string; + + supportedDisplayModes?: Array<'email' | 'web' | 'popup' | 'document'>; + + type?: string; + + values?: { [key: string]: unknown }; + + [k: string]: unknown; + } + + export namespace CustomTool { + export interface Options { + options?: unknown; + } + } +} + +export declare namespace Validate { + export { + type ValidateCreateResponse as ValidateCreateResponse, + type ValidateCreateParams as ValidateCreateParams, + }; +} diff --git a/src/resources/webhooks.ts b/src/resources/webhooks.ts new file mode 100644 index 0000000..8aad965 --- /dev/null +++ b/src/resources/webhooks.ts @@ -0,0 +1,3 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export * from './webhooks/index'; diff --git a/src/resources/webhooks/index.ts b/src/resources/webhooks/index.ts new file mode 100644 index 0000000..cb1800e --- /dev/null +++ b/src/resources/webhooks/index.ts @@ -0,0 +1,13 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export { RotateSecret, type RotateSecretCreateResponse } from './rotate-secret'; +export { + Webhooks, + type WebhookCreateResponse, + type WebhookRetrieveResponse, + type WebhookUpdateResponse, + type WebhookListResponse, + type WebhookDeleteResponse, + type WebhookCreateParams, + type WebhookUpdateParams, +} from './webhooks'; diff --git a/src/resources/webhooks/rotate-secret.ts b/src/resources/webhooks/rotate-secret.ts new file mode 100644 index 0000000..f8342ff --- /dev/null +++ b/src/resources/webhooks/rotate-secret.ts @@ -0,0 +1,37 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +export class RotateSecret extends APIResource { + /** + * Generate a new signing secret for a webhook. The new secret is returned once — + * store it securely. The old secret is invalidated immediately. + */ + create(id: string, options?: RequestOptions): APIPromise { + return this._client.post(path`/v3/webhooks/${id}/rotate-secret`, options); + } +} + +export interface RotateSecretCreateResponse { + data: RotateSecretCreateResponse.Data; +} + +export namespace RotateSecretCreateResponse { + export interface Data { + id?: number; + + /** + * New signing secret — only returned once. Store it securely. + */ + secret?: string; + + updatedAt?: string; + } +} + +export declare namespace RotateSecret { + export { type RotateSecretCreateResponse as RotateSecretCreateResponse }; +} diff --git a/src/resources/webhooks/webhooks.ts b/src/resources/webhooks/webhooks.ts new file mode 100644 index 0000000..87b2e95 --- /dev/null +++ b/src/resources/webhooks/webhooks.ts @@ -0,0 +1,255 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import * as RotateSecretAPI from './rotate-secret'; +import { RotateSecret, RotateSecretCreateResponse } from './rotate-secret'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +export class Webhooks extends APIResource { + rotateSecret: RotateSecretAPI.RotateSecret = new RotateSecretAPI.RotateSecret(this._client); + + /** + * Create a new webhook endpoint. A signing secret is auto-generated and returned + * once. Use it to verify webhook signatures. + */ + create(body: WebhookCreateParams, options?: RequestOptions): APIPromise { + return this._client.post('/v3/webhooks', { body, ...options }); + } + + /** + * Get details of a specific webhook endpoint. + */ + retrieve(id: string, options?: RequestOptions): APIPromise { + return this._client.get(path`/v3/webhooks/${id}`, options); + } + + /** + * Update a webhook endpoint URL, events, or active status. + */ + update( + id: string, + body: WebhookUpdateParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.patch(path`/v3/webhooks/${id}`, { body, ...options }); + } + + /** + * List all webhook endpoints configured for a project. + */ + list(options?: RequestOptions): APIPromise { + return this._client.get('/v3/webhooks', options); + } + + /** + * Delete a webhook endpoint. It will no longer receive events. + */ + delete(id: string, options?: RequestOptions): APIPromise { + return this._client.delete(path`/v3/webhooks/${id}`, options); + } +} + +export interface WebhookCreateResponse { + data: WebhookCreateResponse.Data; +} + +export namespace WebhookCreateResponse { + export interface Data { + /** + * Webhook ID + */ + id?: number; + + /** + * Whether the webhook is actively receiving events + */ + active?: boolean; + + /** + * When the webhook was created + */ + createdAt?: string; + + /** + * Event types this webhook is subscribed to + */ + events?: Array<'email.sent' | 'email.delivered' | 'email.bounced' | 'email.complained'>; + + /** + * Signing secret — only returned on creation. Store it securely; you will not be + * able to retrieve it again. + */ + secret?: string; + + /** + * The HTTPS URL receiving webhook events + */ + url?: string; + } +} + +export interface WebhookRetrieveResponse { + data: WebhookRetrieveResponse.Data; +} + +export namespace WebhookRetrieveResponse { + export interface Data { + /** + * Webhook ID + */ + id?: number; + + /** + * Whether the webhook is actively receiving events + */ + active?: boolean; + + /** + * When the webhook was created + */ + createdAt?: string; + + /** + * Event types this webhook is subscribed to + */ + events?: Array<'email.sent' | 'email.delivered' | 'email.bounced' | 'email.complained'>; + + /** + * When the webhook was last updated + */ + updatedAt?: string; + + /** + * The HTTPS URL receiving webhook events + */ + url?: string; + } +} + +export interface WebhookUpdateResponse { + data: WebhookUpdateResponse.Data; +} + +export namespace WebhookUpdateResponse { + export interface Data { + /** + * Webhook ID + */ + id?: number; + + /** + * Whether the webhook is actively receiving events + */ + active?: boolean; + + /** + * Event types this webhook is subscribed to + */ + events?: Array<'email.sent' | 'email.delivered' | 'email.bounced' | 'email.complained'>; + + /** + * When the webhook was last updated + */ + updatedAt?: string; + + /** + * The HTTPS URL receiving webhook events + */ + url?: string; + } +} + +export interface WebhookListResponse { + data: Array; +} + +export namespace WebhookListResponse { + export interface Data { + /** + * Webhook ID + */ + id?: number; + + /** + * Whether the webhook is actively receiving events + */ + active?: boolean; + + /** + * When the webhook was created + */ + createdAt?: string; + + /** + * Event types this webhook is subscribed to + */ + events?: Array<'email.sent' | 'email.delivered' | 'email.bounced' | 'email.complained'>; + + /** + * The HTTPS URL receiving webhook events + */ + url?: string; + } +} + +export interface WebhookDeleteResponse { + data?: WebhookDeleteResponse.Data; +} + +export namespace WebhookDeleteResponse { + export interface Data { + success?: boolean; + } +} + +export interface WebhookCreateParams { + /** + * The HTTPS URL to receive webhook events + */ + url: string; + + /** + * Whether the webhook is active + */ + active?: boolean; + + /** + * Event types to subscribe to. If omitted or empty, all events are sent. + */ + events?: Array<'email.sent' | 'email.delivered' | 'email.bounced' | 'email.complained'>; +} + +export interface WebhookUpdateParams { + /** + * Whether the webhook is actively receiving events + */ + active?: boolean; + + /** + * Event types to subscribe to. If omitted or empty, all events are sent. + */ + events?: Array<'email.sent' | 'email.delivered' | 'email.bounced' | 'email.complained'>; + + /** + * The HTTPS URL to receive webhook events + */ + url?: string; +} + +Webhooks.RotateSecret = RotateSecret; + +export declare namespace Webhooks { + export { + type WebhookCreateResponse as WebhookCreateResponse, + type WebhookRetrieveResponse as WebhookRetrieveResponse, + type WebhookUpdateResponse as WebhookUpdateResponse, + type WebhookListResponse as WebhookListResponse, + type WebhookDeleteResponse as WebhookDeleteResponse, + type WebhookCreateParams as WebhookCreateParams, + type WebhookUpdateParams as WebhookUpdateParams, + }; + + export { RotateSecret as RotateSecret, type RotateSecretCreateResponse as RotateSecretCreateResponse }; +} diff --git a/tests/api-resources/domains/domains.test.ts b/tests/api-resources/domains/domains.test.ts new file mode 100644 index 0000000..c12a1c0 --- /dev/null +++ b/tests/api-resources/domains/domains.test.ts @@ -0,0 +1,58 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource domains', () => { + test('create: only required params', async () => { + const responsePromise = client.domains.create({ domain: 'domain' }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('create: required and optional params', async () => { + const response = await client.domains.create({ domain: 'domain' }); + }); + + test('retrieve', async () => { + const responsePromise = client.domains.retrieve('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('list', async () => { + const responsePromise = client.domains.list(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('delete', async () => { + const responsePromise = client.domains.delete('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); +}); diff --git a/tests/api-resources/domains/verify.test.ts b/tests/api-resources/domains/verify.test.ts new file mode 100644 index 0000000..c42c251 --- /dev/null +++ b/tests/api-resources/domains/verify.test.ts @@ -0,0 +1,21 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource verify', () => { + test('create', async () => { + const responsePromise = client.domains.verify.create('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); +}); diff --git a/tests/api-resources/emails/emails.test.ts b/tests/api-resources/emails/emails.test.ts new file mode 100644 index 0000000..cc81582 --- /dev/null +++ b/tests/api-resources/emails/emails.test.ts @@ -0,0 +1,90 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource emails', () => { + test('create: only required params', async () => { + const responsePromise = client.emails.create({ + from: 'from', + html: 'html', + subject: 'subject', + to: ['dev@stainless.com'], + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('create: required and optional params', async () => { + const response = await client.emails.create({ + from: 'from', + html: 'html', + subject: 'subject', + to: ['dev@stainless.com'], + attachments: [ + { + content: 'content', + contentType: 'application/pdf', + filename: 'filename', + }, + ], + bcc: [], + cc: [], + headers: { foo: 'J!Q0Ok0bzJb7' }, + replyTo: 'dev@stainless.com', + tags: { foo: '_1' }, + text: 'text', + 'idempotency-key': 'idempotency-key', + }); + }); + + test('retrieve', async () => { + const responsePromise = client.emails.retrieve('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('list', async () => { + const responsePromise = client.emails.list(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('list: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.emails.list( + { + cursor: 'cursor', + from: '2019-12-27', + limit: 1, + projectId: 'projectId', + search: 'search', + status: 'queued', + tag: 'tag', + to: '2019-12-27', + }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(Unlayer.NotFoundError); + }); +}); diff --git a/tests/api-resources/emails/events.test.ts b/tests/api-resources/emails/events.test.ts new file mode 100644 index 0000000..43c7b86 --- /dev/null +++ b/tests/api-resources/emails/events.test.ts @@ -0,0 +1,21 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource events', () => { + test('retrieve', async () => { + const responsePromise = client.emails.events.retrieve('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); +}); diff --git a/tests/api-resources/emails/render.test.ts b/tests/api-resources/emails/render.test.ts new file mode 100644 index 0000000..0cbf3fe --- /dev/null +++ b/tests/api-resources/emails/render.test.ts @@ -0,0 +1,28 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource render', () => { + test('create: only required params', async () => { + const responsePromise = client.emails.render.create({ templateId: '496' }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('create: required and optional params', async () => { + const response = await client.emails.render.create({ + templateId: '496', + variables: { foo: 'string' }, + }); + }); +}); diff --git a/tests/api-resources/emails/settings.test.ts b/tests/api-resources/emails/settings.test.ts new file mode 100644 index 0000000..dade53f --- /dev/null +++ b/tests/api-resources/emails/settings.test.ts @@ -0,0 +1,42 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource settings', () => { + test('retrieve', async () => { + const responsePromise = client.emails.settings.retrieve(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('update', async () => { + const responsePromise = client.emails.settings.update(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('update: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.emails.settings.update( + { defaultFromName: 'defaultFromName' }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(Unlayer.NotFoundError); + }); +}); diff --git a/tests/api-resources/emails/stats.test.ts b/tests/api-resources/emails/stats.test.ts new file mode 100644 index 0000000..8b6d7ab --- /dev/null +++ b/tests/api-resources/emails/stats.test.ts @@ -0,0 +1,35 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource stats', () => { + test('retrieve', async () => { + const responsePromise = client.emails.stats.retrieve(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('retrieve: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.emails.stats.retrieve( + { + groupBy: 'day', + period: '7d', + projectId: 'projectId', + }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(Unlayer.NotFoundError); + }); +}); diff --git a/tests/api-resources/emails/suppressions-check.test.ts b/tests/api-resources/emails/suppressions-check.test.ts new file mode 100644 index 0000000..26871b3 --- /dev/null +++ b/tests/api-resources/emails/suppressions-check.test.ts @@ -0,0 +1,28 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource suppressionsCheck', () => { + test('retrieve: only required params', async () => { + const responsePromise = client.emails.suppressionsCheck.retrieve({ email: 'email' }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('retrieve: required and optional params', async () => { + const response = await client.emails.suppressionsCheck.retrieve({ + email: 'email', + projectId: 'projectId', + }); + }); +}); diff --git a/tests/api-resources/emails/suppressions.test.ts b/tests/api-resources/emails/suppressions.test.ts new file mode 100644 index 0000000..c3f8f9c --- /dev/null +++ b/tests/api-resources/emails/suppressions.test.ts @@ -0,0 +1,65 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource suppressions', () => { + test('create: only required params', async () => { + const responsePromise = client.emails.suppressions.create({ email: 'dev@stainless.com' }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('create: required and optional params', async () => { + const response = await client.emails.suppressions.create({ email: 'dev@stainless.com' }); + }); + + test('retrieve', async () => { + const responsePromise = client.emails.suppressions.retrieve(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('retrieve: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.emails.suppressions.retrieve( + { + cursor: 'cursor', + limit: 1, + projectId: 'projectId', + }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(Unlayer.NotFoundError); + }); + + test('delete: only required params', async () => { + const responsePromise = client.emails.suppressions.delete({ email: 'email' }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('delete: required and optional params', async () => { + const response = await client.emails.suppressions.delete({ email: 'email', projectId: 'projectId' }); + }); +}); diff --git a/tests/api-resources/emails/template.test.ts b/tests/api-resources/emails/template.test.ts new file mode 100644 index 0000000..2558da4 --- /dev/null +++ b/tests/api-resources/emails/template.test.ts @@ -0,0 +1,49 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource template', () => { + test('create: only required params', async () => { + const responsePromise = client.emails.template.create({ + from: 'from', + templateId: '496', + to: ['dev@stainless.com'], + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('create: required and optional params', async () => { + const response = await client.emails.template.create({ + from: 'from', + templateId: '496', + to: ['dev@stainless.com'], + attachments: [ + { + content: 'content', + contentType: 'application/pdf', + filename: 'filename', + }, + ], + bcc: [], + cc: [], + headers: { foo: 'J!Q0Ok0bzJb7' }, + replyTo: 'dev@stainless.com', + subject: 'subject', + tags: { foo: '_1' }, + text: 'text', + variables: { foo: 'string' }, + 'idempotency-key': 'idempotency-key', + }); + }); +}); diff --git a/tests/api-resources/templates/schema.test.ts b/tests/api-resources/templates/schema.test.ts new file mode 100644 index 0000000..5241672 --- /dev/null +++ b/tests/api-resources/templates/schema.test.ts @@ -0,0 +1,31 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource schema', () => { + test('retrieve', async () => { + const responsePromise = client.templates.schema.retrieve(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('retrieve: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.templates.schema.retrieve( + { displayMode: 'email', simple: true }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(Unlayer.NotFoundError); + }); +}); diff --git a/tests/api-resources/templates/validate.test.ts b/tests/api-resources/templates/validate.test.ts new file mode 100644 index 0000000..70d9bbb --- /dev/null +++ b/tests/api-resources/templates/validate.test.ts @@ -0,0 +1,40 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource validate', () => { + test('create: only required params', async () => { + const responsePromise = client.templates.validate.create({ design: { foo: 'bar' } }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('create: required and optional params', async () => { + const response = await client.templates.validate.create({ + design: { foo: 'bar' }, + customTools: [ + { + options: { foo: { options: {} } }, + slug: 'slug', + label: 'label', + supportedDisplayModes: ['email'], + type: 'type', + values: { foo: 'bar' }, + }, + ], + displayMode: 'email', + migrate: true, + schema: 'full', + }); + }); +}); diff --git a/tests/api-resources/webhooks/rotate-secret.test.ts b/tests/api-resources/webhooks/rotate-secret.test.ts new file mode 100644 index 0000000..f1f7557 --- /dev/null +++ b/tests/api-resources/webhooks/rotate-secret.test.ts @@ -0,0 +1,21 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource rotateSecret', () => { + test('create', async () => { + const responsePromise = client.webhooks.rotateSecret.create('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); +}); diff --git a/tests/api-resources/webhooks/webhooks.test.ts b/tests/api-resources/webhooks/webhooks.test.ts new file mode 100644 index 0000000..6505153 --- /dev/null +++ b/tests/api-resources/webhooks/webhooks.test.ts @@ -0,0 +1,88 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource webhooks', () => { + test('create: only required params', async () => { + const responsePromise = client.webhooks.create({ url: 'https://example.com' }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('create: required and optional params', async () => { + const response = await client.webhooks.create({ + url: 'https://example.com', + active: true, + events: ['email.sent'], + }); + }); + + test('retrieve', async () => { + const responsePromise = client.webhooks.retrieve('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('update', async () => { + const responsePromise = client.webhooks.update('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('update: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.webhooks.update( + 'id', + { + active: true, + events: ['email.sent'], + url: 'https://example.com', + }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(Unlayer.NotFoundError); + }); + + test('list', async () => { + const responsePromise = client.webhooks.list(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('delete', async () => { + const responsePromise = client.webhooks.delete('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); +}); From cbe853435e7d34377b9bc2dcfbf4044f1580a162 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 11:13:11 +0000 Subject: [PATCH 50/54] feat(api): api update --- .stats.yml | 8 +- api.md | 10 ++ src/client.ts | 12 +++ src/resources/blocks.ts | 143 +++++++++++++++++++++++++++++ src/resources/index.ts | 1 + tests/api-resources/blocks.test.ts | 40 ++++++++ 6 files changed, 210 insertions(+), 4 deletions(-) create mode 100644 src/resources/blocks.ts create mode 100644 tests/api-resources/blocks.test.ts diff --git a/.stats.yml b/.stats.yml index b5c4710..022e0e6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 50 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-112356d364a4c9b7cf7f8d272937732568d750f9574ecc0c07516eca112cdddf.yml -openapi_spec_hash: 283e15c9bc02c4d7c69189873b1da9d4 -config_hash: c65b47a2f20400d392a50837c0a10945 +configured_endpoints: 51 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-92f6b863c3ee96cd2d4dd5b6a38d2820b0a56bf1006a0429a3d6fb756530d781.yml +openapi_spec_hash: 204d0db45842998a761ec507960eae9f +config_hash: 31bbe4c50af7bd9aea5582d9a2ab786d diff --git a/api.md b/api.md index 66d02c4..76404ab 100644 --- a/api.md +++ b/api.md @@ -1,3 +1,13 @@ +# Blocks + +Types: + +- BlockRetrieveResponse + +Methods: + +- client.blocks.retrieve({ ...params }) -> BlockRetrieveResponse + # Domains Types: diff --git a/src/client.ts b/src/client.ts index be8e975..dd24fe1 100644 --- a/src/client.ts +++ b/src/client.ts @@ -19,6 +19,7 @@ import { AbstractPage, type CursorPageParams, CursorPageResponse } from './core/ import * as Uploads from './core/uploads'; import * as API from './resources/index'; import { APIPromise } from './core/api-promise'; +import { BlockRetrieveParams, BlockRetrieveResponse, Blocks } from './resources/blocks'; import { EditorSessionCreateParams, EditorSessionCreateResponse, @@ -852,6 +853,10 @@ export class Unlayer { static toFile = Uploads.toFile; + /** + * Reusable design blocks — list shared project blocks and end-user saved blocks for backup, migration, and usage reporting. + */ + blocks: API.Blocks = new API.Blocks(this); domains: API.Domains = new API.Domains(this); editorSessions: API.EditorSessions = new API.EditorSessions(this); emails: API.Emails = new API.Emails(this); @@ -871,6 +876,7 @@ export class Unlayer { workspaces: API.Workspaces = new API.Workspaces(this); } +Unlayer.Blocks = Blocks; Unlayer.Domains = Domains; Unlayer.EditorSessions = EditorSessions; Unlayer.Emails = Emails; @@ -886,6 +892,12 @@ export declare namespace Unlayer { export import CursorPage = Pagination.CursorPage; export { type CursorPageParams as CursorPageParams, type CursorPageResponse as CursorPageResponse }; + export { + Blocks as Blocks, + type BlockRetrieveResponse as BlockRetrieveResponse, + type BlockRetrieveParams as BlockRetrieveParams, + }; + export { Domains as Domains, type DomainCreateResponse as DomainCreateResponse, diff --git a/src/resources/blocks.ts b/src/resources/blocks.ts new file mode 100644 index 0000000..d007356 --- /dev/null +++ b/src/resources/blocks.ts @@ -0,0 +1,143 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../core/resource'; +import { APIPromise } from '../core/api-promise'; +import { RequestOptions } from '../internal/request-options'; + +/** + * Reusable design blocks — list shared project blocks and end-user saved blocks for backup, migration, and usage reporting. + */ +export class Blocks extends APIResource { + /** + * List blocks with cursor-based pagination. Returns both shared project blocks and + * blocks saved by end-users; each user-saved block carries the userId it was saved + * under (null for shared blocks), so usage can be aggregated per end-user without + * enumerating user IDs. Returns blocks in descending order by creation. + */ + retrieve( + query: BlockRetrieveParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.get('/v3/blocks', { query, ...options }); + } +} + +export interface BlockRetrieveResponse { + data: Array; + + /** + * Whether there are more results after this page + */ + has_more: boolean; + + /** + * Cursor for the next page. Null if no more results. + */ + next_cursor?: string | null; +} + +export namespace BlockRetrieveResponse { + export interface Data { + /** + * Block ID + */ + id?: string; + + /** + * Block category + */ + category?: string; + + createdAt?: string; + + /** + * The block design JSON. Omitted when includeData=false is passed. + */ + data?: { [key: string]: unknown }; + + /** + * Display mode the block was saved for: email, web, popup, or document + */ + displayMode?: string; + + /** + * Whether the block is currently a synced block + */ + isSyncEnabled?: boolean; + + /** + * Synced-block ID referenced by designs using this block. Null when the block has + * never been synced. + */ + syncId?: string | null; + + /** + * Block tags + */ + tags?: Array; + + /** + * URL of the auto-generated block thumbnail, if available + */ + thumbnailUrl?: string | null; + + updatedAt?: string; + + /** + * End-user ID the block was saved under (the user id your app passes to the + * editor). Null for shared project blocks. + */ + userId?: string | null; + } +} + +export interface BlockRetrieveParams { + /** + * Filter by category (case-insensitive search) + */ + category?: string; + + /** + * Pagination cursor from previous response + */ + cursor?: string; + + /** + * Filter by display mode + */ + displayMode?: 'email' | 'web' | 'popup' | 'document'; + + /** + * Include the block design JSON in each item. Pass false for lightweight sweeps + * (e.g. usage reports). + */ + includeData?: boolean; + + /** + * Number of blocks to return (1-100) + */ + limit?: number; + + /** + * The project ID to list blocks for + */ + projectId?: string; + + /** + * Filter by block ownership: shared project blocks, end-user saved blocks, or both + */ + scope?: 'all' | 'shared' | 'user'; + + /** + * Only blocks saved by this end-user (exact match on the user id your app passes + * to the editor) + */ + userId?: string; +} + +export declare namespace Blocks { + export { + type BlockRetrieveResponse as BlockRetrieveResponse, + type BlockRetrieveParams as BlockRetrieveParams, + }; +} diff --git a/src/resources/index.ts b/src/resources/index.ts index 6fadd48..c40edde 100644 --- a/src/resources/index.ts +++ b/src/resources/index.ts @@ -1,5 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +export { Blocks, type BlockRetrieveResponse, type BlockRetrieveParams } from './blocks'; export { Domains, type DomainCreateResponse, diff --git a/tests/api-resources/blocks.test.ts b/tests/api-resources/blocks.test.ts new file mode 100644 index 0000000..6520e67 --- /dev/null +++ b/tests/api-resources/blocks.test.ts @@ -0,0 +1,40 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Unlayer from '@unlayer/sdk'; + +const client = new Unlayer({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource blocks', () => { + test('retrieve', async () => { + const responsePromise = client.blocks.retrieve(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + test('retrieve: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.blocks.retrieve( + { + category: 'category', + cursor: 'cursor', + displayMode: 'email', + includeData: true, + limit: 1, + projectId: 'projectId', + scope: 'all', + userId: 'userId', + }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(Unlayer.NotFoundError); + }); +}); From ce56076fdab27b5da8c0cef75ea8547c1d6358ea Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 12:16:32 +0000 Subject: [PATCH 51/54] feat(api): api update --- .stats.yml | 4 ++-- src/resources/projects/ai-credits-usage.ts | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index 022e0e6..e8a0a73 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 51 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-92f6b863c3ee96cd2d4dd5b6a38d2820b0a56bf1006a0429a3d6fb756530d781.yml -openapi_spec_hash: 204d0db45842998a761ec507960eae9f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-268cac778fb55e8dafe406262f0ead04728e2bafb672ca60161f985f942cf4f8.yml +openapi_spec_hash: e5ac63432486a66a5b5e101151bd95f4 config_hash: 31bbe4c50af7bd9aea5582d9a2ab786d diff --git a/src/resources/projects/ai-credits-usage.ts b/src/resources/projects/ai-credits-usage.ts index 38bec77..77631f9 100644 --- a/src/resources/projects/ai-credits-usage.ts +++ b/src/resources/projects/ai-credits-usage.ts @@ -11,8 +11,10 @@ import { path } from '../../internal/utils/path'; export class AICreditsUsage extends APIResource { /** * Returns AI credit consumption for the project, broken down by end user and - * feature type. Filterable by date range, end user, and feature type. Defaults to - * the current billing period. Only credit counts are returned; token counts, model + * feature type. Filterable by date range, end user, and feature type. Usage is + * updated near real time and grouped by the UTC date when the AI activity + * occurred. Recent activity may take a short time to appear. Defaults to the + * current billing period. Only credit counts are returned; token counts, model * names, and costs are never exposed. Per-end-user attribution requires the * partner to pass `endUserId` on editor initialization. */ From a072644224a14f898ce0bf882f0800cbce82f69f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 20:32:17 +0000 Subject: [PATCH 52/54] feat(api): api update --- .stats.yml | 4 ++-- src/resources/templates/generate.ts | 27 ++++++++++++++++----------- src/resources/templates/import.ts | 6 +++--- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/.stats.yml b/.stats.yml index e8a0a73..203960b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 51 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-268cac778fb55e8dafe406262f0ead04728e2bafb672ca60161f985f942cf4f8.yml -openapi_spec_hash: e5ac63432486a66a5b5e101151bd95f4 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-d939bc17efff27c8a66b48b80e6ef9d5790105eed03bbddf57642c65a22ad16c.yml +openapi_spec_hash: f60ac2085adb51c5a437684028a065b2 config_hash: 31bbe4c50af7bd9aea5582d9a2ab786d diff --git a/src/resources/templates/generate.ts b/src/resources/templates/generate.ts index d5e60bf..8420147 100644 --- a/src/resources/templates/generate.ts +++ b/src/resources/templates/generate.ts @@ -50,8 +50,8 @@ export interface GenerateCreateResponse { output?: GenerateCreateResponse.Output; /** - * Aggregate token usage for the turn when exposed by the caller. Builder copilot - * endpoints expose it only in local/dev/QA and omit it in staging/production. + * Aggregate token usage and billed AI credits for the turn. Estimated provider + * cost is included only by builder copilot endpoints in local/dev/QA. */ usage?: GenerateCreateResponse.Usage; } @@ -63,7 +63,7 @@ export namespace GenerateCreateResponse { */ export interface Model { /** - * Resolved model id, e.g. "claude-opus-4-7". + * Resolved model id, e.g. "claude-opus-5". */ id?: string; @@ -90,10 +90,16 @@ export namespace GenerateCreateResponse { } /** - * Aggregate token usage for the turn when exposed by the caller. Builder copilot - * endpoints expose it only in local/dev/QA and omit it in staging/production. + * Aggregate token usage and billed AI credits for the turn. Estimated provider + * cost is included only by builder copilot endpoints in local/dev/QA. */ export interface Usage { + /** + * Marked-up integer AI credits used by the complete turn, including failover + * attempts. + */ + aiCreditsUsed?: number; + cachedInputTokens?: number; estimatedCostMicroUsd?: number; @@ -111,10 +117,10 @@ export namespace GenerateCreateResponse { export interface GenerateCreateParams { /** * Body param: Conversation messages in chronological order, capped at 10 messages. - * The last `user` message is the prompt for this turn; any earlier - * `user`/`assistant` text turns are forwarded to the model as prior chat context. - * A `user` message may carry a predefined prompt action via `metadata.action.id` - * (e.g. SPELLING, REPHRASE). + * The last `user` message is the prompt for this turn; the newest earlier + * `user`/`assistant` turns are forwarded within a 12,000-character aggregate + * history budget. A `user` message may carry a predefined prompt action via + * `metadata.action.id` (e.g. SPELLING, REPHRASE). */ messages: Array; @@ -153,8 +159,7 @@ export interface GenerateCreateParams { /** * Body param: Preferred AI model in "provider/id" form, e.g. - * "anthropic/claude-opus-4-7". Optional — server resolves a default per output - * kind. + * "anthropic/claude-opus-5". Optional — server resolves a default per output kind. */ model?: string; } diff --git a/src/resources/templates/import.ts b/src/resources/templates/import.ts index 4e00541..dbffd65 100644 --- a/src/resources/templates/import.ts +++ b/src/resources/templates/import.ts @@ -85,10 +85,10 @@ export interface ImportCreateParams { /** * Body param: Preferred AI model. Accepts a provider/model string (e.g. - * "anthropic/claude-opus-4-7", "openai/gpt-5.5"), a bare provider ("anthropic", + * "anthropic/claude-opus-5", "openai/gpt-5.6-luna"), a bare provider ("anthropic", * "openai") which uses that provider's default model, or a bare model id - * ("claude-opus-4-7", "gpt-5.5") with the provider inferred from the name. - * Optional — defaults to anthropic/claude-opus-4-7. + * ("claude-opus-5", "gpt-5.6-luna") with the provider inferred from the name. + * Optional — defaults to anthropic/claude-opus-5. */ model?: string; } From bff9ad6962155e5b799146fd10258f22771c7043 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 15 Aug 2026 20:59:35 +0000 Subject: [PATCH 53/54] feat(api): api update --- .stats.yml | 4 +-- src/resources/templates/generate.ts | 34 +++++++++++++++++++ .../api-resources/templates/generate.test.ts | 13 +++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 203960b..15f8edc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 51 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-d939bc17efff27c8a66b48b80e6ef9d5790105eed03bbddf57642c65a22ad16c.yml -openapi_spec_hash: f60ac2085adb51c5a437684028a065b2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-a96fca229b9908a56f32846b8ac997932655bc35644c267004c3bada5ed3d9fe.yml +openapi_spec_hash: e3c4bccf1cf35f4ba5ec3d8ba207c689 config_hash: 31bbe4c50af7bd9aea5582d9a2ab786d diff --git a/src/resources/templates/generate.ts b/src/resources/templates/generate.ts index 8420147..8220af9 100644 --- a/src/resources/templates/generate.ts +++ b/src/resources/templates/generate.ts @@ -223,6 +223,8 @@ export namespace GenerateCreateParams { export interface Context { availableTools?: Array; + brand?: Context.Brand; + customTools?: Array; fullDesign?: { [key: string]: unknown } | null; @@ -233,6 +235,38 @@ export namespace GenerateCreateParams { } export namespace Context { + export interface Brand { + colors?: Brand.Colors; + + companyName?: string; + + fonts?: Brand.Fonts; + + guidelines?: string; + + productDescription?: string; + + targetAudience?: string; + + voice?: string; + } + + export namespace Brand { + export interface Colors { + accent?: string; + + primary?: string; + + secondary?: string; + } + + export interface Fonts { + body?: string; + + heading?: string; + } + } + export interface CustomTool { options: { [key: string]: unknown }; diff --git a/tests/api-resources/templates/generate.test.ts b/tests/api-resources/templates/generate.test.ts index e383821..ba969ba 100644 --- a/tests/api-resources/templates/generate.test.ts +++ b/tests/api-resources/templates/generate.test.ts @@ -46,6 +46,19 @@ describe('resource generate', () => { projectId: 'projectId', context: { availableTools: ['string'], + brand: { + colors: { + accent: 'accent', + primary: 'primary', + secondary: 'secondary', + }, + companyName: 'companyName', + fonts: { body: 'body', heading: 'heading' }, + guidelines: 'guidelines', + productDescription: 'productDescription', + targetAudience: 'targetAudience', + voice: 'voice', + }, customTools: [ { options: { foo: 'bar' }, From 94d6a0d536f8a6b99fe6da44b335a2359d4bb8a8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 15 Aug 2026 23:18:18 +0000 Subject: [PATCH 54/54] feat(api): api update --- .stats.yml | 4 ++-- src/resources/templates/generate.ts | 18 +++++++++++++++++- tests/api-resources/templates/generate.test.ts | 2 ++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 15f8edc..5f064af 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 51 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-a96fca229b9908a56f32846b8ac997932655bc35644c267004c3bada5ed3d9fe.yml -openapi_spec_hash: e3c4bccf1cf35f4ba5ec3d8ba207c689 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-231dfb6902a557101992c19fd91a3cf6d4d187b06c9d6b20f2b70d7df77e664a.yml +openapi_spec_hash: 487c5e8289dd85c5d412f6635c2bc3fc config_hash: 31bbe4c50af7bd9aea5582d9a2ab786d diff --git a/src/resources/templates/generate.ts b/src/resources/templates/generate.ts index 8220af9..d835af5 100644 --- a/src/resources/templates/generate.ts +++ b/src/resources/templates/generate.ts @@ -221,9 +221,11 @@ export namespace GenerateCreateParams { } export interface Context { + availableFonts?: Array; + availableTools?: Array; - brand?: Context.Brand; + brand?: Context.Brand | null; customTools?: Array; @@ -235,6 +237,12 @@ export namespace GenerateCreateParams { } export namespace Context { + export interface AvailableFont { + label: string; + + value: string; + } + export interface Brand { colors?: Brand.Colors; @@ -244,6 +252,8 @@ export namespace GenerateCreateParams { guidelines?: string; + logos?: Brand.Logos; + productDescription?: string; targetAudience?: string; @@ -265,6 +275,12 @@ export namespace GenerateCreateParams { heading?: string; } + + export interface Logos { + primary?: string; + + secondary?: string; + } } export interface CustomTool { diff --git a/tests/api-resources/templates/generate.test.ts b/tests/api-resources/templates/generate.test.ts index ba969ba..476e271 100644 --- a/tests/api-resources/templates/generate.test.ts +++ b/tests/api-resources/templates/generate.test.ts @@ -45,6 +45,7 @@ describe('resource generate', () => { }, projectId: 'projectId', context: { + availableFonts: [{ label: 'x', value: 'x' }], availableTools: ['string'], brand: { colors: { @@ -55,6 +56,7 @@ describe('resource generate', () => { companyName: 'companyName', fonts: { body: 'body', heading: 'heading' }, guidelines: 'guidelines', + logos: { primary: 'https://example.com', secondary: 'https://example.com' }, productDescription: 'productDescription', targetAudience: 'targetAudience', voice: 'voice',