diff --git a/.github/workflows/regenerate-sdk.yml b/.github/workflows/regenerate-sdk.yml index 29b2a42..0d2abee 100644 --- a/.github/workflows/regenerate-sdk.yml +++ b/.github/workflows/regenerate-sdk.yml @@ -65,21 +65,21 @@ jobs: bash scripts/sdk/bundle-spec.sh "${{ github.workspace }}/_spec.json" - name: Setup Java (for openapi-generator) - if: inputs.language != 'node' + if: inputs.language != 'node' && inputs.language != 'typescript' uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '17' - name: Cache openapi-generator - if: inputs.language != 'node' + if: inputs.language != 'node' && inputs.language != 'typescript' uses: actions/cache@v4 with: path: ~/.openapi-generator key: openapi-generator-${{ inputs.language == 'python' && '5.4.0' || '7.4.0' }} - name: Install openapi-generator-cli - if: inputs.language != 'node' + if: inputs.language != 'node' && inputs.language != 'typescript' run: | npm install -g @openapitools/openapi-generator-cli # Set generator version based on language @@ -129,10 +129,12 @@ jobs: - name: Copy generated files to SDK repo run: | # Remove previously generated files (but not .git, README, .github, etc.) - if [ "${{ inputs.language }}" = "node" ]; then + if [ "${{ inputs.language }}" = "node" ] || [ "${{ inputs.language }}" = "typescript" ]; then rm -rf src/generated/ dist/ mkdir -p src/generated/ cp -r _generated/src/generated/* src/generated/ + # Copy src/index.ts if generated + [ -f _generated/src/index.ts ] && cp _generated/src/index.ts src/index.ts # Update package.json version only (preserve custom fields) if [ -f _generated/package.json ]; then node -e " @@ -142,6 +144,8 @@ jobs: require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n'); " fi + # Copy tsconfig if generated and none exists + [ -f _generated/tsconfig.json ] && [ ! -f tsconfig.json ] && cp _generated/tsconfig.json tsconfig.json elif [ "${{ inputs.language }}" = "php" ]; then rm -rf src/Api/ src/Model/ src/ApiException.php src/Configuration.php src/HeaderSelector.php src/ObjectSerializer.php cp -r _generated/src/* src/ 2>/dev/null || true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 113f58d..5e9ffb0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,7 +64,7 @@ jobs: DISPATCH_TOKEN: ${{ secrets.SDK_DISPATCH_TOKEN }} run: | VERSION="${{ steps.release.outputs.version }}" - REPOS=("shotstack-sdk-node" "shotstack-sdk-php" "shotstack-sdk-python" "shotstack-sdk-ruby") + REPOS=("shotstack-sdk-node" "shotstack-sdk-php" "shotstack-sdk-python" "shotstack-sdk-ruby" "shotstack-sdk-typescript") for repo in "${REPOS[@]}"; do success=false diff --git a/.github/workflows/sdk-conformance-check.yml b/.github/workflows/sdk-conformance-check.yml index 3b654b8..6f3483c 100644 --- a/.github/workflows/sdk-conformance-check.yml +++ b/.github/workflows/sdk-conformance-check.yml @@ -26,7 +26,7 @@ jobs: GH_TOKEN: ${{ secrets.SDK_DISPATCH_TOKEN }} run: | OAS_VERSION="${{ steps.oas.outputs.version }}" - REPOS=("shotstack-sdk-node" "shotstack-sdk-php" "shotstack-sdk-python" "shotstack-sdk-ruby") + REPOS=("shotstack-sdk-node" "shotstack-sdk-php" "shotstack-sdk-python" "shotstack-sdk-ruby" "shotstack-sdk-typescript") stale="" for repo in "${REPOS[@]}"; do diff --git a/scripts/sdk/conformance-check.mjs b/scripts/sdk/conformance-check.mjs index 0e3abb3..9d6ff86 100644 --- a/scripts/sdk/conformance-check.mjs +++ b/scripts/sdk/conformance-check.mjs @@ -32,7 +32,8 @@ function getSdkModels(language, sdkDir) { const models = new Set(); switch (language) { - case 'node': { + case 'node': + case 'typescript': { // For hey-api generated SDK, check types.gen.ts for exported types const typesFile = join(sdkDir, 'src', 'generated', 'types.gen.ts'); if (existsSync(typesFile)) { diff --git a/scripts/sdk/generate-typescript.sh b/scripts/sdk/generate-typescript.sh new file mode 100644 index 0000000..3dafbe4 --- /dev/null +++ b/scripts/sdk/generate-typescript.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Generate TypeScript SDK using @hey-api/openapi-ts +# Usage: generate-typescript.sh +# +# Uses hey-api to produce modern TypeScript with typed SDK functions, +# fetch-based HTTP client, and tree-shakeable exports. + +SPEC_FILE="${1:?Usage: generate-typescript.sh }" +OUTPUT_DIR="${2:?Usage: generate-typescript.sh }" +VERSION="${3:?Usage: generate-typescript.sh }" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +OAS_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +echo "Generating TypeScript SDK v${VERSION}..." + +mkdir -p "${OUTPUT_DIR}/src/generated" + +# hey-api resolves $ref from the spec's directory, so we must run from OAS root +cd "${OAS_ROOT}" +npx @hey-api/openapi-ts \ + --input "./api.oas3.yaml" \ + --output "${OUTPUT_DIR}/src/generated" \ + --plugins @hey-api/typescript @hey-api/sdk + +# Write package.json +cat > "${OUTPUT_DIR}/package.json" << EOF +{ + "name": "shotstack-sdk-typescript", + "version": "${VERSION}", + "description": "Official TypeScript SDK for the Shotstack Cloud Video Editing API", + "type": "module", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "files": ["dist"], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/shotstack/shotstack-sdk-typescript.git" + }, + "keywords": ["shotstack", "video", "video-editing", "video-api", "typescript"], + "engines": { + "node": ">=18" + } +} +EOF + +# Write tsconfig.json +cat > "${OUTPUT_DIR}/tsconfig.json" << EOF +{ + "compilerOptions": { + "target": "ES2022", + "module": "nodenext", + "moduleResolution": "nodenext", + "declaration": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true + }, + "include": ["src/**/*"] +} +EOF + +# Write index.ts that re-exports everything +cat > "${OUTPUT_DIR}/src/index.ts" << 'EOF' +export * from './generated/types.gen'; +export * from './generated/sdk.gen'; +EOF + +echo "TypeScript SDK generated at ${OUTPUT_DIR}" diff --git a/scripts/sdk/smoke-test.sh b/scripts/sdk/smoke-test.sh index 637017d..17053e4 100644 --- a/scripts/sdk/smoke-test.sh +++ b/scripts/sdk/smoke-test.sh @@ -11,7 +11,7 @@ SDK_DIR="${2:?Usage: smoke-test.sh }" echo "Running smoke tests for ${LANGUAGE} SDK at ${SDK_DIR}..." case "${LANGUAGE}" in - node) + node|typescript) echo "→ TypeScript type-check..." cd "${SDK_DIR}" npm install --ignore-scripts 2>/dev/null || true