Repository: InsightSoftwareConsortium/ITK-Wasm (language: python)

Issue:
Transfer target_include_directories to emscripten umd target



Discussion / hints:

Fix patch:
diff --git a/package-lock.json b/package-lock.json
index 19b2c5845..a637d674a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,7 @@
       "license": "Apache-2.0",
       "dependencies": {
         "@babel/runtime": "^7.15.4",
+        "@thewtex/zstddec": "^0.1.2",
         "@types/emscripten": "^1.39.6",
         "axios": "^1.4.0",
         "commander": "^9.4.0",
@@ -3186,6 +3187,11 @@
       "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
       "dev": true
     },
+    "node_modules/@thewtex/zstddec": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/@thewtex/zstddec/-/zstddec-0.1.2.tgz",
+      "integrity": "sha512-Bv50pouFqlmIZDcAA2Nrpk9tjJpAPlqHHeD5h0noK+oNXMimrZ/hMbJK2N09Svr6TI/S6nT63dzkWoim4ZzTuw=="
+    },
     "node_modules/@tootallnate/once": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
@@ -5268,9 +5274,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001388",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001388.tgz",
-      "integrity": "sha512-znVbq4OUjqgLxMxoNX2ZeeLR0d7lcDiE5uJ4eUiWdml1J1EkxbnQq6opT9jb9SMfJxB0XA16/ziHwni4u1I3GQ==",
+      "version": "1.0.30001527",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001527.tgz",
+      "integrity": "sha512-YkJi7RwPgWtXVSgK4lG9AHH57nSzvvOp9MesgXmw4Q7n0C3H04L0foHqfxcmSAm5AcWb8dW9AYj2tR7/5GnddQ==",
       "dev": true,
       "funding": [
         {
@@ -5280,6 +5286,10 @@
         {
           "type": "tidelift",
           "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
         }
       ]
     },
@@ -23013,6 +23023,11 @@
       "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
       "dev": true
     },
+    "@thewtex/zstddec": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/@thewtex/zstddec/-/zstddec-0.1.2.tgz",
+      "integrity": "sha512-Bv50pouFqlmIZDcAA2Nrpk9tjJpAPlqHHeD5h0noK+oNXMimrZ/hMbJK2N09Svr6TI/S6nT63dzkWoim4ZzTuw=="
+    },
     "@tootallnate/once": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
