diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 746dabb..a15001b 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/**' @@ -16,15 +18,15 @@ jobs: lint: 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 + 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@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' + node-version: '24' - name: Bootstrap run: ./scripts/bootstrap @@ -35,18 +37,18 @@ jobs: build: 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 + 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 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' + node-version: '24' - name: Bootstrap run: ./scripts/bootstrap @@ -55,14 +57,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 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 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 }} @@ -71,15 +77,15 @@ 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@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' + node-version: '24' - name: Bootstrap run: ./scripts/bootstrap diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index 8d3b32c..17e1064 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -14,12 +14,12 @@ 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' + node-version: '24' - name: Install dependencies run: | diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 71339c4..4b829da 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -12,11 +12,10 @@ 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: | bash ./bin/check-release-environment env: NPM_TOKEN: ${{ secrets.UNLAYER_NPM_TOKEN || secrets.NPM_TOKEN }} - 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 diff --git a/.stats.yml b/.stats.yml index 2702d73..5f064af 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 +configured_endpoints: 51 +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/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/api.md b/api.md index 6cee992..76404ab 100644 --- a/api.md +++ b/api.md @@ -1,46 +1,369 @@ -# Convert +# Blocks -## FullToSimple +Types: + +- BlockRetrieveResponse + +Methods: + +- client.blocks.retrieve({ ...params }) -> BlockRetrieveResponse + +# 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: + +- EditorSessionCreateResponse + +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: -- FullToSimpleCreateResponse +- SuppressionsCheckRetrieveResponse Methods: -- client.convert.fullToSimple.create({ ...params }) -> FullToSimpleCreateResponse +- client.emails.suppressionsCheck.retrieve({ ...params }) -> SuppressionsCheckRetrieveResponse -## SimpleToFull +## Template Types: -- SimpleToFullCreateResponse +- TemplateCreateResponse Methods: -- client.convert.simpleToFull.create({ ...params }) -> SimpleToFullCreateResponse +- client.emails.template.create({ ...params }) -> TemplateCreateResponse + +# Me + +## Subscription + +Types: + +- SubscriptionRetrieveResponse + +Methods: + +- client.me.subscription.retrieve({ ...params }) -> SubscriptionRetrieveResponse # Projects Types: -- ProjectRetrieveResponse +- ProjectRetrieveResponse + +Methods: + +- client.projects.retrieve(id) -> ProjectRetrieveResponse + +## AICredits + +Types: + +- AICreditRetrieveResponse + +Methods: + +- client.projects.aiCredits.retrieve(id) -> AICreditRetrieveResponse + +## AICreditsSettings + +Types: + +- AICreditsSettingRetrieveResponse +- AICreditsSettingUpdateResponse Methods: -- client.projects.retrieve(id) -> ProjectRetrieveResponse +- 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 Types: -- TemplateRetrieveResponse -- TemplateListResponse +- TemplateRetrieveResponse +- TemplateListResponse + +Methods: + +- client.templates.retrieve(id, { ...params }) -> TemplateRetrieveResponse +- client.templates.list({ ...params }) -> TemplateListResponsesCursorPage + +## ConvertFullToSimple + +Types: + +- ConvertFullToSimpleCreateResponse + +Methods: + +- client.templates.convertFullToSimple.create({ ...params }) -> ConvertFullToSimpleCreateResponse + +## ConvertSimpleToFull + +Types: + +- ConvertSimpleToFullCreateResponse + +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: + +- GenerateCreateResponse + +Methods: + +- client.templates.generate.create({ ...params }) -> GenerateCreateResponse +- client.templates.generate.retrieve() -> void + +## Import + +Types: + +- ImportCreateResponse + +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.templates.retrieve(id, { ...params }) -> TemplateRetrieveResponse -- client.templates.list({ ...params }) -> TemplateListResponsesCursorPage +- client.webhooks.rotateSecret.create(id) -> RotateSecretCreateResponse # Workspaces 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 af883de..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", @@ -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", @@ -44,12 +43,23 @@ "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", "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/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 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/scripts/mock b/scripts/mock index 0b28f6e..feebe5e 100755 --- a/scripts/mock +++ b/scripts/mock @@ -19,23 +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 - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & + # Pre-install the package so the download doesn't eat into the startup timeout + npm exec --package=@stdy/cli@0.22.1 -- steady --version - # Wait for server to come online + 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" - while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + attempts=0 + 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 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.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 7bce051..19b8d0c 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.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 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 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 889cbc0..dd24fe1 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'; @@ -18,7 +19,31 @@ 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 { ProjectRetrieveResponse, Projects } from './resources/projects'; +import { BlockRetrieveParams, BlockRetrieveResponse, Blocks } from './resources/blocks'; +import { + EditorSessionCreateParams, + EditorSessionCreateResponse, + 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 { TemplateListParams, TemplateListResponse, @@ -26,9 +51,17 @@ import { TemplateRetrieveParams, TemplateRetrieveResponse, Templates, -} from './resources/templates'; -import { WorkspaceListResponse, WorkspaceRetrieveResponse, Workspaces } from './resources/workspaces'; -import { Convert } from './resources/convert/convert'; +} 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'; @@ -191,6 +224,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; @@ -271,21 +316,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 { @@ -317,12 +349,13 @@ 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)) { - url.search = this.stringifyQuery(query as Record); + url.search = this.stringifyQuery(query); } return url.toString(); @@ -651,9 +684,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); } @@ -748,11 +781,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]); @@ -786,7 +827,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 }); @@ -812,15 +853,37 @@ export class Unlayer { static toFile = Uploads.toFile; - convert: API.Convert = new API.Convert(this); + /** + * 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); + me: API.Me = new API.Me(this); + /** + * Project details and configuration. + */ projects: API.Projects = new API.Projects(this); + /** + * 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.Convert = Convert; +Unlayer.Blocks = Blocks; +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 { @@ -829,7 +892,37 @@ export declare namespace Unlayer { export import CursorPage = Pagination.CursorPage; export { type CursorPageParams as CursorPageParams, type CursorPageResponse as CursorPageResponse }; - export { Convert as Convert }; + export { + Blocks as Blocks, + type BlockRetrieveResponse as BlockRetrieveResponse, + type BlockRetrieveParams as BlockRetrieveParams, + }; + + 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 }; @@ -842,6 +935,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/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/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/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; }; 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' ) ? 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/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/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/full-to-simple.ts b/src/resources/convert/full-to-simple.ts deleted file mode 100644 index 44e02b9..0000000 --- a/src/resources/convert/full-to-simple.ts +++ /dev/null @@ -1,59 +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'; - -export class FullToSimple 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 }); - } -} - -export interface FullToSimpleCreateResponse { - data?: FullToSimpleCreateResponse.Data; - - success?: true; -} - -export namespace FullToSimpleCreateResponse { - export interface Data { - design?: { [key: string]: unknown }; - } -} - -export interface FullToSimpleCreateParams { - design: FullToSimpleCreateParams.Design; - - displayMode?: 'email' | 'web' | 'popup' | 'document'; - - /** - * When true, includes \_conversion metadata in the response. This metadata can be - * passed to simple-to-full to restore original values without data loss. - */ - includeConversion?: boolean; - - includeDefaultValues?: boolean; -} - -export namespace FullToSimpleCreateParams { - export interface Design { - body: { [key: string]: unknown }; - - counters?: { [key: string]: unknown }; - - schemaVersion?: number; - - [k: string]: unknown; - } -} - -export declare namespace FullToSimple { - export { - type FullToSimpleCreateResponse as FullToSimpleCreateResponse, - type FullToSimpleCreateParams as FullToSimpleCreateParams, - }; -} 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 c1051de..0000000 --- a/src/resources/convert/simple-to-full.ts +++ /dev/null @@ -1,63 +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'; - -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/convert.ts b/src/resources/domains.ts similarity index 72% rename from src/resources/convert.ts rename to src/resources/domains.ts index 1334f91..5c8cf7e 100644 --- a/src/resources/convert.ts +++ b/src/resources/domains.ts @@ -1,3 +1,3 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -export * from './convert/index'; +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/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/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 303eae0..c40edde 100644 --- a/src/resources/index.ts +++ b/src/resources/index.ts @@ -1,7 +1,29 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -export { Convert } from './convert/convert'; -export { Projects, type ProjectRetrieveResponse } from './projects'; +export { Blocks, type BlockRetrieveResponse, type BlockRetrieveParams } from './blocks'; +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 { Templates, type TemplateRetrieveResponse, @@ -9,5 +31,15 @@ export { type TemplateRetrieveParams, type TemplateListParams, type TemplateListResponsesCursorPage, -} from './templates'; +} 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/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/src/resources/projects.ts b/src/resources/projects.ts index 6b97668..f9985fc 100644 --- a/src/resources/projects.ts +++ b/src/resources/projects.ts @@ -1,57 +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'; - -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..77631f9 --- /dev/null +++ b/src/resources/projects/ai-credits-usage.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'; +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. 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. + */ + 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/src/resources/templates.ts b/src/resources/templates.ts index 38eef4b..cf710e0 100644 --- a/src/resources/templates.ts +++ b/src/resources/templates.ts @@ -1,110 +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'; - -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/templates/convert-full-to-simple.ts b/src/resources/templates/convert-full-to-simple.ts new file mode 100644 index 0000000..e5a3c08 --- /dev/null +++ b/src/resources/templates/convert-full-to-simple.ts @@ -0,0 +1,71 @@ +// 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 ConvertFullToSimple extends APIResource { + /** + * Convert design json from Full to Simple schema. + */ + create( + body: ConvertFullToSimpleCreateParams, + options?: RequestOptions, + ): APIPromise { + return this._client.post('/v3/templates/convert/full-to-simple', { body, ...options }); + } +} + +export interface ConvertFullToSimpleCreateResponse { + data?: ConvertFullToSimpleCreateResponse.Data; + + success?: true; +} + +export namespace ConvertFullToSimpleCreateResponse { + export interface Data { + design?: { [key: string]: unknown }; + } +} + +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'; + + /** + * When true, includes \_conversion metadata in the response. This metadata can be + * passed to simple-to-full to restore original values without data loss. + */ + includeConversion?: boolean; + + includeDefaultValues?: boolean; +} + +export namespace ConvertFullToSimpleCreateParams { + export interface Design { + body: { [key: string]: unknown }; + + counters?: { [key: string]: unknown }; + + schemaVersion?: number; + + [k: string]: unknown; + } +} + +export declare namespace ConvertFullToSimple { + export { + 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..0e848d5 --- /dev/null +++ b/src/resources/templates/convert-simple-to-full.ts @@ -0,0 +1,75 @@ +// 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; + + /** + * 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; +} + +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/templates/export-html.ts b/src/resources/templates/export-html.ts new file mode 100644 index 0000000..7803e53 --- /dev/null +++ b/src/resources/templates/export-html.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 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 { + amp?: { [key: string]: unknown }; + + chunks?: Data.Chunks; + + design?: { [key: string]: unknown }; + + html?: string; + } + + export namespace Data { + export interface Chunks { + body?: string; + + css?: string; + + fonts?: Array; + + js?: string; + + tags?: Array; + } + } +} + +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/generate.ts b/src/resources/templates/generate.ts new file mode 100644 index 0000000..d835af5 --- /dev/null +++ b/src/resources/templates/generate.ts @@ -0,0 +1,311 @@ +// 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 Generate extends APIResource { + /** + * 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]), + }); + } +} + +/** + * The generated (or modified) design plus model metadata and optional usage + * metadata. + */ +export interface GenerateCreateResponse { + /** + * Provider response id for the generation turn. + */ + id?: 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; + + /** + * 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; +} + +export namespace GenerateCreateResponse { + /** + * 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-5". + */ + id?: string; + + /** + * 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 }; + + /** + * Echoes the requested `output.kind`. + */ + kind?: string; + } + + /** + * 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; + + inputTokens?: number; + + outputTokens?: number; + + reasoningTokens?: number; + + totalTokens?: number; + } +} + +export interface GenerateCreateParams { + /** + * Body param: Conversation messages in chronological order, capped at 10 messages. + * 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; + + /** + * Body param + */ + output: GenerateCreateParams.Output; + + /** + * Query param: The project ID (required for PAT auth, auto-resolved for API key + * auth) + */ + projectId?: string; + + /** + * Body param + */ + context?: GenerateCreateParams.Context; + + /** + * Body param: Reserved for future server-side conversation memory. + */ + 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: Preferred AI model in "provider/id" form, e.g. + * "anthropic/claude-opus-5". Optional — server resolves a default per output kind. + */ + model?: string; +} + +export namespace GenerateCreateParams { + export interface Message { + content: Array; + + role: 'user' | 'assistant' | 'system'; + + metadata?: Message.Metadata; + } + + export namespace Message { + export interface Content { + type: 'text' | 'image' | 'file'; + + file?: Content.File; + + /** + * URL or data URL of the image + */ + image?: string; + + text?: 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; + } + } + } + + export interface Output { + displayMode: 'email' | 'web' | 'popup' | 'document'; + + kind: 'template' | 'page' | 'body' | 'header' | 'footer' | 'row' | 'column' | 'content' | 'text'; + + schemaVersion?: number; + } + + export interface Context { + availableFonts?: Array; + + availableTools?: Array; + + brand?: Context.Brand | null; + + customTools?: Array; + + fullDesign?: { [key: string]: unknown } | null; + + selection?: Context.Selection | null; + + [k: string]: unknown; + } + + export namespace Context { + export interface AvailableFont { + label: string; + + value: string; + } + + export interface Brand { + colors?: Brand.Colors; + + companyName?: string; + + fonts?: Brand.Fonts; + + guidelines?: string; + + logos?: Brand.Logos; + + 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 Logos { + primary?: string; + + secondary?: string; + } + } + + export interface CustomTool { + options: { [key: string]: unknown }; + + slug: string; + + [k: string]: unknown; + } + + export interface Selection { + id: string | number; + + collection: 'pages' | 'bodies' | 'rows' | 'columns' | 'contents' | 'headers' | 'footers'; + + value?: string; + + [k: string]: unknown; + } + } +} + +export declare namespace Generate { + export { + type GenerateCreateResponse as GenerateCreateResponse, + type GenerateCreateParams as GenerateCreateParams, + }; +} diff --git a/src/resources/templates/import.ts b/src/resources/templates/import.ts new file mode 100644 index 0000000..dbffd65 --- /dev/null +++ b/src/resources/templates/import.ts @@ -0,0 +1,128 @@ +// 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: 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-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-5", "gpt-5.6-luna") with the provider inferred from the name. + * Optional — defaults to anthropic/claude-opus-5. + */ + model?: string; +} + +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..62ba98f --- /dev/null +++ b/src/resources/templates/index.ts @@ -0,0 +1,28 @@ +// 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 { 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 { Schema, type SchemaRetrieveParams } from './schema'; +export { + Templates, + type TemplateRetrieveResponse, + type TemplateListResponse, + type TemplateRetrieveParams, + 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 new file mode 100644 index 0000000..45f8f0f --- /dev/null +++ b/src/resources/templates/templates.ts @@ -0,0 +1,221 @@ +// 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 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'; +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'; +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); + 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); + schema: SchemaAPI.Schema = new SchemaAPI.Schema(this._client); + validate: ValidateAPI.Validate = new ValidateAPI.Validate(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.ExportHTML = ExportHTML; +Templates.ExportImage = ExportImage; +Templates.ExportPdf = ExportPdf; +Templates.ExportZip = ExportZip; +Templates.Generate = Generate; +Templates.Import = Import; +Templates.Schema = Schema; +Templates.Validate = Validate; + +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 { + 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, + type GenerateCreateParams as GenerateCreateParams, + }; + + export { + Import as Import, + 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/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 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); + }); +}); 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/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/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/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); + }); +}); 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 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/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, + }); + }); +}); diff --git a/tests/api-resources/templates/generate.test.ts b/tests/api-resources/templates/generate.test.ts new file mode 100644 index 0000000..476e271 --- /dev/null +++ b/tests/api-resources/templates/generate.test.ts @@ -0,0 +1,94 @@ +// 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.templates.generate.create({ + messages: [{ content: [{ type: 'text' }], role: 'user' }], + output: { displayMode: 'email', kind: 'template' }, + }); + 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.generate.create({ + messages: [ + { + content: [ + { + type: 'text', + file: { url: 'url', mediaType: 'mediaType' }, + image: 'image', + text: 'text', + }, + ], + role: 'user', + metadata: { action: { id: 'id' } }, + }, + ], + output: { + displayMode: 'email', + kind: 'template', + schemaVersion: 0, + }, + projectId: 'projectId', + context: { + availableFonts: [{ label: 'x', value: 'x' }], + availableTools: ['string'], + brand: { + colors: { + accent: 'accent', + primary: 'primary', + secondary: 'secondary', + }, + companyName: 'companyName', + fonts: { body: 'body', heading: 'heading' }, + guidelines: 'guidelines', + logos: { primary: 'https://example.com', secondary: 'https://example.com' }, + productDescription: 'productDescription', + targetAudience: 'targetAudience', + voice: 'voice', + }, + customTools: [ + { + options: { foo: 'bar' }, + slug: 'slug', + }, + ], + fullDesign: { foo: 'bar' }, + selection: { + id: 'string', + collection: 'pages', + value: 'value', + }, + }, + conversationId: 'conversationId', + fallbackModels: true, + 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 new file mode 100644 index 0000000..2101af0 --- /dev/null +++ b/tests/api-resources/templates/import.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 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', + fallbackModels: true, + model: 'model', + }); + }); +}); 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.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 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); + }); +}); 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); }); 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 fc9f262..f16b683 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" @@ -709,11 +722,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" @@ -1219,18 +1227,10 @@ 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: - version "2.0.2" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" - integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== +brace-expansion@^2.0.2: + 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" @@ -1395,11 +1395,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" @@ -1528,14 +1523,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" @@ -1687,11 +1674,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" @@ -1727,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" @@ -2521,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" @@ -2600,26 +2582,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== +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 "^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== - dependencies: - brace-expansion "^2.0.1" + brace-expansion "^2.0.2" minimist@^1.2.6: version "1.2.6" @@ -2868,13 +2836,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" @@ -3171,13 +3132,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" @@ -3251,9 +3205,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"