diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index bbf3024c5303..e7d1ddf8dc42 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -97,7 +97,22 @@ jobs: - uses: ./.github/actions/verify-package - name: deploy to s3 if: ${{ env.AWS_ACCESS_KEY_ID != '' }} - run: npm run deploy-s3 -- -b "cesium-public-builds" -d cesium/$BRANCH -c 'no-cache' --confirm + run: | + aws s3 sync . s3://cesium-public-builds/cesium/$BRANCH/ \ + --cache-control "no-cache" \ + --exclude ".git/*" \ + --exclude ".concierge/*" \ + --exclude ".github/*" \ + --exclude ".husky/*" \ + --exclude ".vscode/*" \ + --exclude "Build/Coverage/*" \ + --exclude "Build/CesiumDev/*" \ + --exclude "Build/Specs/e2e" \ + --exclude "Documentation/*" \ + --exclude "node_modules/*" \ + --exclude "scripts/*" \ + --exclude "Tools/*" \ + --delete - name: set status if: ${{ env.AWS_ACCESS_KEY_ID != '' }} run: npm run deploy-status -- --status success --message Deployed diff --git a/.github/workflows/prod.yml b/.github/workflows/prod.yml index a906ee8e309b..e9e5a4b00847 100644 --- a/.github/workflows/prod.yml +++ b/.github/workflows/prod.yml @@ -45,4 +45,9 @@ jobs: run: npm run build-apps - name: deploy to cesium.com if: ${{ env.AWS_ACCESS_KEY_ID != '' }} - run: npm run deploy-s3 -- -b "cesium.com-next" -c 'public, max-age=1800' --skip --confirm \ No newline at end of file + run: | + unzip Cesium-$(cat package.json | jq -r '.version').zip -d Build/release/ -x "Apps" + aws s3 sync Build/release/ s3://cesium-website/cesiumjs/releases/$(cat package.json | jq -r '.version')/ --cache-control "public, max-age=1800" --delete + aws s3 sync Build/Documentation/ s3://cesium-website/cesiumjs/ref-doc/ --cache-control "public, max-age=1800" --delete + aws s3 sync Build/CesiumViewer/ s3://cesium-website/cesiumjs/cesium-viewer/ --cache-control "public, max-age=1800" --delete + aws s3 sync Build/Sandcastle/ s3://cesium-sandcastle-website/ --cache-control "public, max-age=1800" --delete diff --git a/.slackbot.yml b/.slackbot.yml index f1b54f977c8d..ca0d87fc8160 100644 --- a/.slackbot.yml +++ b/.slackbot.yml @@ -4,5 +4,11 @@ releaseSchedule: - ggetz, 3/1/2023 - jjhembd, 4/3/2023 - ggetz, 5/1/2023 - -jjhembd, 6/1/2023 + - jjhembd, 6/1/2023 - ggetz, 7/1/2023 + - jjhembd, 8/1/2023 + - ggetz, 9/1/2023 + - jjhembd, 10/1/2023 + - ggetz, 11/1/2023 + - jjhembd, 12/1/2023 + - ggetz, 1/2/2024 diff --git a/CHANGES.md b/CHANGES.md index d61a6c8b42c5..90c14c5724a6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,14 @@ # Change Log +### 1.112 - 2023-12-01 + +#### @cesium/engine + +##### Fixes :wrench: + +- Fixed terrain lockups in `requestTileGeometry` by ensuring promise handling aligns with CesiumJS's expectations. [#11630](https://github.com/CesiumGS/cesium/pull/11630) +- Corrected JSDoc and Typescript definitions that marked optional arguments as required in `Cesium3dTileset.fromIonAssetId` [#11623](https://github.com/CesiumGS/cesium/issues/11623), and `IonImageryProvider.fromAssetId` [#11624](https://github.com/CesiumGS/cesium/issues/11624) + ### 1.111 - 2023-11-01 #### @cesium/engine @@ -10,7 +19,7 @@ ##### Fixes :wrench: -- By default, `createGooglePhotorealistic3DTileset` no longer shows credits on screen, as this is compliant with the minimum required attribution. To restore this behavior, pass the option `showCreditsOnScreen: true`. [#11589](https://github.com/CesiumGS/cesium/pull/11589) +- By default, `createGooglePhotorealistic3DTileset` no longer shows credits on screen but links to them instead, as this is compliant with the minimum required attribution. To restore this behavior, pass the option `showCreditsOnScreen: true`. [#11589](https://github.com/CesiumGS/cesium/pull/11589) - Fixed an issue with polygon hole rendering. [#11583](https://github.com/CesiumGS/cesium/issues/11583) - Fixed error with rhumb lines that have a 0 degree heading. [#11573](https://github.com/CesiumGS/cesium/pull/11573) - Fixed `czm_normal`, `czm_normal3D`, `czm_inverseNormal`, and `czm_inverseNormal3D` for cases where the model matrix has non-uniform scale. [#11553](https://github.com/CesiumGS/cesium/pull/11553) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 20b960874675..6b4f70366e1b 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -160,6 +160,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu - [Daniel Krupka](https://github.com/krupkad) - [Jeshurun Hembd](https://github.com/jjhembd) - [Mia Tang](https://github.com/miatang13) + - [Mark Dane](https://github.com/angrycat9000) - [Northrop Grumman](http://www.northropgrumman.com) - [Joseph Stein](https://github.com/nahgrin) - [EOX IT Services GmbH](https://eox.at) @@ -184,6 +185,9 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu - [Robert Irving](https://github.com/robert-irving-snc) - [General Atomics CCRi](https://www.ga-ccri.com/) - [matthias-ccri](https://github.com/matthias-ccri) +- [Terradepth, Inc.](https://www.terradepth.com/) + - [Marc Johnson](https://github.com/marcejohnson) + - [Jacob Frazer](https://github.com/coderjake91) ## [Individual CLA](Documentation/Contributors/CLAs/individual-contributor-license-agreement-v1.0.pdf) diff --git a/Documentation/Contributors/BuildGuide/README.md b/Documentation/Contributors/BuildGuide/README.md index 3502e4899e3e..df4fa912da65 100644 --- a/Documentation/Contributors/BuildGuide/README.md +++ b/Documentation/Contributors/BuildGuide/README.md @@ -148,7 +148,6 @@ Here's the full set of scripts and what they do. - `test-webgl-validation` - Runs all tests with Karma and enables low-level WebGL validation - `test-release` - Runs all tests on the minified release version of built Cesium - **Deployment scripts** - - `deploy-s3` - Deploys the built CesiumJS files, the npm package, and the zip file to Amazon S3. This requires having credentials set up for the S3 bucket to which you are deploying - `deploy-status` - Sets the deployment statuses in GitHub, for use in CI - `deploy-set-version` - Sets the version of `package.json`, for use in CI @@ -179,7 +178,7 @@ aws s3 sync ./Build/Coverage s3://cesium-public-builds/cesium/$BRANCH/Build/Cove ``` ```yml -npm run deploy-s3 -- -b "cesium-public-builds" -d cesium/$BRANCH -c 'no-cache' --confirm +aws s3 sync Build/unzipped/ s3://cesium-public-builds/cesium/$BRANCH/Build/ --cache-control "no-cache" --delete ``` - In `gulpfile.js`, edit the following line: @@ -188,7 +187,7 @@ npm run deploy-s3 -- -b "cesium-public-builds" -d cesium/$BRANCH -c 'no-cache' - const devDeployUrl = "https://ci-builds.cesium.com/cesium/"; ``` -- Edit the URL to match the URL of the S3 bucket specified in the previous step. +- Edit the URL to match the URL hosting the S3 bucket specified in the previous step. ### Configure S3 Credentials diff --git a/Specs/Data/CesiumTerrainTileJson/11_3027_1342.terrain b/Specs/Data/CesiumTerrainTileJson/11_3027_1342.terrain new file mode 100644 index 000000000000..ca4dabcf67ff Binary files /dev/null and b/Specs/Data/CesiumTerrainTileJson/11_3027_1342.terrain differ diff --git a/Specs/test.cjs b/Specs/test.cjs index 11388433e704..c2d24da84ba8 100644 --- a/Specs/test.cjs +++ b/Specs/test.cjs @@ -5,15 +5,13 @@ const assert = require("node:assert"); const { - CesiumTerrainProvider, Cartographic, + createWorldTerrainAsync, sampleTerrain, } = require("cesium"); async function test() { - const provider = await CesiumTerrainProvider.fromUrl( - "https://s3.amazonaws.com/cesiumjs/smallTerrain" - ); + const provider = await createWorldTerrainAsync(); const results = await sampleTerrain(provider, 11, [ Cartographic.fromDegrees(86.925145, 27.988257), Cartographic.fromDegrees(87.0, 28.0), diff --git a/Specs/test.mjs b/Specs/test.mjs index 13ba4c1162ed..37bd1260575b 100644 --- a/Specs/test.mjs +++ b/Specs/test.mjs @@ -1,12 +1,10 @@ -import { Cartographic, CesiumTerrainProvider, sampleTerrain } from "cesium"; +import { Cartographic, createWorldTerrainAsync, sampleTerrain } from "cesium"; import assert from "node:assert"; // NodeJS smoke screen test async function test() { - const provider = await CesiumTerrainProvider.fromUrl( - "https://s3.amazonaws.com/cesiumjs/smallTerrain" - ); + const provider = await createWorldTerrainAsync(); const results = await sampleTerrain(provider, 11, [ Cartographic.fromDegrees(86.925145, 27.988257), Cartographic.fromDegrees(87.0, 28.0), diff --git a/ThirdParty.json b/ThirdParty.json index 21ee7959ef66..5e1b954747a5 100644 --- a/ThirdParty.json +++ b/ThirdParty.json @@ -133,7 +133,7 @@ "license": [ "MIT" ], - "version": "0.19.0", + "version": "0.20.0", "url": "https://www.npmjs.com/package/meshoptimizer" }, { diff --git a/gulpfile.js b/gulpfile.js index 83c0355a2706..0921a05ba711 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,11 +1,8 @@ /*eslint-env node*/ import { writeFileSync, copyFileSync, readFileSync, existsSync } from "fs"; import { readFile, writeFile } from "fs/promises"; -import { join, basename, resolve, posix, dirname } from "path"; +import { join, basename, resolve, dirname } from "path"; import { exec, execSync } from "child_process"; -import { createHash } from "crypto"; -import { gzipSync } from "zlib"; -import { createInterface } from "readline"; import fetch from "node-fetch"; import { createRequire } from "module"; @@ -22,21 +19,9 @@ import mergeStream from "merge-stream"; import streamToPromise from "stream-to-promise"; import karma from "karma"; import yargs from "yargs"; -import { - S3Client, - DeleteObjectsCommand, - HeadObjectCommand, - ListObjectsCommand, - PutObjectCommand, -} from "@aws-sdk/client-s3"; -import { Upload } from "@aws-sdk/lib-storage"; -import mime from "mime"; import typeScript from "typescript"; import { build as esbuild } from "esbuild"; import { createInstrumenter } from "istanbul-lib-instrument"; -import pLimit from "p-limit"; -import download from "download"; -import decompress from "decompress"; import { buildCesium, @@ -525,7 +510,6 @@ async function pruneScriptsForZip(packageJsonPath) { delete scripts.prettier; // Remove deploy tasks - delete scripts["deploy-s3"]; delete scripts["deploy-status"]; delete scripts["deploy-set-version"]; delete scripts["website-release"]; @@ -697,529 +681,6 @@ export const makeZip = gulp.series(release, async function () { ); }); -export async function deployS3() { - const argv = yargs(process.argv) - .usage("Usage: deploy-s3 -b [Bucket Name] -d [Upload Directory]") - .options({ - bucket: { - alias: "b", - description: "Bucket name.", - type: "string", - demandOption: true, - }, - directory: { - alias: "d", - description: "Upload directory.", - type: "string", - }, - "cache-control": { - alias: "c", - description: - "The cache control option set on the objects uploaded to S3.", - type: "string", - default: "max-age=3600", - }, - "dry-run": { - description: "Only print file paths and S3 keys.", - type: "boolean", - default: false, - }, - skip: { - description: "Skip re-uploading exisiting files.", - type: "boolean", - default: false, - }, - confirm: { - description: "Skip confirmation step, useful for CI.", - type: "boolean", - default: false, - }, - }).argv; - - const uploadDirectory = argv.directory; - const bucketName = argv.bucket; - const dryRun = argv.dryRun; - const cacheControl = argv.cacheControl ? argv.cacheControl : "max-age=3600"; - const skipFiles = argv.skip; - - if (argv.confirm) { - return deployCesium( - bucketName, - uploadDirectory, - cacheControl, - skipFiles, - dryRun - ); - } - - const iface = createInterface({ - input: process.stdin, - output: process.stdout, - }); - - return new Promise((resolve) => { - // prompt for confirmation - iface.question( - `Files from your computer will be published to the ${bucketName} bucket. Continue? [y/n] `, - function (answer) { - iface.close(); - if (answer === "y") { - resolve( - deployCesium( - bucketName, - uploadDirectory, - cacheControl, - skipFiles, - dryRun - ) - ); - } else { - console.log("Deploy aborted by user."); - resolve(); - } - } - ); - }); -} - -// Deploy cesium to s3 -async function deployCesium( - bucketName, - uploadDirectory, - cacheControl, - skipFiles, - dryRun -) { - // Limit promise concurrency since we are reading many - // files off disk in parallel - const limit = pLimit(2000); - - const refDocPrefix = "cesiumjs/ref-doc/"; - const sandcastlePrefix = "sandcastle/"; - const cesiumViewerPrefix = "cesiumjs/cesium-viewer/"; - - const s3Client = new S3Client({ - region: "us-east-1", - maxRetries: 10, - retryDelayOptions: { - base: 500, - }, - }); - - const existingBlobs = []; - let totalFiles = 0; - let uploaded = 0; - let skipped = 0; - const errors = []; - - if (!isProduction) { - await listAll(s3Client, bucketName, `${uploadDirectory}/`, existingBlobs); - } - - async function getContents(file, blobName) { - const mimeLookup = getMimeType(blobName); - const contentType = mimeLookup.type; - const compress = mimeLookup.compress; - const contentEncoding = compress ? "gzip" : undefined; - - totalFiles++; - - let content = await readFile(file); - - if (compress) { - const alreadyCompressed = content[0] === 0x1f && content[1] === 0x8b; - if (alreadyCompressed) { - if (verbose) { - console.log(`Skipping compressing already compressed file: ${file}`); - } - } else { - content = gzipSync(content); - } - } - - const computeEtag = (content) => { - return createHash("md5").update(content).digest("base64"); - }; - - const index = existingBlobs.indexOf(blobName); - if (index <= -1) { - return { - content, - etag: computeEtag(content), - contentType, - contentEncoding, - }; - } - - // remove files from the list to clean later - // as we find them on disk - existingBlobs.splice(index, 1); - - // get file info - const headObjectCommand = new HeadObjectCommand({ - Bucket: bucketName, - Key: blobName, - }); - const data = await s3Client.send(headObjectCommand); - const hash = createHash("md5").update(content).digest("hex"); - - if ( - !skipFiles || - data.ETag !== `"${hash}"` || - data.CacheControl !== cacheControl || - data.ContentType !== contentType || - data.ContentEncoding !== contentEncoding - ) { - return { - content, - etag: computeEtag(content), - contentType, - contentEncoding, - }; - } - - // We don't need to upload this file again - skipped++; - } - - async function readAndUpload(prefix, existingPrefix, file) { - const blobName = `${prefix}${file.replace(existingPrefix, "")}`; - - let fileContents; - try { - fileContents = await getContents(file, blobName); - } catch (e) { - errors.push(e); - } - - if (!fileContents) { - return; - } - - const content = fileContents.content; - const etag = fileContents.etag; - const contentType = fileContents.contentType; - const contentEncoding = fileContents.contentEncoding; - - if (verbose) { - console.log(`Uploading ${blobName}...`); - } - - const params = { - Bucket: bucketName, - Key: blobName, - Body: content, - ContentMD5: etag, - ContentType: contentType, - ContentEncoding: contentEncoding, - CacheControl: cacheControl, - }; - - const putObjectCommand = new PutObjectCommand(params); - - if (dryRun) { - uploaded++; - return; - } - - try { - await s3Client.send(putObjectCommand); - uploaded++; - } catch (e) { - errors.push(e); - } - } - - let uploads; - if (isProduction) { - const uploadSandcastle = async () => { - const files = await globby(["Build/Sandcastle/**"]); - return Promise.all( - files.map((file) => { - return limit(() => - readAndUpload(sandcastlePrefix, "Build/Sandcastle/", file) - ); - }) - ); - }; - - const uploadRefDoc = async () => { - const files = await globby(["Build/Documentation/**"]); - return Promise.all( - files.map((file) => { - return limit(() => - readAndUpload(refDocPrefix, "Build/Documentation/", file) - ); - }) - ); - }; - - const uploadCesiumViewer = async () => { - const files = await globby(["Build/CesiumViewer/**"]); - return Promise.all( - files.map((file) => { - return limit(() => - readAndUpload(cesiumViewerPrefix, "Build/CesiumViewer/", file) - ); - }) - ); - }; - - uploads = [ - uploadSandcastle(), - uploadRefDoc(), - uploadCesiumViewer(), - deployCesiumRelease(bucketName, s3Client, errors), - ]; - } else { - const files = await globby( - [ - "Apps/**", - "Build/**", - "!Build/CesiumDev/**", - "packages/**", - "Source/**", - "Specs/**", - "ThirdParty/**", - "*.md", - "favicon.ico", - "gulpfile.js", - "index.html", - "package.json", - "server.js", - "web.config", - "*.zip", - "*.tgz", - ], - { - dot: true, // include hidden files - } - ); - - uploads = files.map((file) => { - return limit(() => readAndUpload(`${uploadDirectory}/`, "", file)); - }); - } - - await Promise.all(uploads); - - console.log( - `Skipped ${skipped} files and successfully uploaded ${uploaded} files of ${ - totalFiles - skipped - } files.` - ); - - if (!isProduction && existingBlobs.length >= 0) { - const objectsToDelete = []; - existingBlobs.forEach(function (file) { - // Don't delete generated zip files - if (!/\.(zip|tgz)$/.test(file)) { - objectsToDelete.push({ Key: file }); - } - }); - - if (objectsToDelete.length > 0) { - console.log(`Cleaning ${objectsToDelete.length} files...`); - - // If more than 1000 files, we must issue multiple requests - const batches = []; - while (objectsToDelete.length > 1000) { - batches.push(objectsToDelete.splice(0, 1000)); - } - batches.push(objectsToDelete); - - const deleteObjects = async (objects) => { - const deleteObjectsCommand = new DeleteObjectsCommand({ - Bucket: bucketName, - Delete: { - Objects: objects, - }, - }); - - try { - if (!dryRun) { - await s3Client.send(deleteObjectsCommand); - } - } catch (e) { - errors.push(e); - } - - if (verbose) { - console.log(`Cleaned ${objects.length} files.`); - } - }; - - await Promise.all(batches.map(deleteObjects)); - } - } - - if (errors.length === 0) { - return; - } - - console.log("Errors: "); - errors.map(console.log); - return Promise.reject("There was an error while deploying Cesium"); -} - -async function deployCesiumRelease(bucketName, s3Client, errors) { - const releaseDir = "cesiumjs/releases"; - const quiet = process.env.CI; - - let release; - try { - // Deploy any new releases - const response = await fetch( - "https://api.github.com/repos/CesiumGS/cesium/releases/latest", - { - method: "GET", - headers: { - Authorization: process.env.GITHUB_TOKEN - ? `token ${process.env.GITHUB_TOKEN}` - : undefined, - "User-Agent": "cesium.com-build", - }, - } - ); - - const body = await response.json(); - - release = { - tag: body.tag_name, - name: body.name, - url: body.assets[0].browser_download_url, - }; - - const headObjectCommand = new HeadObjectCommand({ - Bucket: bucketName, - Key: posix.join(releaseDir, release.tag, "cesium.zip"), - }); - await s3Client.send(headObjectCommand); - console.log( - `Cesium version ${release.tag} up to date. Skipping release deployment.` - ); - } catch (error) { - if (error.$metadata) { - const { httpStatusCode } = error.$metadata; - // The current version is not uploaded - if (httpStatusCode === 404) { - console.log("Updating cesium version..."); - const data = await download(release.url); - // upload and unzip contents - const key = posix.join(releaseDir, release.tag, "cesium.zip"); - await uploadObject(bucketName, s3Client, key, data, quiet); - const files = await decompress(data); - const limit = pLimit(5); - return Promise.all( - files.map((file) => { - return limit(async () => { - if (file.path.startsWith("Apps")) { - // skip uploading apps and sandcastle - return; - } - - // Upload to release directory - const key = posix.join(releaseDir, release.tag, file.path); - return uploadObject(bucketName, s3Client, key, file.data, quiet); - }); - }) - ); - } - } - - // else, unexpected error - errors.push(error); - } -} - -async function uploadObject(bucketName, s3Client, key, contents, quiet) { - if (!quiet) { - console.log(`Uploading ${key}...`); - } - - const upload = new Upload({ - client: s3Client, - params: { - Bucket: bucketName, - Key: key, - Body: contents, - ContentType: mime.getType(key) || undefined, - CacheControl: "public, max-age=1800", - }, - }); - return upload.done(); -} - -function getMimeType(filename) { - //Remove page anchors from documentation, as mime does not properly handle them - filename = filename.split("#")[0]; - - const mimeType = mime.getType(filename); - if (mimeType) { - //Compress everything except zipfiles, binary images, and video - let compress = !/^(image\/|video\/|application\/zip|application\/gzip)/i.test( - mimeType - ); - if (mimeType === "image/svg+xml") { - compress = true; - } - return { type: mimeType, compress: compress }; - } - - //Non-standard mime types not handled by mime - if (/\.(glsl|LICENSE|config|state)$/i.test(filename)) { - return { type: "text/plain", compress: true }; - } else if (/\.(czml|topojson)$/i.test(filename)) { - return { type: "application/json", compress: true }; - } else if (/\.tgz$/i.test(filename)) { - return { type: "application/octet-stream", compress: false }; - } - - // Handle dotfiles, such as .jshintrc - const baseName = basename(filename); - if (baseName[0] === "." || baseName.indexOf(".") === -1) { - return { type: "text/plain", compress: true }; - } - - // Everything else can be octet-stream compressed but print a warning - // if we introduce a type we aren't specifically handling. - if (!/\.(terrain|b3dm|geom|pnts|vctr|cmpt|i3dm|metadata)$/i.test(filename)) { - console.log(`Unknown mime type for ${filename}`); - } - - return { type: "application/octet-stream", compress: true }; -} - -// get all files currently in bucket asynchronously -async function listAll(s3Client, bucketName, prefix, files, marker) { - const listObjectsCommand = new ListObjectsCommand({ - Bucket: bucketName, - MaxKeys: 1000, - Prefix: prefix, - Marker: marker, - }); - const data = await s3Client.send(listObjectsCommand); - const items = data.Contents; - if (!items) { - return; - } - - for (let i = 0; i < items.length; i++) { - files.push(items[i].Key); - } - - if (data.IsTruncated) { - // get next page of results - return listAll( - s3Client, - bucketName, - prefix, - files, - files[files.length - 1] - ); - } -} - export async function deploySetVersion() { const buildVersion = argv.buildVersion; if (buildVersion) { @@ -1233,7 +694,7 @@ export async function deployStatus() { const status = argv.status; const message = argv.message; - const deployUrl = `${devDeployUrl + process.env.BRANCH}/index.html`; + const deployUrl = `${devDeployUrl + process.env.BRANCH}/`; const zipUrl = `${deployUrl}Cesium-${version}.zip`; const npmUrl = `${deployUrl}cesium-${version}.tgz`; const coverageUrl = `${ diff --git a/package.json b/package.json index bcf1bdf590c2..fd9639e2e426 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cesium", - "version": "1.111.0", + "version": "1.112.0", "description": "CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.", "homepage": "http://cesium.com/cesiumjs/", "license": "Apache-2.0", @@ -50,26 +50,22 @@ "./Specs/**/*" ], "dependencies": { - "@cesium/engine": "^6.1.0", - "@cesium/widgets": "^4.3.0" + "@cesium/engine": "^6.1.1", + "@cesium/widgets": "^4.3.1" }, "devDependencies": { - "@aws-sdk/client-s3": "^3.438.0", - "@aws-sdk/lib-storage": "^3.438.0", - "@playwright/test": "^1.34.3", + "@playwright/test": "^1.40.1", "chokidar": "^3.5.3", "cloc": "^2.8.0", "compression": "^1.7.4", - "decompress": "^4.2.1", - "download": "^8.0.0", - "esbuild": "^0.19.2", - "eslint": "^8.41.0", + "esbuild": "^0.19.8", + "eslint": "^8.54.0", "eslint-config-cesium": "^10.0.1", "eslint-plugin-es": "^4.1.0", "eslint-plugin-html": "^7.1.0", "eslint-plugin-node": "^11.1.0", "express": "^4.17.1", - "globby": "^13.1.3", + "globby": "^14.0.0", "glsl-strip-comments": "^1.0.0", "gulp": "^4.0.2", "gulp-clean-css": "^4.3.0", @@ -77,7 +73,7 @@ "gulp-rename": "^2.0.0", "gulp-replace": "^1.1.3", "gulp-tap": "^2.0.0", - "gulp-zip": "^5.1.0", + "gulp-zip": "^6.0.0", "husky": "^8.0.2", "istanbul-lib-instrument": "^6.0.0", "jasmine-core": "^5.0.1", @@ -96,11 +92,9 @@ "karma-spec-reporter": "^0.0.36", "markdownlint-cli": "^0.37.0", "merge-stream": "^2.0.0", - "mime": "^3.0.0", "mkdirp": "^3.0.1", "node-fetch": "^3.2.10", "open": "^9.1.0", - "p-limit": "^4.0.0", "prettier": "2.1.2", "prismjs": "^1.28.0", "request": "^2.79.0", @@ -108,7 +102,7 @@ "sinon": "^17.0.0", "stream-to-promise": "^3.0.0", "tsd-jsdoc": "^2.5.0", - "typescript": "^5.0.2", + "typescript": "^5.3.2", "yargs": "^17.0.1" }, "scripts": { @@ -144,7 +138,6 @@ "test-e2e-release-all": "release=true playwright test -c Specs/e2e/playwright.config.js", "test-e2e-report": "playwright show-report Build/Specs/e2e/report", "test-e2e-update": "playwright test -c Specs/e2e/playwright.config.js --project=chromium -u", - "deploy-s3": "gulp deployS3", "deploy-status": "gulp deployStatus", "deploy-set-version": "gulp deploySetVersion", "prettier": "prettier --write --no-config \"**/*\"", diff --git a/packages/engine/Source/Core/CesiumTerrainProvider.js b/packages/engine/Source/Core/CesiumTerrainProvider.js index 84d43629725a..c70769c02fcf 100644 --- a/packages/engine/Source/Core/CesiumTerrainProvider.js +++ b/packages/engine/Source/Core/CesiumTerrainProvider.js @@ -897,11 +897,18 @@ CesiumTerrainProvider.prototype.requestTileGeometry = function ( if (!defined(layerToUse) && unknownAvailability) { // Try again when availability data is ready– Otherwise the tile will be marked as failed and never re-requested - return availabilityPromise.then(() => - this.requestTileGeometry(x, y, level, request) - ); + return availabilityPromise.then(() => { + // handle promise or undefined return + return new Promise((resolve) => { + // defer execution to the next event loop + setTimeout(() => { + const promise = this.requestTileGeometry(x, y, level, request); + resolve(promise); + }, 0); // next tick + }); + }); } - + // call overridden function below return requestTileGeometry(this, x, y, level, layerToUse, request); }; diff --git a/packages/engine/Source/Core/Ion.js b/packages/engine/Source/Core/Ion.js index ecc601a4f4ea..7028ddfc6e59 100644 --- a/packages/engine/Source/Core/Ion.js +++ b/packages/engine/Source/Core/Ion.js @@ -8,7 +8,7 @@ let defaultTokenCredit; const cesiumWebsiteToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI3ODZkMDQzOS03ZGJjLTQzZWUtYjlmYy04ZmM5Y2UwNzNhMmYiLCJpZCI6MjU5LCJpYXQiOjE2MzgyMDYwMDB9.cK1hsaFBgz0l2dG9Ry5vBFHWp-HF2lwjLC0tcK8Z8tY"; const defaultAccessToken = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIyMzVjZDg3Yi0wMjVkLTQ0MzctOTZjOC1jZjMwNjkzZmI5MWUiLCJpZCI6MjU5LCJpYXQiOjE2OTg4NTM0MDl9.L_g01CjNKNVijEJ_8kOxiO02OADnuSjRFLkZWvhupcc"; + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzN2UwMjViMC0xOTJhLTQxMmItOWEzMS1kMjJiNmZiY2E5N2YiLCJpZCI6MjU5LCJpYXQiOjE3MDE0NTc1NjB9.bMNhc7S4cuG1qUnRUiLxr5lunSH1k6-UfkRXSqVFYyM"; /** * Default settings for accessing the Cesium ion API. * diff --git a/packages/engine/Source/Scene/ArcGisMapService.js b/packages/engine/Source/Scene/ArcGisMapService.js index 205ddf815af3..721af18c359e 100644 --- a/packages/engine/Source/Scene/ArcGisMapService.js +++ b/packages/engine/Source/Scene/ArcGisMapService.js @@ -8,7 +8,7 @@ const cesiumWebsiteToken = let defaultTokenCredit; const defaultAccessToken = - "AAPK72121c3f05244af285ad75999db084bfDGkWA0QECX6QWOdaQmarXC21i1ogkvj8XCctaZExrW8Hw1AT58yG7twH3VFzZl13"; + "AAPK2b93071721df4cc78be0d8b3d79b1fd54YMocOcx2NxlbYTDkyO5gPk8XsDnguQgeMdFKepFwLwTgb8vHfPvSTdjy_KlMHlS"; /** * Default options for accessing the ArcGIS image tile service. * diff --git a/packages/engine/Source/Scene/Cesium3DTileset.js b/packages/engine/Source/Scene/Cesium3DTileset.js index c9964489ca10..0f5f3b56bcb9 100644 --- a/packages/engine/Source/Scene/Cesium3DTileset.js +++ b/packages/engine/Source/Scene/Cesium3DTileset.js @@ -1902,7 +1902,7 @@ Object.defineProperties(Cesium3DTileset.prototype, { * used for streaming massive heterogeneous 3D geospatial datasets, from a Cesium ion asset ID. * * @param {number} assetId The Cesium ion asset id. - * @param {Cesium3DTileset.ConstructorOptions} options An object describing initialization options + * @param {Cesium3DTileset.ConstructorOptions} [options] An object describing initialization options * @returns {Promise} * * @exception {DeveloperError} The tileset must be 3D Tiles version 0.0 or 1.0. diff --git a/packages/engine/Source/Scene/IonImageryProvider.js b/packages/engine/Source/Scene/IonImageryProvider.js index 17ef1d580094..5b1361bb734e 100644 --- a/packages/engine/Source/Scene/IonImageryProvider.js +++ b/packages/engine/Source/Scene/IonImageryProvider.js @@ -251,7 +251,7 @@ Object.defineProperties(IonImageryProvider.prototype, { * Creates a provider for tiled imagery using the Cesium ion REST API. * * @param {Number} assetId An ion imagery asset ID. - * @param {IonImageryProvider.ConstructorOptions} options Object describing initialization options. + * @param {IonImageryProvider.ConstructorOptions} [options] Object describing initialization options. * @returns {Promise} A promise which resolves to the created IonImageryProvider. * * @example diff --git a/packages/engine/Specs/Core/sampleTerrainMostDetailedSpec.js b/packages/engine/Specs/Core/sampleTerrainMostDetailedSpec.js index 3a55df6250dc..77635449b0d9 100644 --- a/packages/engine/Specs/Core/sampleTerrainMostDetailedSpec.js +++ b/packages/engine/Specs/Core/sampleTerrainMostDetailedSpec.js @@ -28,9 +28,9 @@ describe("Core/sampleTerrainMostDetailed", function () { expect(positions[1].height).toBeLessThan(10000); }); - it("should throw querying heights from Small Terrain", async function () { + it("should throw querying heights from terrain without availability", async function () { const terrainProvider = await CesiumTerrainProvider.fromUrl( - "https://s3.amazonaws.com/cesiumjs/smallTerrain" + "Data/CesiumTerrainTileJson/StandardHeightmap.tile.json" ); const positions = [ diff --git a/packages/engine/Specs/Core/sampleTerrainSpec.js b/packages/engine/Specs/Core/sampleTerrainSpec.js index 3369481f068d..c88c1a5ddff3 100644 --- a/packages/engine/Specs/Core/sampleTerrainSpec.js +++ b/packages/engine/Specs/Core/sampleTerrainSpec.js @@ -15,6 +15,11 @@ describe("Core/sampleTerrain", function () { worldTerrain = await createWorldTerrainAsync(); }); + afterEach(function () { + Resource._Implementations.loadWithXhr = + Resource._DefaultImplementations.loadWithXhr; + }); + it("queries heights", function () { const positions = [ Cartographic.fromDegrees(86.925145, 27.988257), @@ -32,9 +37,42 @@ describe("Core/sampleTerrain", function () { }); }); - it("queries heights from Small Terrain", async function () { + it("queries heights from terrain without availability", async function () { + // Mock terrain tile loading + Resource._Implementations.loadWithXhr = function ( + url, + responseType, + method, + data, + headers, + deferred, + overrideMimeType + ) { + if (defined(url.match(/\/\d+\/\d+\/\d+\.terrain/))) { + Resource._DefaultImplementations.loadWithXhr( + "Data/CesiumTerrainTileJson/11_3027_1342.terrain", + responseType, + method, + data, + headers, + deferred + ); + return; + } + + Resource._DefaultImplementations.loadWithXhr( + url, + responseType, + method, + data, + headers, + deferred, + overrideMimeType + ); + }; + const terrainProvider = await CesiumTerrainProvider.fromUrl( - "https://s3.amazonaws.com/cesiumjs/smallTerrain" + "Data/CesiumTerrainTileJson/StandardHeightmap.tile.json" ); const positions = [ diff --git a/packages/engine/Specs/Scene/GlobeSurfaceTileProviderSpec.js b/packages/engine/Specs/Scene/GlobeSurfaceTileProviderSpec.js index 129bb57b8cf0..9818f2534b2b 100644 --- a/packages/engine/Specs/Scene/GlobeSurfaceTileProviderSpec.js +++ b/packages/engine/Specs/Scene/GlobeSurfaceTileProviderSpec.js @@ -12,6 +12,7 @@ import { GeographicProjection, HeadingPitchRoll, Rectangle, + Resource, WebMercatorProjection, ContextLimits, RenderState, @@ -104,6 +105,8 @@ describe( afterEach(function () { scene.imageryLayers.removeAll(); scene.primitives.removeAll(); + Resource._Implementations.loadWithXhr = + Resource._DefaultImplementations.loadWithXhr; }); it("conforms to QuadtreeTileProvider interface", function () { @@ -902,8 +905,41 @@ describe( scene.imageryLayers.addImageryProvider(provider); const terrainCredit = new Credit("terrain credit"); + + // Mock terrain tile loading + Resource._Implementations.loadWithXhr = function ( + url, + responseType, + method, + data, + headers, + deferred, + overrideMimeType + ) { + if (defined(url.match(/\/\d+\/\d+\/\d+\.terrain/))) { + Resource._DefaultImplementations.loadWithXhr( + "Data/CesiumTerrainTileJson/tile.32bitIndices.terrain", + responseType, + method, + data, + headers, + deferred + ); + return; + } + + Resource._DefaultImplementations.loadWithXhr( + url, + responseType, + method, + data, + headers, + deferred, + overrideMimeType + ); + }; scene.terrainProvider = await CesiumTerrainProvider.fromUrl( - "https://s3.amazonaws.com/cesiumjs/smallTerrain", + "Data/CesiumTerrainTileJson/QuantizedMesh.tile.json", { credit: terrainCredit, } diff --git a/packages/engine/Specs/Scene/OctahedralProjectedCubeMapSpec.js b/packages/engine/Specs/Scene/OctahedralProjectedCubeMapSpec.js index 4d888da1355d..2d13eba58fb1 100644 --- a/packages/engine/Specs/Scene/OctahedralProjectedCubeMapSpec.js +++ b/packages/engine/Specs/Scene/OctahedralProjectedCubeMapSpec.js @@ -208,7 +208,7 @@ describe( }); }); - it("raises error event when environment map fails to load.", function () { + it("raises error event when environment map fails to load.", async function () { if (!OctahedralProjectedCubeMap.isSupported(context)) { return; } @@ -217,17 +217,22 @@ describe( const frameState = createFrameState(context); let error; - const removeListener = projection.errorEvent.addEventListener((e) => { - error = e; - expect(error).toBeDefined(); - expect(projection.ready).toEqual(false); - removeListener(); + const promise = new Promise((resolve, reject) => { + const removeListener = projection.errorEvent.addEventListener((e) => { + error = e; + expect(error).toBeDefined(); + expect(projection.ready).toEqual(false); + removeListener(); + resolve(); + }); }); - return pollToPromise(function () { + await pollToPromise(function () { projection.update(frameState); return defined(error); }); + + return promise; }); }, "WebGL" diff --git a/packages/engine/Specs/test.mjs b/packages/engine/Specs/test.mjs index 23f6bbf07cc3..1479b34b2dd2 100644 --- a/packages/engine/Specs/test.mjs +++ b/packages/engine/Specs/test.mjs @@ -1,9 +1,9 @@ -import { Cartographic, CesiumTerrainProvider, sampleTerrain } from "@cesium/engine"; +import { Cartographic, createWorldTerrainAsync, sampleTerrain } from "@cesium/engine"; import assert from "node:assert"; // NodeJS smoke screen test async function test() { - const provider = await CesiumTerrainProvider.fromUrl("https://s3.amazonaws.com/cesiumjs/smallTerrain"); + const provider = await createWorldTerrainAsync(); const results = await sampleTerrain(provider, 11, [ Cartographic.fromDegrees(86.925145, 27.988257), Cartographic.fromDegrees(87.0, 28.0), diff --git a/packages/engine/package.json b/packages/engine/package.json index 81ddf1a2a54b..ee1c7b7a0681 100644 --- a/packages/engine/package.json +++ b/packages/engine/package.json @@ -1,6 +1,6 @@ { "name": "@cesium/engine", - "version": "6.1.0", + "version": "6.1.1", "description": "CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.", "keywords": [ "3D", @@ -45,7 +45,7 @@ "ktx-parse": "^0.6.0", "lerc": "^2.0.0", "mersenne-twister": "^1.1.0", - "meshoptimizer": "^0.19.0", + "meshoptimizer": "^0.20.0", "pako": "^2.0.4", "protobufjs": "^7.1.0", "rbush": "^3.0.1", diff --git a/packages/widgets/package.json b/packages/widgets/package.json index 0696b3475d11..5b121d5c1689 100644 --- a/packages/widgets/package.json +++ b/packages/widgets/package.json @@ -1,6 +1,6 @@ { "name": "@cesium/widgets", - "version": "4.3.0", + "version": "4.3.1", "description": "A widgets library for use with CesiumJS. CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.", "keywords": [ "3D", @@ -28,7 +28,7 @@ "node": ">=14.0.0" }, "dependencies": { - "@cesium/engine": "^6.1.0", + "@cesium/engine": "^6.1.1", "nosleep.js": "^0.12.0" }, "type": "module",