@@ -24592,9 +24607,9 @@
       }
     },
     "caniuse-lite": {
-      "version": "1.0.30001388",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001388.tgz",
-      "integrity": "sha512-znVbq4OUjqgLxMxoNX2ZeeLR0d7lcDiE5uJ4eUiWdml1J1EkxbnQq6opT9jb9SMfJxB0XA16/ziHwni4u1I3GQ==",
+      "version": "1.0.30001527",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001527.tgz",
+      "integrity": "sha512-YkJi7RwPgWtXVSgK4lG9AHH57nSzvvOp9MesgXmw4Q7n0C3H04L0foHqfxcmSAm5AcWb8dW9AYj2tR7/5GnddQ==",
       "dev": true
     },
     "cardinal": {
diff --git a/package.json b/package.json
index 187a8e24e..ee144de03 100644
--- a/package.json
+++ b/package.json
@@ -167,7 +167,8 @@
     "markdown-table": "^3.0.3",
     "mime-types": "^2.1.35",
     "wasm-feature-detect": "^1.5.1",
-    "webworker-promise": "^0.4.2"
+    "webworker-promise": "^0.4.2",
+    "@thewtex/zstddec": "^0.1.2"
   },
   "bin": {
     "itk-wasm": "./src/itk-wasm-cli.js"
diff --git a/src/bindgen/typescript/typescript-bindings.js b/src/bindgen/typescript/typescript-bindings.js
index a0dee3057..d6af43b24 100644
--- a/src/bindgen/typescript/typescript-bindings.js
+++ b/src/bindgen/typescript/typescript-bindings.js
@@ -87,6 +87,7 @@ function typescriptBindings (outputDir, buildDir, wasmBinaries, options, forNode
       if (err.code !== 'EEXIST') throw err
     }
     fs.copyFileSync(wasmBinaryRelativePath, path.join(distPipelinesDir, path.basename(wasmBinaryRelativePath)))
+    fs.copyFileSync(`${wasmBinaryRelativePath}.zst`, path.join(distPipelinesDir, `${path.basename(wasmBinaryRelativePath)}.zst`))
     const prefix = wasmBinaryRelativePath.substring(0, wasmBinaryRelativePath.length-5)
     fs.copyFileSync(`${prefix}.js`, path.join(distPipelinesDir, `${path.basename(prefix)}.js`))
     fs.copyFileSync(`${prefix}.umd.js`, path.join(distPipelinesDir, `${path.basename(prefix)}.umd.js`))
diff --git a/src/build-emscripten.js b/src/build-emscripten.js
index cb77ac33f..4da4921f4 100644
--- a/src/build-emscripten.js
+++ b/src/build-emscripten.js
@@ -140,7 +140,9 @@ if (options.copyBuildArtifacts) {
   }
   let imageIOFiles = glob.sync(path.join(buildDir, 'image-io', '*.js'))
   imageIOFiles = imageIOFiles.concat(glob.sync(path.join(buildDir, 'image-io', '*.wasm')))
+  imageIOFiles = imageIOFiles.concat(glob.sync(path.join(buildDir, 'image-io', '*.wasm.zst')))
   imageIOFiles = imageIOFiles.filter((fn) => !fn.endsWith('.umd.wasm'))
+  imageIOFiles = imageIOFiles.filter((fn) => !fn.endsWith('.umd.wasm.zst'))
   const copyImageIOModules = function (imageIOFile, callback) {
     const io = path.basename(imageIOFile)
     const output = path.join('dist', 'image-io', io)
@@ -154,7 +156,9 @@ if (options.copyBuildArtifacts) {
   }
   let meshIOFiles = glob.sync(path.join(buildDir, 'mesh-io', '*.js'))
   meshIOFiles = meshIOFiles.concat(glob.sync(path.join(buildDir, 'mesh-io', '*.wasm')))
+  meshIOFiles = meshIOFiles.concat(glob.sync(path.join(buildDir, 'mesh-io', '*.wasm.zst')))
   meshIOFiles = meshIOFiles.filter((fn) => !fn.endsWith('.umd.wasm'))
+  meshIOFiles = meshIOFiles.filter((fn) => !fn.endsWith('.umd.wasm.zst'))
   const copyMeshIOModules = function (meshIOFile, callback) {
     const io = path.basename(meshIOFile)
     const output = path.join('dist', 'mesh-io', io)
@@ -168,7 +172,9 @@ if (options.copyBuildArtifacts) {
   }
   let dicomFiles = glob.sync(path.join(buildDir, 'dicom', '*.js'))
   dicomFiles = dicomFiles.concat(glob.sync(path.join(buildDir, 'dicom', '*.wasm')))
+  dicomFiles = dicomFiles.concat(glob.sync(path.join(buildDir, 'dicom', '*.wasm.zst')))
   dicomFiles = dicomFiles.filter((fn) => !fn.endsWith('.umd.wasm'))
+  dicomFiles = dicomFiles.filter((fn) => !fn.endsWith('.umd.wasm.zst'))
   const copyDICOMModules = function (dicomFile, callback) {
     const io = path.basename(dicomFile)
     const output = path.join('dist', 'dicom', 'public', 'pipelines', io)
@@ -219,6 +225,7 @@ if (options.buildTestPipelines) {
     }
     let pipelineFiles = glob.sync(path.join(pipelinePath, 'emscripten-build', '*.js'))
     pipelineFiles = pipelineFiles.concat(glob.sync(path.join(pipelinePath, 'emscripten-build', '*.wasm')))
+    pipelineFiles = pipelineFiles.concat(glob.sync(path.join(pipelinePath, 'emscripten-build', '*.wasm.zst')))
     pipelineFiles.forEach((file) => {
       const filename = path.basename(file)
       const output = path.join('dist', 'pipelines', filename)
diff --git a/src/core/internal/loadEmscriptenModuleWebWorker.ts b/src/core/internal/loadEmscriptenModuleWebWorker.ts
index c195c4c6d..9793ab3b9 100644
--- a/src/core/internal/loadEmscriptenModuleWebWorker.ts
+++ b/src/core/internal/loadEmscriptenModuleWebWorker.ts
@@ -1,5 +1,9 @@
 import axios from 'axios'
 
+import { ZSTDDecoder } from '@thewtex/zstddec'
+const decoder = new ZSTDDecoder()
+let decoderInitialized = false
+
 import ITKWasmEmscriptenModule from '../ITKWasmEmscriptenModule.js'
 import camelCase from './camelCase.js'
 
@@ -26,8 +30,13 @@ async function loadEmscriptenModuleWebWorker(moduleRelativePathOrURL: string | U
   // adds worker dynamic import support:
   // https://bugzilla.mozilla.org/show_bug.cgi?id=1540913
   const wasmBinaryPath = `${modulePrefix}.wasm`
-  const response = await axios.get(wasmBinaryPath, { responseType: 'arraybuffer' })
-  const wasmBinary = response.data
+  const response = await axios.get(`${wasmBinaryPath}.zst`, { responseType: 'arraybuffer' })
+  if (!decoderInitialized) {
+    await decoder.init()
+    decoderInitialized = true
+  }
+  const decompressedArray = decoder.decode(new Uint8Array(response.data))
+  const wasmBinary = decompressedArray.buffer
   const modulePath = `${modulePrefix}.umd.js`
   importScripts(modulePath)
   const moduleBaseName: string = camelCase(modulePrefix.replace(/.*\//, ''))
diff --git a/src/docker/itk-wasm-base/Dockerfile b/src/docker/itk-wasm-base/Dockerfile
index c3a7e0491..d057e48d9 100644
--- a/src/docker/itk-wasm-base/Dockerfile
+++ b/src/docker/itk-wasm-base/Dockerfile
@@ -18,6 +18,24 @@ RUN curl -LO https://nodejs.org/dist/${NODE_TAG}/node-${NODE_TAG}-linux-x64.tar.
   ln -s /node-${NODE_TAG}-linux-x64/bin/npx /emsdk/upstream/emscripten
 ENV PATH /emsdk/upstream/emscripten:${PATH}
 
+ENV zstd_GIT_TAG v1.5.5
+RUN git clone https://github.com/facebook/zstd.git && \
+  cd zstd && \
+  git checkout ${zstd_GIT_TAG} && \
+  cd / && \
+  mkdir zstd-build && \
+  cd zstd-build && \
+  CC=cc CXX=c++ /usr/bin/cmake \
+    -G Ninja \
+    -DCMAKE_INSTALL_PREFIX:PATH=/usr \
+    -DZSTD_BUILD_SHARED:BOOL=OFF \
+    -DCMAKE_TOOLCHAIN_FILE="" \
+    ../zstd/build/cmake/ && \
+  ninja && \
+  ninja install && \
+  cd .. && \
+  rm -rf zstd-build zstd
+
 # tag commit date: 2023-06-23 branch: itkwasm-2023-01-15-f42c10a20f-4
 ENV ITK_GIT_TAG d6e6fd9535130ba677cb018c512ed72a466ee997
 RUN git clone https://github.com/KitwareMedical/ITK.git && \
@@ -102,16 +120,6 @@ RUN git clone https://github.com/InsightSoftwareConsortium/ITKMeshToPolyData.git
   find . -name '*.o' -delete && \
   cd .. && chmod -R 777 ITK-build
 
-ENV IPFS_TAG v0.10.0
-RUN curl -LO https://github.com/ipfs/go-ipfs/releases/download/${IPFS_TAG}/go-ipfs_${IPFS_TAG}_linux-amd64.tar.gz && \
-  tar xvzf go-ipfs_${IPFS_TAG}_linux-amd64.tar.gz && \
-  cp go-ipfs/ipfs /usr/local/bin/ && \
-  rm -rf go-ipfs
-ENV IPFS_PATH /home/ipfs/.ipfs
-RUN mkdir -p $IPFS_PATH && \
- ipfs init && \
- chmod -R 777 $IPFS_PATH
-
 # Build-time metadata as defined at http://label-schema.org
 ARG BUILD_DATE
 ARG IMAGE=itkwasm/base
diff --git a/src/docker/itk-wasm/ITKWebAssemblyInterface.cmake b/src/docker/itk-wasm/ITKWebAssemblyInterface.cmake
index 19658f366..632128e91 100644
--- a/src/docker/itk-wasm/ITKWebAssemblyInterface.cmake
+++ b/src/docker/itk-wasm/ITKWebAssemblyInterface.cmake
@@ -51,9 +51,20 @@ function(add_executable target)
     set_property(TARGET ${wasm_target} PROPERTY LINK_FLAGS "${common_link_flags} -s EXPORT_ES6=1 -s USE_ES6_IMPORT_META=1")
     set_property(TARGET ${umd_target} PROPERTY LINK_FLAGS "${common_link_flags}")
 
+    get_property(_include_dirs TARGET ${target} PROPERTY INCLUDE_DIRECTORIES)
+    set_property(TARGET ${umd_target} PROPERTY INCLUDE_DIRECTORIES "${_include_dirs}")
+
     get_property(_link_flags_debug TARGET ${target} PROPERTY LINK_FLAGS_DEBUG)
     set_property(TARGET ${wasm_target} PROPERTY LINK_FLAGS_DEBUG " -s EXPORT_EXCEPTION_HANDLING_HELPERS=1 -fno-lto -s SAFE_HEAP=1 -s DISABLE_EXCEPTION_CATCHING=0 -lembind ${_link_flags_debug}")
     set_property(TARGET ${umd_target} PROPERTY LINK_FLAGS_DEBUG " -s EXPORT_EXCEPTION_HANDLING_HELPERS=1 -fno-lto -s SAFE_HEAP=1 -s DISABLE_EXCEPTION_CATCHING=0 -lembind ${_link_flags_debug}")
+
+    get_property(_is_imported TARGET ${target} PROPERTY IMPORTED)
+    if (NOT ${_is_imported})
+      add_custom_command(TARGET ${target}
+        POST_BUILD
+        COMMAND /usr/bin/zstd -f "$<TARGET_FILE_DIR:${target}>/$<TARGET_FILE_BASE_NAME:${target}>.wasm" -o "$<TARGET_FILE_DIR:${target}>/$<TARGET_FILE_BASE_NAME:${target}>.wasm.zst"
+        )
+    endif()
   else()
     # WASI
     set_property(TARGET ${wasm_target} PROPERTY SUFFIX ".wasi.wasm")
diff --git a/src/io/extensionToImageIO.ts b/src/io/extensionToImageIO.ts
index 76eb22fa0..0b6de07b5 100644
--- a/src/io/extensionToImageIO.ts
+++ b/src/io/extensionToImageIO.ts
@@ -17,7 +17,7 @@ const extensionToIO = new Map([
 
   ['iwi', 'WasmImageIO'],
   ['iwi.cbor', 'WasmImageIO'],
-  ['iwi.cbor.zstd', 'WasmZstdImageIO'],
+  ['iwi.cbor.zst', 'WasmZstdImageIO'],
 
   ['lsm', 'LSMImageIO'],
 
diff --git a/src/io/extensionToMeshIO.ts b/src/io/extensionToMeshIO.ts
index 26aaf8d1c..b65f0e179 100644
--- a/src/io/extensionToMeshIO.ts
+++ b/src/io/extensionToMeshIO.ts
@@ -17,7 +17,7 @@ const extensionToIO = new Map([
   ['SWC', 'SWCMeshIO'],
   ['iwm', 'WasmMeshIO'],
   ['iwm.cbor', 'WasmMeshIO'],
-  ['iwm.cbor.zstd', 'WasmZstdMeshIO']
+  ['iwm.cbor.zst', 'WasmZstdMeshIO']
 ])
 
 export default extensionToIO
diff --git a/src/io/getFileExtension.ts b/src/io/getFileExtension.ts
index d3c333999..48fe9c081 100644
--- a/src/io/getFileExtension.ts
+++ b/src/io/getFileExtension.ts
@@ -6,7 +6,7 @@ function getFileExtension (filePath: string): string {
   } else if (extension.toLowerCase() === 'cbor') {
     const index = filePath.slice(0, -5).lastIndexOf('.')
     extension = filePath.slice((index - 1 >>> 0) + 2)
-  } else if (extension.toLowerCase() === 'zstd') {
+  } else if (extension.toLowerCase() === 'zst') {
     // .iwi.cbor.zstd
     const index = filePath.slice(0, -10).lastIndexOf('.')
     extension = filePath.slice((index - 1 >>> 0) + 2)
diff --git a/src/io/internal/pipelines/image/convert-image/itkWasmZstdImageIO.cxx b/src/io/internal/pipelines/image/convert-image/itkWasmZstdImageIO.cxx
index fe31f047b..39ffa3016 100644
--- a/src/io/internal/pipelines/image/convert-image/itkWasmZstdImageIO.cxx
+++ b/src/io/internal/pipelines/image/convert-image/itkWasmZstdImageIO.cxx
@@ -25,8 +25,8 @@ namespace itk
 WasmZstdImageIO
 ::WasmZstdImageIO()
 {
-  this->AddSupportedWriteExtension(".iwi.cbor.zstd");
-  this->AddSupportedReadExtension(".iwi.cbor.zstd");
+  this->AddSupportedWriteExtension(".iwi.cbor.zst");
+  this->AddSupportedReadExtension(".iwi.cbor.zst");
 }
 
 
@@ -68,9 +68,9 @@ ::ReadImageInformation()
 
   const std::string path = this->GetFileName();
 
-  std::string::size_type zstdPos = path.rfind(".zstd");
+  std::string::size_type zstdPos = path.rfind(".zst");
   if ( ( zstdPos != std::string::npos )
-       && ( zstdPos == path.length() - 5 ) )
+       && ( zstdPos == path.length() - 4 ) )
   {
     std::ifstream dataStream;
     this->OpenFileForReading( dataStream, this->GetFileName() );
@@ -98,9 +98,9 @@ WasmZstdImageIO
 ::Read( void *buffer )
 {
   const std::string path = this->GetFileName();
-  std::string::size_type zstdPos = path.rfind(".zstd");
+  std::string::size_type zstdPos = path.rfind(".zst");
   if ( ( zstdPos != std::string::npos )
-       && ( zstdPos == path.length() - 5 ) )
+       && ( zstdPos == path.length() - 4 ) )
   {
     std::ifstream dataStream;
     this->OpenFileForReading( dataStream, this->GetFileName() );
@@ -166,9 +166,9 @@ ::Write( const void *buffer )
 {
   const std::string path(this->GetFileName());
 
-  std::string::size_type cborPos = path.rfind(".zstd");
+  std::string::size_type cborPos = path.rfind(".zst");
   if ( ( cborPos != std::string::npos )
-       && ( cborPos == path.length() - 5 ) )
+       && ( cborPos == path.length() - 4 ) )
   {
     unsigned char * inputBinary;
     const size_t inputBinarySize = this->WriteCBOR(buffer, &inputBinary, true);
diff --git a/src/io/internal/pipelines/image/convert-image/itkWasmZstdImageIO.h b/src/io/internal/pipelines/image/convert-image/itkWasmZstdImageIO.h
index 26fbf9f90..0c7c7be12 100644
--- a/src/io/internal/pipelines/image/convert-image/itkWasmZstdImageIO.h
+++ b/src/io/internal/pipelines/image/convert-image/itkWasmZstdImageIO.h
@@ -33,7 +33,7 @@ namespace itk
  * 
  * This class extends WasmImageIO by adding support for zstandard compression.
  *
- * The file extensions used are .iwi, .iwi.cbor, and .iwi.cbor.zstd.
+ * The file extensions used are .iwi, .iwi.cbor, and .iwi.cbor.zst.
  * 
  * \ingroup IOFilters
  * \ingroup WebAssemblyInterface
diff --git a/src/io/internal/pipelines/mesh/convert-mesh/itkWasmZstdMeshIO.cxx b/src/io/internal/pipelines/mesh/convert-mesh/itkWasmZstdMeshIO.cxx
index fb9c0ca1d..32b25d5ff 100644
--- a/src/io/internal/pipelines/mesh/convert-mesh/itkWasmZstdMeshIO.cxx
+++ b/src/io/internal/pipelines/mesh/convert-mesh/itkWasmZstdMeshIO.cxx
@@ -25,8 +25,8 @@ namespace itk
 WasmZstdMeshIO
 ::WasmZstdMeshIO()
 {
-  this->AddSupportedWriteExtension(".iwm.cbor.zstd");
-  this->AddSupportedReadExtension(".iwm.cbor.zstd");
+  this->AddSupportedWriteExtension(".iwm.cbor.zst");
+  this->AddSupportedReadExtension(".iwm.cbor.zst");
 }
 
 
@@ -68,9 +68,9 @@ ::ReadMeshInformation()
 
   const std::string path = this->GetFileName();
 
-  std::string::size_type zstdPos = path.rfind(".zstd");
+  std::string::size_type zstdPos = path.rfind(".zst");
   if ( ( zstdPos != std::string::npos )
-       && ( zstdPos == path.length() - 5 ) )
+       && ( zstdPos == path.length() - 4 ) )
   {
     std::ifstream dataStream;
     this->OpenFileForReading( dataStream, this->GetFileName() );
@@ -127,9 +127,9 @@ ::Write()
 {
   const std::string path(this->GetFileName());
 
-  std::string::size_type cborPos = path.rfind(".zstd");
+  std::string::size_type cborPos = path.rfind(".zst");
   if ( ( cborPos != std::string::npos )
-       && ( cborPos == path.length() - 5 ) )
+       && ( cborPos == path.length() - 4 ) )
   {
     unsigned char* cborBuffer;
     size_t cborBufferSize;
diff --git a/src/io/internal/pipelines/mesh/convert-mesh/itkWasmZstdMeshIO.h b/src/io/internal/pipelines/mesh/convert-mesh/itkWasmZstdMeshIO.h
index 5a769fab7..eaae6a470 100644
--- a/src/io/internal/pipelines/mesh/convert-mesh/itkWasmZstdMeshIO.h
+++ b/src/io/internal/pipelines/mesh/convert-mesh/itkWasmZstdMeshIO.h
@@ -33,7 +33,7 @@ namespace itk
  * 
  * This class extends WasmMeshIO by adding support for zstandard compression.
  *
- * The file extensions used are .iwm, .iwm.cbor, and .iwm.cbor.zstd.
+ * The file extensions used are .iwm, .iwm.cbor, and .iwm.cbor.zst.
  * 
  * \ingroup IOFilters
  * \ingroup WebAssemblyInterface
diff --git a/src/itk-wasm-cli.js b/src/itk-wasm-cli.js
index 13000a3ae..8b250ad53 100755
--- a/src/itk-wasm-cli.js
+++ b/src/itk-wasm-cli.js
@@ -13,7 +13,7 @@ import pythonWebDemoBindgen from './bindgen/python-web-demo/python-web-demo-bind
 
 const program = new Command()
 
-const defaultImageTag = '20230827-82d0c2e9'
+const defaultImageTag = '20230906-2a85d1ac'
 
 function processCommonOptions(wasiDefault=false) {
   const options = program.opts()
diff --git a/src/itkWasmImageIO.cxx b/src/itkWasmImageIO.cxx
index cfaa0c6a0..0598ddb61 100644
--- a/src/itkWasmImageIO.cxx
+++ b/src/itkWasmImageIO.cxx
@@ -90,7 +90,7 @@ ::CanReadFile(const char *filename)
     return false;
     }
 
-  std::string::size_type zstdPos = fname.rfind(".zstd");
+  std::string::size_type zstdPos = fname.rfind(".zst");
   // WasmZstdImageIO is required
   if ( zstdPos != std::string::npos )
     {
@@ -602,7 +602,7 @@ ::CanWriteFile(const char *name)
     return false;
     }
 
-  std::string::size_type zstdPos = filename.rfind(".zstd");
+  std::string::size_type zstdPos = filename.rfind(".zst");
   // WasmZstdImageIO is required
   if ( zstdPos != std::string::npos )
     {


Test patch:
diff --git a/test/node/io/image/WasmTest.js b/test/node/io/image/WasmTest.js
index 221bfbc8e..62b154d42 100644
--- a/test/node/io/image/WasmTest.js
+++ b/test/node/io/image/WasmTest.js
@@ -5,7 +5,7 @@ import { IntTypes, PixelTypes, getMatrixElement, readImageLocalFile, writeImageL
 
 const testInputFilePath = path.resolve('build-emscripten', 'ExternalData', 'test', 'Input', 'cthead1.png')
 const testOutputFilePath = path.resolve('build-emscripten', 'Testing', 'Temporary', 'TestWasm-cthead1.iwi.cbor')
-const testZstdOutputFilePath = path.resolve('build-emscripten', 'Testing', 'Temporary', 'TestWasm-cthead1.iwi.cbor.zstd')
+const testZstdOutputFilePath = path.resolve('build-emscripten', 'Testing', 'Temporary', 'TestWasm-cthead1.iwi.cbor.zst')
 
 const verifyImage = (t, image) => {
   t.is(image.imageType.dimension, 2, 'dimension')
diff --git a/test/node/io/mesh/extensionToMeshIOTest.js b/test/node/io/mesh/extensionToMeshIOTest.js
index be3b48148..51cee208c 100644
--- a/test/node/io/mesh/extensionToMeshIOTest.js
+++ b/test/node/io/mesh/extensionToMeshIOTest.js
@@ -92,7 +92,7 @@ test('iwm.cbor maps to WasmMeshIO', t => {
   t.is(io, 'WasmMeshIO')
 })
 
-test('iwm.cbor.zstd maps to WasmMeshIO', t => {
-  const io = ExtensionToIO.get('iwm.cbor.zstd')
+test('iwm.cbor.zst maps to WasmMeshIO', t => {
+  const io = ExtensionToIO.get('iwm.cbor.zst')
   t.is(io, 'WasmZstdMeshIO')
 })