diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 32dbe0e2b0..affd1ab3d1 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "5.0.0" + ".": "5.1.0" } diff --git a/.stats.yml b/.stats.yml index 75717626cc..c4c893951c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 1809 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare%2Fcloudflare-77d61495fecd0d26b9adff1af0ab3510b06a3cc2c6781b9a40aabcad2f10588a.yml openapi_spec_hash: 95dee3be411dda77306a41dc7d49eb35 -config_hash: db0ff227af424d15b1c84f467e04c023 +config_hash: ac04197a992afb1d8c3b416fc46e8c8e diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e5d5710ad..2ecc9139b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## 5.1.0 (2025-09-12) + +Full Changelog: [v5.0.0...v5.1.0](https://github.com/cloudflare/cloudflare-typescript/compare/v5.0.0...v5.1.0) + +### Features + +* Merge branch 'vaishak/skip-worker-test' into 'main' ([a556698](https://github.com/cloudflare/cloudflare-typescript/commit/a5566983ae47453ffc4d5d9a266d7480ed620d51)) + + +### Bug Fixes + +* coerce nullable values to undefined ([7847e84](https://github.com/cloudflare/cloudflare-typescript/commit/7847e845eb146f099a63f32931e5991840799c9c)) +* correctly handle sending multipart/form-data requests with JSON ([e9deab6](https://github.com/cloudflare/cloudflare-typescript/commit/e9deab61fad243c4339df7362529fd9b92693f8a)) + + +### Chores + +* ci build action ([23d3577](https://github.com/cloudflare/cloudflare-typescript/commit/23d3577a92a835ffc1aeffb3004801b75efeb136)) +* fix lint on the examples ([dd14379](https://github.com/cloudflare/cloudflare-typescript/commit/dd143796d0494f2c9a57493a330f229ce5d2533a)) +* fix lint on the examples ([dd14379](https://github.com/cloudflare/cloudflare-typescript/commit/dd143796d0494f2c9a57493a330f229ce5d2533a)) +* fix lint on the examples ([5b2f145](https://github.com/cloudflare/cloudflare-typescript/commit/5b2f145f72d68b9f9bfde59c55323b4abb89ca71)) +* **internal:** codegen related update ([2860479](https://github.com/cloudflare/cloudflare-typescript/commit/2860479dc7310af6a02647597900aba2c76c554c)) + ## 5.0.0 (2025-09-02) Full Changelog: [v4.5.0...v5.0.0](https://github.com/cloudflare/cloudflare-typescript/compare/v4.5.0...v5.0.0) diff --git a/examples/workers/script-upload.ts b/examples/workers/script-upload.ts index 653b27fc83..19b254ea80 100644 --- a/examples/workers/script-upload.ts +++ b/examples/workers/script-upload.ts @@ -2,11 +2,11 @@ /** * Create and deploy a Worker - * + * * Docs: * - https://developers.cloudflare.com/workers/configuration/versions-and-deployments/ * - https://developers.cloudflare.com/workers/platform/infrastructure-as-code/ - * + * * Prerequisites: * 1. Generate an API token: https://developers.cloudflare.com/fundamentals/api/get-started/create-token/ * 2. Find your account ID: https://developers.cloudflare.com/fundamentals/setup/find-account-and-zone-ids/ @@ -72,7 +72,7 @@ async function main(): Promise { return new Response(env.MESSAGE, { status: 200 }); }, }`.trim(); - + let worker; try { worker = await client.workers.beta.workers.get(config.workerName, { @@ -80,7 +80,9 @@ async function main(): Promise { }); console.log(`♻️ Worker ${config.workerName} already exists. Using it.`); } catch (error) { - if (!(error instanceof Cloudflare.NotFoundError)) { throw error; } + if (!(error instanceof Cloudflare.NotFoundError)) { + throw error; + } console.log(`✏️ Creating Worker ${config.workerName}...`); worker = await client.workers.beta.workers.create({ account_id: config.accountId, @@ -96,7 +98,7 @@ async function main(): Promise { console.log(`⚙️ Worker id: ${worker.id}`); console.log('✏️ Creating Worker version...'); - + // Create the first version of the Worker const version = await client.workers.beta.workers.versions.create(worker.id, { account_id: config.accountId, @@ -120,21 +122,21 @@ async function main(): Promise { console.log(`⚙️ Version id: ${version.id}`); console.log('🚚 Creating Worker deployment...'); - + // Create a deployment and point all traffic to the version we created await client.workers.scripts.deployments.create(config.workerName, { account_id: config.accountId, strategy: 'percentage', versions: [ { - percentage: 100, - version_id: version.id, - }, - ], + percentage: 100, + version_id: version.id, + }, + ], }); - + console.log('✅ Deployment successful!'); - + if (config.subdomain) { console.log(` 🌍 Your Worker is live! diff --git a/examples/workers/script-with-assets-upload.ts b/examples/workers/script-with-assets-upload.ts index 7fe1134909..a8d7e1348a 100644 --- a/examples/workers/script-with-assets-upload.ts +++ b/examples/workers/script-with-assets-upload.ts @@ -2,11 +2,11 @@ /** * Create a Worker that serves static assets - * + * * This example demonstrates how to: * - Upload static assets to Cloudflare Workers * - Create and deploy a Worker that serves those assets - * + * * Docs: * - https://developers.cloudflare.com/workers/static-assets/direct-upload * @@ -17,7 +17,7 @@ * * Environment variables: * - CLOUDFLARE_API_TOKEN (required) - * - CLOUDFLARE_ACCOUNT_ID (required) + * - CLOUDFLARE_ACCOUNT_ID (required) * - ASSETS_DIRECTORY (required) * - CLOUDFLARE_SUBDOMAIN (optional) * @@ -99,11 +99,11 @@ const client = new Cloudflare({ */ function createManifest(directory: string): AssetManifest { const manifest: AssetManifest = {}; - + function processDirectory(currentDir: string, basePath = ''): void { try { const entries = fs.readdirSync(currentDir, { withFileTypes: true }); - + for (const entry of entries) { const fullPath = path.join(currentDir, entry.name); const relativePath = path.join(basePath, entry.name); @@ -124,12 +124,12 @@ function createManifest(directory: string): AssetManifest { // Normalize path separators to forward slashes const manifestPath = `/${relativePath.replace(/\\/g, '/')}`; - + manifest[manifestPath] = { hash, size: fileContent.length, }; - + console.log(`Added to manifest: ${manifestPath} (${fileContent.length} bytes)`); } catch (error) { console.warn(`Failed to process file ${fullPath}:`, error); @@ -140,13 +140,13 @@ function createManifest(directory: string): AssetManifest { throw new Error(`Failed to read directory ${currentDir}: ${error}`); } } - + processDirectory(directory); - + if (Object.keys(manifest).length === 0) { throw new Error(`No files found in assets directory: ${directory}`); } - + console.log(`Created manifest with ${Object.keys(manifest).length} files`); return manifest; } @@ -201,26 +201,24 @@ export default { async function createUploadPayloads( buckets: string[][], manifest: AssetManifest, - assetsDirectory: string + assetsDirectory: string, ): Promise { const payloads: UploadPayload[] = []; - + for (const bucket of buckets) { const payload: UploadPayload = {}; - + for (const hash of bucket) { // Find the file path for this hash - const manifestEntry = Object.entries(manifest).find( - ([_, data]) => data.hash === hash - ); - + const manifestEntry = Object.entries(manifest).find(([_, data]) => data.hash === hash); + if (!manifestEntry) { throw new Error(`Could not find file for hash: ${hash}`); } - + const [relativePath] = manifestEntry; const fullPath = path.join(assetsDirectory, relativePath); - + try { const fileContent = await readFile(fullPath); payload[hash] = fileContent.toString('base64'); @@ -229,10 +227,10 @@ async function createUploadPayloads( throw new Error(`Failed to read file ${fullPath}: ${error}`); } } - + payloads.push(payload); } - + return payloads; } @@ -242,16 +240,16 @@ async function createUploadPayloads( async function uploadAssets( payloads: UploadPayload[], uploadJwt: string, - accountId: string + accountId: string, ): Promise { let completionJwt: string | undefined; - + console.log(`Uploading ${payloads.length} payload(s)...`); - + for (let i = 0; i < payloads.length; i++) { const payload = payloads[i]!; console.log(`Uploading payload ${i + 1}/${payloads.length}...`); - + try { const response = await client.workers.assets.upload.create( { @@ -261,9 +259,9 @@ async function uploadAssets( }, { headers: { Authorization: `Bearer ${uploadJwt}` }, - } + }, ); - + if (response?.jwt) { completionJwt = response.jwt; } @@ -271,11 +269,11 @@ async function uploadAssets( throw new Error(`Failed to upload payload ${i + 1}: ${error}`); } } - + if (!completionJwt) { throw new Error('Upload completed but no completion JWT received'); } - + console.log('✅ All assets uploaded successfully'); return completionJwt; } @@ -284,13 +282,13 @@ async function main(): Promise { try { console.log('🚀 Starting Worker creation and deployment with static assets...'); console.log(`📁 Assets directory: ${config.assetsDirectory}`); - + console.log('📝 Creating asset manifest...'); const manifest = createManifest(config.assetsDirectory); const exampleFile = Object.keys(manifest)[0]?.replace(/^\//, '') || 'file.txt'; const scriptContent = generateWorkerScript(exampleFile); - + let worker; try { worker = await client.workers.beta.workers.get(config.workerName, { @@ -298,7 +296,9 @@ async function main(): Promise { }); console.log(`♻️ Worker ${config.workerName} already exists. Using it.`); } catch (error) { - if (!(error instanceof Cloudflare.NotFoundError)) { throw error; } + if (!(error instanceof Cloudflare.NotFoundError)) { + throw error; + } console.log(`✏️ Creating Worker ${config.workerName}...`); worker = await client.workers.beta.workers.create({ account_id: config.accountId, @@ -314,39 +314,28 @@ async function main(): Promise { console.log(`⚙️ Worker id: ${worker.id}`); console.log('🔄 Starting asset upload session...'); - - const uploadResponse = await client.workers.scripts.assets.upload.create( - config.workerName, - { - account_id: config.accountId, - manifest, - } - ); - + + const uploadResponse = await client.workers.scripts.assets.upload.create(config.workerName, { + account_id: config.accountId, + manifest, + }); + const { buckets, jwt: uploadJwt } = uploadResponse; - + if (!uploadJwt || !buckets) { throw new Error('Failed to start asset upload session'); } - + let completionJwt: string; - + if (buckets.length === 0) { console.log('✅ No new assets to upload!'); // Use the initial upload JWT as completion JWT when no uploads are needed completionJwt = uploadJwt; } else { - const payloads = await createUploadPayloads( - buckets, - manifest, - config.assetsDirectory - ); - - completionJwt = await uploadAssets( - payloads, - uploadJwt, - config.accountId - ); + const payloads = await createUploadPayloads(buckets, manifest, config.assetsDirectory); + + completionJwt = await uploadAssets(payloads, uploadJwt, config.accountId); } console.log('✏️ Creating Worker version...'); @@ -373,23 +362,23 @@ async function main(): Promise { }, ], }); - + console.log('🚚 Creating Worker deployment...'); - + // Create a deployment and point all traffic to the version we created await client.workers.scripts.deployments.create(config.workerName, { account_id: config.accountId, strategy: 'percentage', versions: [ { - percentage: 100, - version_id: version.id, - }, - ], + percentage: 100, + version_id: version.id, + }, + ], }); console.log('✅ Deployment successful!'); - + if (config.subdomain) { console.log(` 🌍 Your Worker is live! diff --git a/package.json b/package.json index 554142f95c..e0d8b54994 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cloudflare", - "version": "5.0.0", + "version": "5.1.0", "description": "The official TypeScript library for the Cloudflare API", "author": "Cloudflare ", "types": "dist/index.d.ts", diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh index b203d04254..37da0aeda5 100755 --- a/scripts/utils/upload-artifact.sh +++ b/scripts/utils/upload-artifact.sh @@ -12,7 +12,7 @@ if [[ "$SIGNED_URL" == "null" ]]; then exit 1 fi -UPLOAD_RESPONSE=$(tar -cz "${BUILD_PATH:-dist}" | curl -v -X PUT \ +UPLOAD_RESPONSE=$(tar "${BASE_PATH:+-C$BASE_PATH}" -cz "${ARTIFACT_PATH:-dist}" | curl -v -X PUT \ -H "Content-Type: application/gzip" \ --data-binary @- "$SIGNED_URL" 2>&1) diff --git a/src/core.ts b/src/core.ts index b921a5e170..4620687513 100644 --- a/src/core.ts +++ b/src/core.ts @@ -1100,21 +1100,21 @@ export const coerceBoolean = (value: unknown): boolean => { }; export const maybeCoerceInteger = (value: unknown): number | undefined => { - if (value === undefined) { + if (value == null) { return undefined; } return coerceInteger(value); }; export const maybeCoerceFloat = (value: unknown): number | undefined => { - if (value === undefined) { + if (value == null) { return undefined; } return coerceFloat(value); }; export const maybeCoerceBoolean = (value: unknown): boolean | undefined => { - if (value === undefined) { + if (value == null) { return undefined; } return coerceBoolean(value); diff --git a/src/resources/radar/ai/to-markdown.ts b/src/resources/radar/ai/to-markdown.ts index 31d9848854..cc254c45c8 100644 --- a/src/resources/radar/ai/to-markdown.ts +++ b/src/resources/radar/ai/to-markdown.ts @@ -25,7 +25,7 @@ export class ToMarkdown extends APIResource { params: ToMarkdownCreateParams, options?: Core.RequestOptions, ): Core.PagePromise { - const { account_id } = params ?? {}; + const { account_id } = params; return this._client.getAPIList( `/accounts/${account_id}/ai/tomarkdown`, ToMarkdownCreateResponsesSinglePage, diff --git a/src/resources/snippets/snippets.ts b/src/resources/snippets/snippets.ts index 41075b61da..bfc3c4dc14 100644 --- a/src/resources/snippets/snippets.ts +++ b/src/resources/snippets/snippets.ts @@ -35,7 +35,7 @@ export class Snippets extends APIResource { return ( this._client.put( `/zones/${zone_id}/snippets/${snippetName}`, - Core.multipartFormRequestOptions({ body, ...options }), + Core.multipartFormRequestOptions({ body, ...options, __multipartSyntax: 'json' }), ) as Core.APIPromise<{ result: SnippetUpdateResponse }> )._thenUnwrap((obj) => obj.result); } diff --git a/src/resources/workers-for-platforms/dispatch/namespaces/scripts/content.ts b/src/resources/workers-for-platforms/dispatch/namespaces/scripts/content.ts index 26364d14d4..f18fe2b478 100644 --- a/src/resources/workers-for-platforms/dispatch/namespaces/scripts/content.ts +++ b/src/resources/workers-for-platforms/dispatch/namespaces/scripts/content.ts @@ -41,6 +41,7 @@ export class Content extends APIResource { Core.multipartFormRequestOptions({ body, ...options, + __multipartSyntax: 'json', headers: { ...(cfWorkerBodyPart != null ? { 'CF-WORKER-BODY-PART': cfWorkerBodyPart } : undefined), ...(cfWorkerMainModulePart != null ? diff --git a/src/resources/workers-for-platforms/dispatch/namespaces/scripts/settings.ts b/src/resources/workers-for-platforms/dispatch/namespaces/scripts/settings.ts index ea51a212a2..3f2241277e 100644 --- a/src/resources/workers-for-platforms/dispatch/namespaces/scripts/settings.ts +++ b/src/resources/workers-for-platforms/dispatch/namespaces/scripts/settings.ts @@ -29,7 +29,7 @@ export class Settings extends APIResource { return ( this._client.patch( `/accounts/${account_id}/workers/dispatch/namespaces/${dispatchNamespace}/scripts/${scriptName}/settings`, - Core.multipartFormRequestOptions({ body, ...options }), + Core.multipartFormRequestOptions({ body, ...options, __multipartSyntax: 'json' }), ) as Core.APIPromise<{ result: SettingEditResponse }> )._thenUnwrap((obj) => obj.result); } diff --git a/src/resources/workers/scripts/content.ts b/src/resources/workers/scripts/content.ts index 797ecb5dd4..2b07197ca7 100644 --- a/src/resources/workers/scripts/content.ts +++ b/src/resources/workers/scripts/content.ts @@ -37,6 +37,7 @@ export class Content extends APIResource { Core.multipartFormRequestOptions({ body, ...options, + __multipartSyntax: 'json', headers: { ...(cfWorkerBodyPart != null ? { 'CF-WORKER-BODY-PART': cfWorkerBodyPart } : undefined), ...(cfWorkerMainModulePart != null ? diff --git a/src/resources/workers/scripts/script-and-version-settings.ts b/src/resources/workers/scripts/script-and-version-settings.ts index b1bbae812a..21511a3bb3 100644 --- a/src/resources/workers/scripts/script-and-version-settings.ts +++ b/src/resources/workers/scripts/script-and-version-settings.ts @@ -27,7 +27,7 @@ export class ScriptAndVersionSettings extends APIResource { return ( this._client.patch( `/accounts/${account_id}/workers/scripts/${scriptName}/settings`, - Core.multipartFormRequestOptions({ body, ...options }), + Core.multipartFormRequestOptions({ body, ...options, __multipartSyntax: 'json' }), ) as Core.APIPromise<{ result: ScriptAndVersionSettingEditResponse }> )._thenUnwrap((obj) => obj.result); } diff --git a/src/version.ts b/src/version.ts index e156b0eaa2..0d43855fee 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '5.0.0'; // x-release-please-version +export const VERSION = '5.1.0'; // x-release-please-version diff --git a/tests/api-resources/workers/scripts/script-and-version-settings.test.ts b/tests/api-resources/workers/scripts/script-and-version-settings.test.ts index 3e22a4b8c7..30eefa0a96 100644 --- a/tests/api-resources/workers/scripts/script-and-version-settings.test.ts +++ b/tests/api-resources/workers/scripts/script-and-version-settings.test.ts @@ -10,7 +10,8 @@ const client = new Cloudflare({ }); describe('resource scriptAndVersionSettings', () => { - test('edit: only required params', async () => { + // 422 Unprocessable Entity: needs schema update which is merged but not published + test.skip('edit: only required params', async () => { const responsePromise = client.workers.scripts.scriptAndVersionSettings.edit('this-is_my_script-01', { account_id: '023e105f4ecef8ad9ca31a8372d0c353', }); @@ -23,7 +24,8 @@ describe('resource scriptAndVersionSettings', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - test('edit: required and optional params', async () => { + // 422 Unprocessable Entity: needs schema update which is merged but not published + test.skip('edit: required and optional params', async () => { const response = await client.workers.scripts.scriptAndVersionSettings.edit('this-is_my_script-01', { account_id: '023e105f4ecef8ad9ca31a8372d0c353', settings: {