diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 746dabb..05b5c45 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/**'
@@ -17,12 +19,12 @@ jobs:
timeout-minutes: 10
name: lint
runs-on: ${{ github.repository == 'stainless-sdks/unlayer-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
- if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
+ if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
steps:
- - uses: actions/checkout@v6
+ - 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'
@@ -36,15 +38,15 @@ jobs:
timeout-minutes: 5
name: build
runs-on: ${{ github.repository == 'stainless-sdks/unlayer-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
- if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
+ if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
permissions:
contents: read
id-token: write
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'
@@ -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 }}
@@ -74,10 +80,10 @@ jobs:
runs-on: ${{ github.repository == 'stainless-sdks/unlayer-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml
index 8d3b32c..59442f8 100644
--- a/.github/workflows/publish-npm.yml
+++ b/.github/workflows/publish-npm.yml
@@ -14,10 +14,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Node
- uses: actions/setup-node@v3
+ uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1
with:
node-version: '20'
diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml
index 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/.release-please-manifest.json b/.release-please-manifest.json
index 466df71..2be9c43 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.1.0"
+ ".": "0.2.0"
}
diff --git a/.stats.yml b/.stats.yml
index 2702d73..eb0d57f 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: 10
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer/unlayer-35699cec89167aa9ce539f8008695911611f8bdf923234ed701ee3dbc0c5bcd2.yml
+openapi_spec_hash: 2ec4eef9500ac0007e1740f431835931
+config_hash: 20e7fbba9d423291aaf676f6a629dcaf
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 53c2610..6b6aa64 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,57 @@
# Changelog
+## 0.2.0 (2026-06-17)
+
+Full Changelog: [v0.1.0...v0.2.0](https://github.com/unlayer/unlayer-typescript/compare/v0.1.0...v0.2.0)
+
+### Features
+
+* **api:** api update ([83ea993](https://github.com/unlayer/unlayer-typescript/commit/83ea9936cd068e51773fc25d5b87beb8caa673d0))
+* **api:** api update ([2096590](https://github.com/unlayer/unlayer-typescript/commit/2096590a872261e1d9dfe6b1f70f513cb25d78cd))
+* **api:** api update ([58c42c3](https://github.com/unlayer/unlayer-typescript/commit/58c42c366680e518d1d9fa45e67ee1c915ebb5c7))
+* **api:** api update ([6f63cba](https://github.com/unlayer/unlayer-typescript/commit/6f63cba6c4e4158a76951f682b5391bac71e74ee))
+
+
+### Bug Fixes
+
+* **client:** preserve URL params already embedded in path ([aa35c3f](https://github.com/unlayer/unlayer-typescript/commit/aa35c3f9daed566ab225bb5460667b55accfdec3))
+* **client:** send content-type header for requests with an omitted optional body ([8716a27](https://github.com/unlayer/unlayer-typescript/commit/8716a278e99b36d3492f4086b50945300df6ce10))
+
+
+### Chores
+
+* **ci:** skip lint on metadata-only changes ([2ff6735](https://github.com/unlayer/unlayer-typescript/commit/2ff6735db5aee19634ac92a8f5903a2496b5d9fd))
+* **ci:** skip uploading artifacts on stainless-internal branches ([5ba5d28](https://github.com/unlayer/unlayer-typescript/commit/5ba5d2871132cd901801f72d9d82ae8a0d03adc1))
+* **internal:** codegen related update ([ebe2eb7](https://github.com/unlayer/unlayer-typescript/commit/ebe2eb769f8ec18596f18a547efb5c750dc53230))
+* **internal:** codegen related update ([b70eaba](https://github.com/unlayer/unlayer-typescript/commit/b70eaba3b4daf2159bd9107c932954631ff5d3ca))
+* **internal:** codegen related update ([5df33b9](https://github.com/unlayer/unlayer-typescript/commit/5df33b94a8781fe7de5c54c232cc3dd556d80a6b))
+* **internal:** codegen related update ([bbe3564](https://github.com/unlayer/unlayer-typescript/commit/bbe35648d10ca91c0d625385894e1dbeb6337bca))
+* **internal:** codegen related update ([c58eac2](https://github.com/unlayer/unlayer-typescript/commit/c58eac28e0d8a7c42225766239777c882f238632))
+* **internal:** codegen related update ([740e1ae](https://github.com/unlayer/unlayer-typescript/commit/740e1ae35ac61a36d7f1e109456844432d2bd6c6))
+* **internal:** codegen related update ([7ce40c6](https://github.com/unlayer/unlayer-typescript/commit/7ce40c6dd2c9ff9f0442e6f89b60ca47c19a9351))
+* **internal:** codegen related update ([6dc4c88](https://github.com/unlayer/unlayer-typescript/commit/6dc4c8844896dcc94667c0b7ff0d0ea37e886e1a))
+* **internal:** codegen related update ([5d0b8ca](https://github.com/unlayer/unlayer-typescript/commit/5d0b8cae4d04648e5904edde4c4be65d2f22b144))
+* **internal:** codegen related update ([c6204bd](https://github.com/unlayer/unlayer-typescript/commit/c6204bdf620e82a1b0de3859c74a0b04ddb220be))
+* **internal:** more robust bootstrap script ([3c105aa](https://github.com/unlayer/unlayer-typescript/commit/3c105aadb230acbf26379b4e7ac258f7c1cf8637))
+* **internal:** move stringifyQuery implementation to internal function ([a382843](https://github.com/unlayer/unlayer-typescript/commit/a3828431e022b0c294eea5dd6d3dfec91dc50f3c))
+* **internal:** tweak CI branches ([37047f6](https://github.com/unlayer/unlayer-typescript/commit/37047f64ad65e540363a5888057fb2031797fa89))
+* **internal:** update dependencies to address dependabot vulnerabilities ([cd210f0](https://github.com/unlayer/unlayer-typescript/commit/cd210f0014bf4c6f91f96b0458b97b28b9b02004))
+* **internal:** update gitignore ([3f8fe21](https://github.com/unlayer/unlayer-typescript/commit/3f8fe21e12dd47a7ec5381b1d01c4b3319d48f0d))
+* **internal:** update multipart form array serialization ([eb9810b](https://github.com/unlayer/unlayer-typescript/commit/eb9810b160faa0c0eb5607a900d0bc3efe80c837))
+* **test:** do not count install time for mock server timeout ([3f4f137](https://github.com/unlayer/unlayer-typescript/commit/3f4f1372bc0146d0abd93d289d1edf1a4c6ce864))
+* **tests:** bump steady to v0.19.4 ([bce44ad](https://github.com/unlayer/unlayer-typescript/commit/bce44adfdaa4fca510a83d182fc9b4788ad454c1))
+* **tests:** bump steady to v0.19.5 ([1ecdf4a](https://github.com/unlayer/unlayer-typescript/commit/1ecdf4a084af6afb151688f3d9ae50ed99d978cb))
+* **tests:** bump steady to v0.19.6 ([13a8f4a](https://github.com/unlayer/unlayer-typescript/commit/13a8f4ab35b1f6487aaf4b2ae1f9fcc771df00f8))
+* **tests:** bump steady to v0.19.7 ([3944238](https://github.com/unlayer/unlayer-typescript/commit/39442381621c93dbeed18b939de999606bcdf3f2))
+* **tests:** bump steady to v0.20.1 ([f058a02](https://github.com/unlayer/unlayer-typescript/commit/f058a02837772b49e641ab186988c76aa4445695))
+* **tests:** bump steady to v0.20.2 ([e312dc6](https://github.com/unlayer/unlayer-typescript/commit/e312dc60c1bf68b3faa91d94feee712f2ab22b60))
+* **tests:** bump steady to v0.22.1 ([005c463](https://github.com/unlayer/unlayer-typescript/commit/005c46350d8b94a4273e5f2490c6f450a7eb8459))
+
+
+### Refactors
+
+* **tests:** switch from prism to steady ([3b6f5e4](https://github.com/unlayer/unlayer-typescript/commit/3b6f5e4fe8b7e7df20f500d9191075331c56310e))
+
## 0.1.0 (2026-02-24)
Full Changelog: [v0.0.1...v0.1.0](https://github.com/unlayer/unlayer-typescript/compare/v0.0.1...v0.1.0)
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..9e7e057 100644
--- a/api.md
+++ b/api.md
@@ -1,46 +1,65 @@
-# Convert
+# Projects
+
+Types:
-## FullToSimple
+- ProjectRetrieveResponse
+
+Methods:
+
+- client.projects.retrieve(id) -> ProjectRetrieveResponse
+
+# Templates
Types:
-- FullToSimpleCreateResponse
+- TemplateRetrieveResponse
+- TemplateListResponse
Methods:
-- client.convert.fullToSimple.create({ ...params }) -> FullToSimpleCreateResponse
+- client.templates.retrieve(id, { ...params }) -> TemplateRetrieveResponse
+- client.templates.list({ ...params }) -> TemplateListResponsesCursorPage
-## SimpleToFull
+## ConvertFullToSimple
Types:
-- SimpleToFullCreateResponse
+- ConvertFullToSimpleCreateResponse
Methods:
-- client.convert.simpleToFull.create({ ...params }) -> SimpleToFullCreateResponse
+- client.templates.convertFullToSimple.create({ ...params }) -> ConvertFullToSimpleCreateResponse
-# Projects
+## ConvertSimpleToFull
Types:
-- ProjectRetrieveResponse
+- ConvertSimpleToFullCreateResponse
Methods:
-- client.projects.retrieve(id) -> ProjectRetrieveResponse
+- client.templates.convertSimpleToFull.create({ ...params }) -> ConvertSimpleToFullCreateResponse
-# Templates
+## Generate
+
+Types:
+
+- GenerateCreateResponse
+
+Methods:
+
+- client.templates.generate.create({ ...params }) -> GenerateCreateResponse
+- client.templates.generate.retrieve() -> void
+
+## Import
Types:
-- TemplateRetrieveResponse
-- TemplateListResponse
+- ImportCreateResponse
Methods:
-- client.templates.retrieve(id, { ...params }) -> TemplateRetrieveResponse
-- client.templates.list({ ...params }) -> TemplateListResponsesCursorPage
+- client.templates.import.create({ ...params }) -> ImportCreateResponse
# Workspaces
diff --git a/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..b720396 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@unlayer/sdk",
- "version": "0.1.0",
+ "version": "0.2.0",
"description": "The official TypeScript library for the Unlayer API",
"author": "Unlayer ",
"types": "dist/index.d.ts",
@@ -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..ad86390 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';
@@ -19,6 +20,7 @@ import * as Uploads from './core/uploads';
import * as API from './resources/index';
import { APIPromise } from './core/api-promise';
import { ProjectRetrieveResponse, Projects } from './resources/projects';
+import { WorkspaceListResponse, WorkspaceRetrieveResponse, Workspaces } from './resources/workspaces';
import {
TemplateListParams,
TemplateListResponse,
@@ -26,9 +28,7 @@ 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 { type Fetch } from './internal/builtin-types';
import { HeadersLike, NullableHeaders, buildHeaders } from './internal/headers';
import { FinalRequestOptions, RequestOptions } from './internal/request-options';
@@ -191,6 +191,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 +283,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 +316,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 +651,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 +748,19 @@ export class Unlayer {
return () => controller.abort();
}
- private buildBody({ options: { body, headers: rawHeaders } }: { options: FinalRequestOptions }): {
+ private buildBody({ options }: { options: FinalRequestOptions }): {
bodyHeaders: HeadersLike;
body: BodyInit | undefined;
} {
+ const { body, headers: rawHeaders } = options;
if (!body) {
+ // A resource method always passes a `body` key when its operation defines a
+ // request body, even if the caller omitted an optional body param. Keep the
+ // content-type for those, and only elide it for operations with no body at
+ // all (e.g. GET/DELETE).
+ if (body == null && 'body' in options) {
+ return this.#encoder({ body, headers: buildHeaders([rawHeaders]) });
+ }
return { bodyHeaders: undefined, body: undefined };
}
const headers = buildHeaders([rawHeaders]);
@@ -786,7 +794,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,13 +820,20 @@ export class Unlayer {
static toFile = Uploads.toFile;
- convert: API.Convert = new API.Convert(this);
+ /**
+ * Project details and configuration.
+ */
projects: API.Projects = new API.Projects(this);
+ /**
+ * Template management — list, retrieve, generate, import, export, and convert designs.
+ */
templates: API.Templates = new API.Templates(this);
+ /**
+ * Workspace access and management.
+ */
workspaces: API.Workspaces = new API.Workspaces(this);
}
-Unlayer.Convert = Convert;
Unlayer.Projects = Projects;
Unlayer.Templates = Templates;
Unlayer.Workspaces = Workspaces;
@@ -829,8 +844,6 @@ export declare namespace Unlayer {
export import CursorPage = Pagination.CursorPage;
export { type CursorPageParams as CursorPageParams, type CursorPageResponse as CursorPageResponse };
- export { Convert as Convert };
-
export { Projects as Projects, type ProjectRetrieveResponse as ProjectRetrieveResponse };
export {
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/convert.ts b/src/resources/convert.ts
deleted file mode 100644
index 1334f91..0000000
--- a/src/resources/convert.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-export * from './convert/index';
diff --git a/src/resources/convert/convert.ts b/src/resources/convert/convert.ts
deleted file mode 100644
index d7930c4..0000000
--- a/src/resources/convert/convert.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-import { APIResource } from '../../core/resource';
-import * as FullToSimpleAPI from './full-to-simple';
-import { FullToSimple, FullToSimpleCreateParams, FullToSimpleCreateResponse } from './full-to-simple';
-import * as SimpleToFullAPI from './simple-to-full';
-import { SimpleToFull, SimpleToFullCreateParams, SimpleToFullCreateResponse } from './simple-to-full';
-
-export class Convert extends APIResource {
- fullToSimple: FullToSimpleAPI.FullToSimple = new FullToSimpleAPI.FullToSimple(this._client);
- simpleToFull: SimpleToFullAPI.SimpleToFull = new SimpleToFullAPI.SimpleToFull(this._client);
-}
-
-Convert.FullToSimple = FullToSimple;
-Convert.SimpleToFull = SimpleToFull;
-
-export declare namespace Convert {
- export {
- FullToSimple as FullToSimple,
- type FullToSimpleCreateResponse as FullToSimpleCreateResponse,
- type FullToSimpleCreateParams as FullToSimpleCreateParams,
- };
-
- export {
- SimpleToFull as SimpleToFull,
- type SimpleToFullCreateResponse as SimpleToFullCreateResponse,
- type SimpleToFullCreateParams as SimpleToFullCreateParams,
- };
-}
diff --git a/src/resources/convert/index.ts b/src/resources/convert/index.ts
deleted file mode 100644
index 833a9fc..0000000
--- a/src/resources/convert/index.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-export { Convert } from './convert';
-export {
- FullToSimple,
- type FullToSimpleCreateResponse,
- type FullToSimpleCreateParams,
-} from './full-to-simple';
-export {
- SimpleToFull,
- type SimpleToFullCreateResponse,
- type SimpleToFullCreateParams,
-} from './simple-to-full';
diff --git a/src/resources/convert/simple-to-full.ts b/src/resources/convert/simple-to-full.ts
deleted file mode 100644
index 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/index.ts b/src/resources/index.ts
index 303eae0..6ca716c 100644
--- a/src/resources/index.ts
+++ b/src/resources/index.ts
@@ -1,6 +1,5 @@
// 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 {
Templates,
@@ -9,5 +8,5 @@ export {
type TemplateRetrieveParams,
type TemplateListParams,
type TemplateListResponsesCursorPage,
-} from './templates';
+} from './templates/templates';
export { Workspaces, type WorkspaceRetrieveResponse, type WorkspaceListResponse } from './workspaces';
diff --git a/src/resources/projects.ts b/src/resources/projects.ts
index 6b97668..0d15122 100644
--- a/src/resources/projects.ts
+++ b/src/resources/projects.ts
@@ -5,6 +5,9 @@ import { APIPromise } from '../core/api-promise';
import { RequestOptions } from '../internal/request-options';
import { path } from '../internal/utils/path';
+/**
+ * Project details and configuration.
+ */
export class Projects extends APIResource {
/**
* Get project details by ID.
diff --git a/src/resources/templates.ts b/src/resources/templates.ts
index 38eef4b..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/convert/full-to-simple.ts b/src/resources/templates/convert-full-to-simple.ts
similarity index 50%
rename from src/resources/convert/full-to-simple.ts
rename to src/resources/templates/convert-full-to-simple.ts
index 44e02b9..c8b3e8c 100644
--- a/src/resources/convert/full-to-simple.ts
+++ b/src/resources/templates/convert-full-to-simple.ts
@@ -4,29 +4,35 @@ import { APIResource } from '../../core/resource';
import { APIPromise } from '../../core/api-promise';
import { RequestOptions } from '../../internal/request-options';
-export class FullToSimple extends APIResource {
+/**
+ * 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: FullToSimpleCreateParams, options?: RequestOptions): APIPromise {
- return this._client.post('/v3/convert/full-to-simple', { body, ...options });
+ create(
+ body: ConvertFullToSimpleCreateParams,
+ options?: RequestOptions,
+ ): APIPromise {
+ return this._client.post('/v3/templates/convert/full-to-simple', { body, ...options });
}
}
-export interface FullToSimpleCreateResponse {
- data?: FullToSimpleCreateResponse.Data;
+export interface ConvertFullToSimpleCreateResponse {
+ data?: ConvertFullToSimpleCreateResponse.Data;
success?: true;
}
-export namespace FullToSimpleCreateResponse {
+export namespace ConvertFullToSimpleCreateResponse {
export interface Data {
design?: { [key: string]: unknown };
}
}
-export interface FullToSimpleCreateParams {
- design: FullToSimpleCreateParams.Design;
+export interface ConvertFullToSimpleCreateParams {
+ design: ConvertFullToSimpleCreateParams.Design;
displayMode?: 'email' | 'web' | 'popup' | 'document';
@@ -39,7 +45,7 @@ export interface FullToSimpleCreateParams {
includeDefaultValues?: boolean;
}
-export namespace FullToSimpleCreateParams {
+export namespace ConvertFullToSimpleCreateParams {
export interface Design {
body: { [key: string]: unknown };
@@ -51,9 +57,9 @@ export namespace FullToSimpleCreateParams {
}
}
-export declare namespace FullToSimple {
+export declare namespace ConvertFullToSimple {
export {
- type FullToSimpleCreateResponse as FullToSimpleCreateResponse,
- type FullToSimpleCreateParams as FullToSimpleCreateParams,
+ type ConvertFullToSimpleCreateResponse as ConvertFullToSimpleCreateResponse,
+ type ConvertFullToSimpleCreateParams as ConvertFullToSimpleCreateParams,
};
}
diff --git a/src/resources/templates/convert-simple-to-full.ts b/src/resources/templates/convert-simple-to-full.ts
new file mode 100644
index 0000000..c1c0af0
--- /dev/null
+++ b/src/resources/templates/convert-simple-to-full.ts
@@ -0,0 +1,69 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../core/resource';
+import { APIPromise } from '../../core/api-promise';
+import { RequestOptions } from '../../internal/request-options';
+
+/**
+ * Template management — list, retrieve, generate, import, export, and convert designs.
+ */
+export class ConvertSimpleToFull extends APIResource {
+ /**
+ * Convert design json from Simple to Full schema.
+ */
+ create(
+ body: ConvertSimpleToFullCreateParams,
+ options?: RequestOptions,
+ ): APIPromise {
+ return this._client.post('/v3/templates/convert/simple-to-full', { body, ...options });
+ }
+}
+
+export interface ConvertSimpleToFullCreateResponse {
+ data?: ConvertSimpleToFullCreateResponse.Data;
+
+ success?: true;
+}
+
+export namespace ConvertSimpleToFullCreateResponse {
+ export interface Data {
+ design?: { [key: string]: unknown };
+ }
+}
+
+export interface ConvertSimpleToFullCreateParams {
+ design: ConvertSimpleToFullCreateParams.Design;
+
+ displayMode?: 'email' | 'web' | 'popup' | 'document';
+
+ includeDefaultValues?: boolean;
+}
+
+export namespace ConvertSimpleToFullCreateParams {
+ export interface Design {
+ body: { [key: string]: unknown };
+
+ _conversion?: Design._Conversion;
+
+ counters?: { [key: string]: unknown };
+
+ schemaVersion?: number;
+
+ [k: string]: unknown;
+ }
+
+ export namespace Design {
+ export interface _Conversion {
+ data?: string;
+
+ version?: number;
+ }
+ }
+}
+
+export declare namespace ConvertSimpleToFull {
+ export {
+ type ConvertSimpleToFullCreateResponse as ConvertSimpleToFullCreateResponse,
+ type ConvertSimpleToFullCreateParams as ConvertSimpleToFullCreateParams,
+ };
+}
diff --git a/src/resources/templates/generate.ts b/src/resources/templates/generate.ts
new file mode 100644
index 0000000..a5b06ca
--- /dev/null
+++ b/src/resources/templates/generate.ts
@@ -0,0 +1,248 @@
+// 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 for the turn when exposed by the caller. Builder copilot
+ * endpoints expose it only in local/dev/QA and omit it in staging/production.
+ */
+ usage?: GenerateCreateResponse.Usage;
+}
+
+export namespace GenerateCreateResponse {
+ /**
+ * The provider + model that actually produced the output (may differ from the
+ * requested model after failover).
+ */
+ export interface Model {
+ /**
+ * Resolved model id, e.g. "claude-opus-4-7".
+ */
+ id?: string;
+
+ /**
+ * 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 for the turn when exposed by the caller. Builder copilot
+ * endpoints expose it only in local/dev/QA and omit it in staging/production.
+ */
+ export interface Usage {
+ cachedInputTokens?: number;
+
+ estimatedCostMicroUsd?: number;
+
+ inputTokens?: number;
+
+ outputTokens?: number;
+
+ 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; any earlier
+ * `user`/`assistant` text turns are forwarded to the model as prior chat context.
+ * A `user` message may carry a predefined prompt action via `metadata.action.id`
+ * (e.g. SPELLING, REPHRASE).
+ */
+ 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: BCP-47 fallback locale for AI status messages.
+ */
+ locale?: string;
+
+ /**
+ * Body param: AI model in "provider/id" form, e.g. "anthropic/claude-opus-4-7".
+ * Optional — server resolves a default per output kind.
+ */
+ model?: string;
+}
+
+export namespace GenerateCreateParams {
+ export interface 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 {
+ availableTools?: Array;
+
+ customTools?: Array;
+
+ fullDesign?: { [key: string]: unknown } | null;
+
+ selection?: Context.Selection | null;
+
+ [k: string]: unknown;
+ }
+
+ export namespace Context {
+ 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..1060f2c
--- /dev/null
+++ b/src/resources/templates/import.ts
@@ -0,0 +1,121 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../core/resource';
+import { APIPromise } from '../../core/api-promise';
+import { RequestOptions } from '../../internal/request-options';
+
+/**
+ * Template management — list, retrieve, generate, import, export, and convert designs.
+ */
+export class Import extends APIResource {
+ /**
+ * Import an existing template from HTML or an image (URL or base64) and return the
+ * resulting Unlayer design JSON. No template DB entry is created.
+ */
+ create(params: ImportCreateParams, options?: RequestOptions): APIPromise {
+ const { projectId, ...body } = params;
+ return this._client.post('/v3/templates/import', { query: { projectId }, body, ...options });
+ }
+}
+
+/**
+ * Successfully imported template
+ */
+export interface ImportCreateResponse {
+ id?: string;
+
+ model?: string;
+
+ output?: ImportCreateResponse.Output;
+
+ provider?: string;
+
+ usage?: ImportCreateResponse.Usage;
+}
+
+export namespace ImportCreateResponse {
+ export interface Output {
+ blockType?: string;
+
+ /**
+ * Imported design data
+ */
+ data?: { [key: string]: unknown };
+
+ type?: string;
+ }
+
+ export interface Usage {
+ cachedInputTokens?: number;
+
+ inputTokens?: number;
+
+ outputTokens?: number;
+
+ reasoningTokens?: number;
+
+ totalTokens?: number;
+ }
+}
+
+export interface ImportCreateParams {
+ /**
+ * Body param: Display mode for the imported design
+ */
+ displayMode: 'email' | 'web' | 'popup' | 'document';
+
+ /**
+ * Body param: Array of input parts. Must contain exactly one "html" or "image"
+ * part; may also contain one or more "text" parts with optional instructions.
+ */
+ input: Array;
+
+ /**
+ * Query param: The project ID (required for PAT auth, auto-resolved for API key
+ * auth)
+ */
+ projectId?: string;
+
+ /**
+ * Body param: AI model to use. Accepts a provider/model string (e.g.
+ * "anthropic/claude-opus-4-7", "openai/gpt-5.5"), a bare provider ("anthropic",
+ * "openai") which uses that provider's default model, or a bare model id
+ * ("claude-opus-4-7", "gpt-5.5") with the provider inferred from the name.
+ * Optional — defaults to anthropic/claude-opus-4-7.
+ */
+ model?: 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..b4f6e4e
--- /dev/null
+++ b/src/resources/templates/index.ts
@@ -0,0 +1,22 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export {
+ ConvertFullToSimple,
+ type ConvertFullToSimpleCreateResponse,
+ type ConvertFullToSimpleCreateParams,
+} from './convert-full-to-simple';
+export {
+ ConvertSimpleToFull,
+ type ConvertSimpleToFullCreateResponse,
+ type ConvertSimpleToFullCreateParams,
+} from './convert-simple-to-full';
+export { Generate, type GenerateCreateResponse, type GenerateCreateParams } from './generate';
+export { Import, type ImportCreateResponse, type ImportCreateParams } from './import';
+export {
+ Templates,
+ type TemplateRetrieveResponse,
+ type TemplateListResponse,
+ type TemplateRetrieveParams,
+ type TemplateListParams,
+ type TemplateListResponsesCursorPage,
+} from './templates';
diff --git a/src/resources/templates/templates.ts b/src/resources/templates/templates.ts
new file mode 100644
index 0000000..fd15c00
--- /dev/null
+++ b/src/resources/templates/templates.ts
@@ -0,0 +1,165 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../core/resource';
+import * as ConvertFullToSimpleAPI from './convert-full-to-simple';
+import {
+ ConvertFullToSimple,
+ ConvertFullToSimpleCreateParams,
+ ConvertFullToSimpleCreateResponse,
+} from './convert-full-to-simple';
+import * as ConvertSimpleToFullAPI from './convert-simple-to-full';
+import {
+ ConvertSimpleToFull,
+ ConvertSimpleToFullCreateParams,
+ ConvertSimpleToFullCreateResponse,
+} from './convert-simple-to-full';
+import * as GenerateAPI from './generate';
+import { Generate, GenerateCreateParams, GenerateCreateResponse } from './generate';
+import * as ImportAPI from './import';
+import { Import, ImportCreateParams, ImportCreateResponse } from './import';
+import { APIPromise } from '../../core/api-promise';
+import { CursorPage, type CursorPageParams, PagePromise } from '../../core/pagination';
+import { RequestOptions } from '../../internal/request-options';
+import { path } from '../../internal/utils/path';
+
+/**
+ * Template management — list, retrieve, generate, import, export, and convert designs.
+ */
+export class Templates extends APIResource {
+ convertFullToSimple: ConvertFullToSimpleAPI.ConvertFullToSimple =
+ new ConvertFullToSimpleAPI.ConvertFullToSimple(this._client);
+ convertSimpleToFull: ConvertSimpleToFullAPI.ConvertSimpleToFull =
+ new ConvertSimpleToFullAPI.ConvertSimpleToFull(this._client);
+ generate: GenerateAPI.Generate = new GenerateAPI.Generate(this._client);
+ import: ImportAPI.Import = new ImportAPI.Import(this._client);
+
+ /**
+ * Get template by ID.
+ */
+ retrieve(
+ id: string,
+ query: TemplateRetrieveParams | null | undefined = {},
+ options?: RequestOptions,
+ ): APIPromise {
+ return this._client.get(path`/v3/templates/${id}`, { query, ...options });
+ }
+
+ /**
+ * List templates with cursor-based pagination. Returns templates in descending
+ * order by update time.
+ */
+ list(
+ query: TemplateListParams | null | undefined = {},
+ options?: RequestOptions,
+ ): PagePromise {
+ return this._client.getAPIList('/v3/templates', CursorPage, { query, ...options });
+ }
+}
+
+export type TemplateListResponsesCursorPage = CursorPage;
+
+export interface TemplateRetrieveResponse {
+ data?: TemplateRetrieveResponse.Data;
+}
+
+export namespace TemplateRetrieveResponse {
+ export interface Data {
+ id?: string;
+
+ createdAt?: string;
+
+ design?: { [key: string]: unknown };
+
+ displayMode?: 'email' | 'web' | 'document';
+
+ html?: string | null;
+
+ name?: string;
+
+ updatedAt?: string;
+ }
+}
+
+export interface TemplateListResponse {
+ /**
+ * Template ID
+ */
+ id?: string;
+
+ createdAt?: string;
+
+ /**
+ * Template type/display mode
+ */
+ displayMode?: 'email' | 'web' | 'document';
+
+ /**
+ * Template name
+ */
+ name?: string;
+
+ updatedAt?: string;
+}
+
+export interface TemplateRetrieveParams {
+ /**
+ * The project ID (required for PAT auth, auto-resolved for API key auth)
+ */
+ projectId?: string;
+}
+
+export interface TemplateListParams extends CursorPageParams {
+ /**
+ * Filter by template type
+ */
+ displayMode?: 'email' | 'web' | 'document';
+
+ /**
+ * Filter by name (case-insensitive search)
+ */
+ name?: string;
+
+ /**
+ * The project ID to list templates for
+ */
+ projectId?: string;
+}
+
+Templates.ConvertFullToSimple = ConvertFullToSimple;
+Templates.ConvertSimpleToFull = ConvertSimpleToFull;
+Templates.Generate = Generate;
+Templates.Import = Import;
+
+export declare namespace Templates {
+ export {
+ type TemplateRetrieveResponse as TemplateRetrieveResponse,
+ type TemplateListResponse as TemplateListResponse,
+ type TemplateListResponsesCursorPage as TemplateListResponsesCursorPage,
+ type TemplateRetrieveParams as TemplateRetrieveParams,
+ type TemplateListParams as TemplateListParams,
+ };
+
+ export {
+ ConvertFullToSimple as ConvertFullToSimple,
+ type ConvertFullToSimpleCreateResponse as ConvertFullToSimpleCreateResponse,
+ type ConvertFullToSimpleCreateParams as ConvertFullToSimpleCreateParams,
+ };
+
+ export {
+ ConvertSimpleToFull as ConvertSimpleToFull,
+ type ConvertSimpleToFullCreateResponse as ConvertSimpleToFullCreateResponse,
+ type ConvertSimpleToFullCreateParams as ConvertSimpleToFullCreateParams,
+ };
+
+ export {
+ Generate as Generate,
+ type GenerateCreateResponse as GenerateCreateResponse,
+ type GenerateCreateParams as GenerateCreateParams,
+ };
+
+ export {
+ Import as Import,
+ type ImportCreateResponse as ImportCreateResponse,
+ type ImportCreateParams as ImportCreateParams,
+ };
+}
diff --git a/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/src/version.ts b/src/version.ts
index 1baa228..bade2ff 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -1 +1 @@
-export const VERSION = '0.1.0'; // x-release-please-version
+export const VERSION = '0.2.0'; // x-release-please-version
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/generate.test.ts b/tests/api-resources/templates/generate.test.ts
new file mode 100644
index 0000000..c6526f4
--- /dev/null
+++ b/tests/api-resources/templates/generate.test.ts
@@ -0,0 +1,78 @@
+// 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: {
+ availableTools: ['string'],
+ customTools: [
+ {
+ options: { foo: 'bar' },
+ slug: 'slug',
+ },
+ ],
+ fullDesign: { foo: 'bar' },
+ selection: {
+ id: 'string',
+ collection: 'pages',
+ value: 'value',
+ },
+ },
+ conversationId: 'conversationId',
+ locale: 'locale',
+ model: 'model',
+ });
+ });
+
+ test('retrieve', async () => {
+ const responsePromise = client.templates.generate.retrieve();
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+});
diff --git a/tests/api-resources/templates/import.test.ts b/tests/api-resources/templates/import.test.ts
new file mode 100644
index 0000000..6f66884
--- /dev/null
+++ b/tests/api-resources/templates/import.test.ts
@@ -0,0 +1,41 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Unlayer from '@unlayer/sdk';
+
+const client = new Unlayer({
+ apiKey: 'My API Key',
+ baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource import', () => {
+ test('create: only required params', async () => {
+ const responsePromise = client.templates.import.create({
+ displayMode: 'email',
+ input: [{ type: 'html' }],
+ });
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('create: required and optional params', async () => {
+ const response = await client.templates.import.create({
+ displayMode: 'email',
+ input: [
+ {
+ type: 'html',
+ data: 'data',
+ html: 'html',
+ text: 'text',
+ url: 'url',
+ },
+ ],
+ projectId: 'projectId',
+ model: 'model',
+ });
+ });
+});
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/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..06fc108 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -709,11 +709,6 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
-"@pkgr/core@^0.2.4":
- version "0.2.4"
- resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.4.tgz#d897170a2b0ba51f78a099edccd968f7b103387c"
- integrity sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==
-
"@sinclair/typebox@^0.27.8":
version "0.27.8"
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
@@ -1219,18 +1214,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.1"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.1.tgz#c68b1c4111c76aae3a6fba55d496cee10c39dad8"
+ integrity sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==
dependencies:
balanced-match "^1.0.0"
@@ -1395,11 +1382,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 +1510,6 @@ escape-string-regexp@^4.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
-eslint-plugin-prettier@^5.4.1:
- version "5.4.1"
- resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.1.tgz#99b55d7dd70047886b2222fdd853665f180b36af"
- integrity sha512-9dF+KuU/Ilkq27A8idRP7N2DH8iUR6qXcjF3FR2wETY21PZdBrIjwCau8oboyGj9b7etWmTGEeM8e7oOed6ZWg==
- dependencies:
- prettier-linter-helpers "^1.0.0"
- synckit "^0.11.7"
-
eslint-plugin-unused-imports@^4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz#62ddc7446ccbf9aa7b6f1f0b00a980423cda2738"
@@ -1687,11 +1661,6 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
-fast-diff@^1.1.2:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0"
- integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==
-
fast-glob@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
@@ -2600,26 +2569,12 @@ mimic-fn@^2.1.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
-minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
- integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
- dependencies:
- brace-expansion "^1.1.7"
-
-minimatch@^5.0.1:
- version "5.1.6"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
- integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
- dependencies:
- brace-expansion "^2.0.1"
-
-minimatch@^9.0.4:
- version "9.0.5"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5"
- integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
+minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2, minimatch@^5.0.1, minimatch@^9.0.4, minimatch@^9.0.5:
+ version "9.0.9"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.9.tgz#9b0cb9fcb78087f6fd7eababe2511c4d3d60574e"
+ integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==
dependencies:
- brace-expansion "^2.0.1"
+ brace-expansion "^2.0.2"
minimist@^1.2.6:
version "1.2.6"
@@ -2868,13 +2823,6 @@ prelude-ls@^1.2.1:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
-prettier-linter-helpers@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
- integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
- dependencies:
- fast-diff "^1.1.2"
-
prettier@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.1.1.tgz#6ba9f23165d690b6cbdaa88cb0807278f7019848"
@@ -3171,13 +3119,6 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
-synckit@^0.11.7:
- version "0.11.8"
- resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.8.tgz#b2aaae998a4ef47ded60773ad06e7cb821f55457"
- integrity sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==
- dependencies:
- "@pkgr/core" "^0.2.4"
-
test-exclude@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
@@ -3251,9 +3192,9 @@ ts-node@^10.5.0:
v8-compile-cache-lib "^3.0.0"
yn "3.1.1"
-"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz":
- version "1.1.9"
- resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz#777f6f5d9e26bf0e94e5170990dd3a841d6707cd"
+"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.11/tsc-multi.tgz":
+ version "1.1.11"
+ resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.11/tsc-multi.tgz#010247051be13b55abdc98f787c017285149f4f2"
dependencies:
debug "^4.3.7"
fast-glob "^3.3.2"