This is an automated email from the ASF dual-hosted git repository.

shanedell pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil-vscode.git


The following commit(s) were added to refs/heads/main by this push:
     new 4d4ac7f  Add support for multiple versions of the debugger
4d4ac7f is described below

commit 4d4ac7f39ed3fba02c040c0ee2f8a7054531db4b
Author: Shane Dell <[email protected]>
AuthorDate: Mon Oct 6 13:05:10 2025 -0400

    Add support for multiple versions of the debugger
    
    - Support 3 versions of the debugger.
      - Scala 2.12 + Daffodil <=3.10.0.
      - Scala 2.13 + Daffodil =3.11.0.
      - Scala 3 + Daffodil >=4.0.0.
    - When running locally, if JDK >= 17 all the debuggers are built but if JDK 
< 17 only the Scala 2 debuggers are built.
    - Fixed .vscode/launch.json so that the debugging of the tests can be done 
now.
    - Update CI jobs that defaulted to Java 8 only to default to Java 17 only.
    - Add Java 21 to CI some CI jobs for testing multiple versions of Java.
    - Update vite config to be able to extract all 3 versions of the debugger.
    - Add launch config for daffodilVersion and debugger timeout.
      - Values for daffodilVersion should be a proper Daffodil release version
      - Timeout is any number but needs to end with s(seconds), m(minutes) or 
h(hours).
        - If no s, m, h set then s is appended to the end.
    - Add more tests for testing that all 3 debuggers can output an XML and 
JSON infoset file.
    - Cleaned up yarn test output so it looks much cleaner.
    - Update daffodil-debugger scripts to not be built to target a single 
Daffodil version.
      - The extension will download and extract the proper Daffodil release 
based on the version set in the launch.json.
    - Added a required flag `--daffodilPath` for the daffodil-debugger scripts.
      - The extension will build the daffodil based on the version being used 
and pass it the script using this flag
      - If the user wants to run the debugger on their own, they will need to 
provide this flag. This will allow users to test unreleased version of Daffodil.
    
    Closes #1352
---
 .github/workflows/CI.yml                           |   4 +-
 .github/workflows/licenses.yml                     |   2 +-
 .github/workflows/nightly.yml                      |   2 +-
 .github/workflows/release-candidate.yml            |   2 +-
 .vscode/launch.json                                |   2 +-
 .vscode/tasks.json                                 |   2 +-
 build.sbt                                          | 169 ++++++++-----
 .../org.apache.daffodil.debugger.dap/Convert.scala |  12 +-
 .../org.apache.daffodil.debugger.dap/Convert.scala |  12 +-
 .../org.apache.daffodil.debugger.dap/DAP.scala     |  15 +-
 .../org.apache.daffodil.debugger.dap/Support.scala |  71 ++++++
 .../org.apache.daffodil.debugger.dap/Convert.scala |  12 +-
 .../org.apache.daffodil.debugger.dap/DAP.scala     |  15 +-
 .../org.apache.daffodil.debugger.dap/Support.scala |  63 +++++
 .../Compiler.scala                                 |  14 +-
 .../org.apache.daffodil.debugger.dap/DAPodil.scala |  19 +-
 .../org.apache.daffodil.debugger.dap/Parse.scala   |  49 ++--
 .../org.apache.daffodil.debugger.dap/Utils.scala   |   2 +-
 .../main/scala/org.apache.daffodil.tdml/TDML.scala |  18 +-
 debugger/src/templates/bash-template               |  17 +-
 debugger/src/templates/bat-template                |  22 +-
 .../org.apache.daffodil.debugger/ParseSuite.scala  |   8 +-
 package.json                                       |  27 ++-
 project/plugins.sbt                                |   1 +
 src/adapter/activateDaffodilDebug.ts               |  21 +-
 src/adapter/daffodilDebugErrors.ts                 |   3 +-
 src/adapter/daffodilEvent.ts                       |   4 +-
 src/classes/dfdlDebugger.ts                        |   2 +
 src/daffodilDebugger/daffodil.ts                   |   6 -
 src/daffodilDebugger/daffodilJars.ts               |  65 +++++
 src/daffodilDebugger/debugger.ts                   |   8 +-
 src/daffodilDebugger/utils.ts                      | 126 +++++++---
 src/launchWizard/launchWizard.ts                   |   8 +-
 src/launchWizard/script.js                         |  16 ++
 src/tests/suite/daffodil.test.ts                   | 143 +++++------
 src/tests/suite/daffodilDebugger.test.ts           | 264 ++++++++++++++-------
 src/tests/suite/utils.test.ts                      |   2 +
 src/utils.ts                                       | 134 ++++++++++-
 vite/dev.vite.config.mjs                           |  53 +++--
 vite/package.vite.config.mjs                       |  86 ++++---
 40 files changed, 1058 insertions(+), 443 deletions(-)

diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index adaeecb..77b12cc 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -61,7 +61,7 @@ jobs:
     strategy:
       matrix:
         java_distribution: [temurin]
-        java_version: [8]
+        java_version: [17]
         os: [ubuntu-22.04]
         node: ["20"]
     runs-on: ${{ matrix.os }}
@@ -169,7 +169,7 @@ jobs:
     strategy:
       matrix:
         java_distribution: [temurin]
-        java_version: [8, 11, 17]
+        java_version: [8, 11, 17, 21]
         os: [macos-13, ubuntu-22.04, windows-2022]
         node: ["20.19.4", "22.14.0"]
         vscode: ["1.90.0", "stable"] # v1.90.0 is the first version of VSCode 
to use Node 20
diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml
index 3a18c46..3af3751 100644
--- a/.github/workflows/licenses.yml
+++ b/.github/workflows/licenses.yml
@@ -29,7 +29,7 @@ jobs:
     strategy:
       matrix:
         java_distribution: [temurin]
-        java_version: [8]
+        java_version: [17]
         os: [ubuntu-22.04]
     runs-on: ${{ matrix.os }}
     defaults:
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index 29a5c76..86b5999 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -38,7 +38,7 @@ jobs:
         node: ["20.18.3", "22.14.0"]
         vscode: ["1.90.0", "stable", "insiders"] # v1.90.0 is the first 
version of VSCode to use Node 18
         java_distribution: [temurin]
-        java_version: [8, 11, 17]
+        java_version: [8, 11, 17, 21]
         exclude:
           # java 8 not available on latest macos
           - os: macos-latest
diff --git a/.github/workflows/release-candidate.yml 
b/.github/workflows/release-candidate.yml
index 8293c9a..2fbc1c9 100644
--- a/.github/workflows/release-candidate.yml
+++ b/.github/workflows/release-candidate.yml
@@ -56,7 +56,7 @@ jobs:
         uses: actions/setup-java@a7ab372554b6eb1a8eb25e7d9aec1cc9f3ea1a76 # 
v4.6.0
         with:
           distribution: temurin
-          java-version: 8
+          java-version: 17
 
       - name: Install Node.js
         uses: actions/setup-node@5e2628c959b9ade56971c0afcebbe5332d44b398 # 
v4.4.0
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 99b6d5a..ad0927c 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -38,7 +38,7 @@
                        "runtimeExecutable": "${execPath}",
                        "args": [
                                "--extensionDevelopmentPath=${workspaceFolder}",
-                               
"--extensionTestsPath=${workspaceFolder}/out/tests/suite"
+                               
"--extensionTestsPath=${workspaceFolder}/out/tests/suite/index"
                        ],
                        "outFiles": [
                                "${workspaceFolder}/out/tests/**/*.js",
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index fcbdcee..029b701 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -32,7 +32,7 @@
             "type": "npm",
             "script": "watch",
             "group": "build",
-            "isBackground": true,
+            "isBackground": true
         }
     ]
 }
diff --git a/build.sbt b/build.sbt
index 06dc63e..67d4420 100644
--- a/build.sbt
+++ b/build.sbt
@@ -27,23 +27,10 @@ import javax.xml.transform.stream.StreamSource
 //Fixes build issues on java11+
 run / fork := true
 Global / lintUnusedKeysOnLoad := false
-val packageJsonStr = scala.io.Source.fromFile("package.json").mkString
-
-val daffodilVer = {
-  val daffodilVerRegex = raw""""daffodilVersion": "(.*)",""".r
-  daffodilVerRegex.findFirstMatchIn(packageJsonStr) match {
-    case Some(m) => m.toString.split(":")(1).trim.replaceAll("\"", 
"").replaceAll(",", "")
-    case None    => sys.error("Missing daffodilVersion specifier in 
package.json")
-  }
-}
 
 lazy val commonSettings =
   Seq(
     version := IO.read((ThisBuild / baseDirectory).value / "VERSION").trim,
-    libraryDependencies ++= Seq(
-      "org.apache.daffodil" %% "daffodil-sapi" % daffodilVer,
-      "org.apache.daffodil" %% "daffodil-runtime1" % daffodilVer
-    ),
     dependencyOverrides ++= Seq(
       "org.apache.commons" % "commons-lang3" % "3.18.0"
     ),
@@ -51,9 +38,6 @@ lazy val commonSettings =
     licenses += ("Apache-2.0", new 
URL("https://www.apache.org/licenses/LICENSE-2.0.txt";)),
     maintainer := "Apache Daffodil <[email protected]>",
     organization := "org.apache.daffodil",
-    // scala-steward:off
-    scalaVersion := "2.13.16",
-    // scala-steward:on
     // remove the -Xcheckinit option added by the sbt tpoletcat plugin. This
     // option leads to non-reproducible builds
     scalacOptions --= Seq("-Xcheckinit"),
@@ -74,40 +58,6 @@ lazy val ratSettings = Seq(
   ratFailBinaries := true
 )
 
-lazy val `daffodil-debugger` = project
-  .in(file("."))
-  .settings(commonSettings, ratSettings)
-  .settings(publish / skip := true)
-  .dependsOn(debugger)
-  .aggregate(debugger)
-
-lazy val debugger = project
-  .in(file("debugger"))
-  .enablePlugins(BuildInfoPlugin, JavaAppPackaging, UniversalPlugin, 
ClasspathJarPlugin, SbtXjcPlugin)
-  .settings(commonSettings)
-  .settings(xjcSettings)
-  .settings(
-    name := "daffodil-debugger",
-    libraryDependencies ++= Seq(
-      /* NOTE: To support Java 8:
-       *   logback-classic can not go above version 1.2.11.
-       *   com.microsoft.java.debug.core can not go above version 0.34.0.
-       */
-      // scala-steward:off
-      "ch.qos.logback" % "logback-classic" % "1.2.11",
-      "com.microsoft.java" % "com.microsoft.java.debug.core" % "0.34.0",
-      // scala-steward:on
-      "co.fs2" %% "fs2-io" % "3.12.0",
-      "com.monovore" %% "decline-effect" % "2.5.0",
-      "org.typelevel" %% "log4cats-slf4j" % "2.7.1",
-      "org.scalameta" %% "munit" % "1.1.1" % Test,
-      "org.fusesource.jansi" % "jansi" % "1.18"
-    ),
-    buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, 
sbtVersion, "daffodilVersion" -> daffodilVer),
-    buildInfoPackage := "org.apache.daffodil.debugger.dap",
-    packageName := s"${name.value}-$daffodilVer"
-  )
-
 /* Workaround: certain reflection (used by JAXB) isn't allowed by default in 
JDK 17:
  * 
https://docs.oracle.com/en/java/javase/17/migrate/migrating-jdk-8-later-jdk-releases.html#GUID-7BB28E4D-99B3-4078-BDC4-FC24180CE82B
  *
@@ -128,7 +78,6 @@ lazy val xjcSettings =
     libraryDependencies ++= Seq(
       "com.sun.xml.bind" % "jaxb-impl" % "2.3.9",
       "javax.activation" % "activation" % "1.1.1",
-      "org.apache.daffodil" %% "daffodil-lib" % daffodilVer % Test,
       "org.glassfish.jaxb" % "jaxb-xjc" % "2.3.9"
     ),
     Test / javaOptions ++= extraJvmOptions, // tests use JAXB at runtime
@@ -150,11 +99,11 @@ lazy val xjcSettings =
       // classes
       lazy val xjcSourceDir = crossTarget.value / "xjc"
 
-      // Get the daffodil-lib jar from the dependencies, this is the only jar 
we need to extract
+      // Get the daffodil-lib or daffodil-core jar from the dependencies, this 
is the only jar we need to extract
       // files from
-      val daffodilLibJar = managedJars(Test, Set[String]("jar"), update.value)
+      val daffodilJarWithTdmlXsd = managedJars(Test, Set[String]("jar"), 
update.value)
         .map(_.data)
-        .find(_.getName.contains("daffodil-lib"))
+        .find(_.getName.contains(getDaffodilJarName(scalaBinaryVersion.value)))
         .get
 
       // cache the results of jar extraction so we don't keep extracting files 
(which would
@@ -162,7 +111,7 @@ lazy val xjcSettings =
       val cachedFun = FileFunction.cached(stream.cacheDirectory / 
"xjcSources") { _ =>
         // Extract the DFDL TDML schema file used for JAXB generation.
         IO.unzip(
-          daffodilLibJar,
+          daffodilJarWithTdmlXsd,
           xjcSourceDir,
           NameFilter.fnToNameFilter(f => f == 
"org/apache/daffodil/xsd/tdml.xsd")
         )
@@ -218,6 +167,114 @@ lazy val xjcSettings =
 
       // only invalidate the cache if the daffodil lib jar changed and so 
there could be a
       // chance the tdml.xsd file has been updated
-      cachedFun(Set(daffodilLibJar)).toSeq
+      cachedFun(Set(daffodilJarWithTdmlXsd)).toSeq
     }
   )
+
+lazy val `daffodil-debugger` = project
+  .in(file("."))
+  .settings(commonSettings, ratSettings)
+  .settings(publish / skip := true)
+  .aggregate(debugger.projectRefs: _*)
+
+/** Since using projectMatrix, there will be a debugger, debugger2_12 and 
debugger3 target. The debugger target is for
+  * Daffodil 3.11.0 and Scala 2.13. The debugger2_12 target is for Daffodil 
3.10.0 abd older and Scala 2.12. The
+  * debugger3 target is for Daffodil 4.0.0 and newer and Scala 3. (only 
availabe when using JDK 17+)
+  *
+  * When running something like "sbt test" that will run all targets. To use a 
single target do one of: sbt
+  * debugger/test OR sbt debugger2_12/test OR sbt debugger3/test. Based on 
which version of the debugger you are
+  * targeting.
+  */
+lazy val debugger =
+  (projectMatrix in (file("debugger")))
+    .enablePlugins(BuildInfoPlugin, JavaAppPackaging, UniversalPlugin, 
ClasspathJarPlugin, SbtXjcPlugin)
+    .settings(commonSettings)
+    .settings(xjcSettings)
+    .settings(
+      name := "daffodil-debugger",
+      scalacOptions ++= buildScalacOptions(scalaBinaryVersion.value),
+      javacOptions ++= buildJavacOptions(scalaBinaryVersion.value),
+      libraryDependencies ++= Seq(
+        /* NOTE: To support Java 8:
+         *  logback-classic can not go above version 1.2.11.
+         *  com.microsoft.java.debug.core can not go above version 0.34.0.
+         *  jansi can not go above version 1.18.
+         */
+        // scala-steward:off
+        "ch.qos.logback" % "logback-classic" % "1.2.11",
+        "com.microsoft.java" % "com.microsoft.java.debug.core" % "0.34.0",
+        "org.fusesource.jansi" % "jansi" % "1.18",
+        // scala-steward:on
+        "co.fs2" %% "fs2-io" % "3.12.0",
+        "com.monovore" %% "decline-effect" % "2.5.0",
+        "org.typelevel" %% "log4cats-slf4j" % "2.7.1",
+        "org.scalameta" %% "munit" % "1.1.1" % Test
+      ),
+      libraryDependencies ++= 
getPlatformSpecificLibraries(scalaBinaryVersion.value),
+      buildInfoPackage := "org.apache.daffodil.debugger.dap",
+      buildInfoKeys := Seq[BuildInfoKey](
+        name,
+        version,
+        scalaVersion,
+        sbtVersion
+      ),
+      packageName := s"${name.value}-${scalaBinaryVersion.value}"
+    )
+    .jvmPlatform(
+      scalaVersions =
+        Seq("2.12.20", "2.13.16") ++ (if 
(scala.util.Properties.isJavaAtLeast("17")) Seq("3.3.6") else Seq())
+    )
+
+def getPlatformSpecificLibraries(scalaBinaryVersion: String) =
+  scalaBinaryVersion match {
+    case "2.12" =>
+      Seq(
+        // scala-steward:off
+        "org.apache.daffodil" %% "daffodil-sapi" % "3.10.0" % "provided,test",
+        "org.apache.daffodil" %% "daffodil-runtime1" % "3.10.0" % 
"provided,test",
+        "org.apache.daffodil" %% "daffodil-lib" % "3.10.0" % Test,
+        // scala-steward:on
+        "org.scala-lang.modules" %% "scala-collection-compat" % "2.14.0"
+      )
+    case "2.13" =>
+      Seq(
+        // scala-steward:off
+        "org.apache.daffodil" %% "daffodil-sapi" % "3.11.0" % "provided,test",
+        "org.apache.daffodil" %% "daffodil-runtime1" % "3.11.0" % 
"provided,test",
+        "org.apache.daffodil" %% "daffodil-lib" % "3.11.0" % Test
+        // scala-steward:on
+      )
+    case "3" =>
+      Seq(
+        "org.apache.daffodil" %% "daffodil-core" % "4.0.0" % "provided,test"
+      )
+  }
+
+def getMinSupportedJavaVersion(scalaBinaryVersion: String): String =
+  if (scalaBinaryVersion == "3") "17"
+  else if (scala.util.Properties.isJavaAtLeast("21")) "11"
+  else "8"
+
+def buildJavacOptions(scalaBinaryVersion: String) = {
+  val jdkCompat = getMinSupportedJavaVersion(scalaBinaryVersion)
+
+  if (jdkCompat == "8")
+    Seq("-source", jdkCompat, "-target", jdkCompat)
+  else
+    Seq("--release", jdkCompat)
+}
+
+def buildScalacOptions(scalaBinaryVersion: String) = {
+  val jdkCompat = getMinSupportedJavaVersion(scalaBinaryVersion)
+
+  val commonSettings = Seq(if (scalaBinaryVersion == "2.12") 
"-Ypartial-unification" else "")
+
+  if (scalaBinaryVersion == "2.12" && jdkCompat == "8")
+    commonSettings ++ Seq(s"--target:jvm-${jdkCompat}")
+  else
+    commonSettings ++ Seq("--release", jdkCompat)
+}
+
+def getDaffodilJarName(scalaBinaryVersion: String) =
+  if (scalaBinaryVersion == "3") "daffodil-core"
+  else "daffodil-lib"
diff --git a/src/classes/dfdlDebugger.ts 
b/debugger/src/main/scala-2.12/org.apache.daffodil.debugger.dap/Convert.scala
similarity index 61%
copy from src/classes/dfdlDebugger.ts
copy to 
debugger/src/main/scala-2.12/org.apache.daffodil.debugger.dap/Convert.scala
index a666ff9..3468694 100644
--- a/src/classes/dfdlDebugger.ts
+++ 
b/debugger/src/main/scala-2.12/org.apache.daffodil.debugger.dap/Convert.scala
@@ -15,8 +15,14 @@
  * limitations under the License.
  */
 
-import { LoggingConfig } from '../classes/loggingConfig'
+/** This file contains support code for making a majority of Scala shareable 
between different versions of Scala and
+  * Daffodil. The main difference comes in package names, converting certain 
variables, etc. This file has all the
+  * helper code for that for Scala 2.13.
+  */
 
-export interface DFDLDebugger {
-  logging: LoggingConfig
+package org.apache.daffodil.debugger.dap
+
+object Convert {
+  /* Daffodil Maybe wrapper methods */
+  def daffodilMaybeToOption[T <: AnyRef](maybe: 
org.apache.daffodil.lib.util.Maybe[T]): Option[T] = maybe.toScalaOption
 }
diff --git a/src/classes/dfdlDebugger.ts 
b/debugger/src/main/scala-2.13/org.apache.daffodil.debugger.dap/Convert.scala
similarity index 62%
copy from src/classes/dfdlDebugger.ts
copy to 
debugger/src/main/scala-2.13/org.apache.daffodil.debugger.dap/Convert.scala
index a666ff9..21decee 100644
--- a/src/classes/dfdlDebugger.ts
+++ 
b/debugger/src/main/scala-2.13/org.apache.daffodil.debugger.dap/Convert.scala
@@ -15,8 +15,14 @@
  * limitations under the License.
  */
 
-import { LoggingConfig } from '../classes/loggingConfig'
+/** This file contains support code for making a majority of Scala shareable 
between different versions of Scala and
+  * Daffodil. The main difference comes in package names, converting certain 
variables, etc. This file has all the
+  * helper code for that for Scala 2.13.
+  */
 
-export interface DFDLDebugger {
-  logging: LoggingConfig
+package org.apache.daffodil.debugger.dap
+
+object Convert {
+  /* Daffodil Maybe wrapper methods */
+  def daffodilMaybeToOption[T <: AnyRef](maybe: 
org.apache.daffodil.lib.util.Maybe[T]): Option[T] = maybe.toOption
 }
diff --git a/src/classes/dfdlDebugger.ts 
b/debugger/src/main/scala-2/org.apache.daffodil.debugger.dap/DAP.scala
similarity index 54%
copy from src/classes/dfdlDebugger.ts
copy to debugger/src/main/scala-2/org.apache.daffodil.debugger.dap/DAP.scala
index a666ff9..424de53 100644
--- a/src/classes/dfdlDebugger.ts
+++ b/debugger/src/main/scala-2/org.apache.daffodil.debugger.dap/DAP.scala
@@ -15,8 +15,17 @@
  * limitations under the License.
  */
 
-import { LoggingConfig } from '../classes/loggingConfig'
+package org.apache.daffodil.debugger
 
-export interface DFDLDebugger {
-  logging: LoggingConfig
+/** The dap package object allows other files inside of the package to be able 
to use specific classes and types without
+  * directly importing them. This help us with the different versions of 
Daffodil where the classes have the same name
+  * but have moved to different import paths.
+  */
+
+package object dap {
+  type Debugger = org.apache.daffodil.runtime1.debugger.Debugger
+  type Diagnostic = org.apache.daffodil.lib.api.Diagnostic
+  type SDiagnostic = org.apache.daffodil.sapi.Diagnostic
+  type InfosetOutputter = org.apache.daffodil.sapi.infoset.InfosetOutputter
+  type DataProcessor = org.apache.daffodil.sapi.DataProcessor
 }
diff --git 
a/debugger/src/main/scala-2/org.apache.daffodil.debugger.dap/Support.scala 
b/debugger/src/main/scala-2/org.apache.daffodil.debugger.dap/Support.scala
new file mode 100644
index 0000000..76394f1
--- /dev/null
+++ b/debugger/src/main/scala-2/org.apache.daffodil.debugger.dap/Support.scala
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** This file contains support code for making a majority of Scala shareable 
between different versions of Scala and
+  * Daffodil. The main difference comes in package names, converting certain 
variables, etc. This file has all the
+  * helper code for that for Scala 2.13.
+  */
+
+package org.apache.daffodil.debugger.dap
+
+import java.io._
+import java.nio.file.Path
+import org.apache.daffodil.sapi._
+import org.apache.daffodil.sapi.io.InputSourceDataInputStream
+import org.apache.daffodil.sapi.infoset.{JsonInfosetOutputter, 
XMLTextInfosetOutputter}
+
+object Support {
+  /* Daffodil DataProcessor wrapper methods */
+  def dataProcessorWithDebugger(
+      p: DataProcessor,
+      debugger: org.apache.daffodil.runtime1.debugger.Debugger,
+      variables: Map[String, String]
+  ): DataProcessor =
+    p.withDebugger(debugger)
+      .withDebugging(true)
+      .withExternalVariables(variables)
+      .withValidationMode(ValidationMode.Limited)
+
+  /* Daffodil infoset wrapper methods */
+  def getInputSourceDataInputStream(data: InputStream): 
InputSourceDataInputStream = new InputSourceDataInputStream(
+    data
+  )
+  def getInfosetOutputter(infosetFormat: String, stream: OutputStream): 
InfosetOutputter =
+    infosetFormat match {
+      case "xml"  => new XMLTextInfosetOutputter(stream, true)
+      case "json" => new JsonInfosetOutputter(stream, true)
+    }
+
+  /* Daffodil ProcessorFactory wrapper methods */
+  def getProcessorFactory(
+      schema: Path,
+      rootName: Option[String],
+      rootNamespace: Option[String],
+      tunables: Map[String, String]
+  ): ProcessorFactory =
+    Daffodil
+      .compiler()
+      .withTunables(tunables)
+      .compileFile(
+        schema.toFile(),
+        rootName,
+        rootNamespace
+      )
+
+  /* Method to parse diagnostic list, this only necessary in scala 3 but to 
make everything match this is needed. */
+  def parseDiagnosticList(dl: Seq[org.apache.daffodil.sapi.Diagnostic]): 
Seq[org.apache.daffodil.sapi.Diagnostic] = dl
+}
diff --git a/src/classes/dfdlDebugger.ts 
b/debugger/src/main/scala-3/org.apache.daffodil.debugger.dap/Convert.scala
similarity index 62%
copy from src/classes/dfdlDebugger.ts
copy to debugger/src/main/scala-3/org.apache.daffodil.debugger.dap/Convert.scala
index a666ff9..b44e158 100644
--- a/src/classes/dfdlDebugger.ts
+++ b/debugger/src/main/scala-3/org.apache.daffodil.debugger.dap/Convert.scala
@@ -15,8 +15,14 @@
  * limitations under the License.
  */
 
-import { LoggingConfig } from '../classes/loggingConfig'
+/** This file contains support code for making a majority of Scala shareable 
between different versions of Scala and
+  * Daffodil. The main difference comes in package names, converting certain 
variables, etc. This file has all the
+  * helper code for that for Scala 3.
+  */
 
-export interface DFDLDebugger {
-  logging: LoggingConfig
+package org.apache.daffodil.debugger.dap
+
+object Convert {
+  /* Daffodil Maybe wrapper methods */
+  def daffodilMaybeToOption[T <: AnyRef](maybe: 
org.apache.daffodil.lib.util.Maybe[T]): Option[T] = maybe.toOption
 }
diff --git a/src/classes/dfdlDebugger.ts 
b/debugger/src/main/scala-3/org.apache.daffodil.debugger.dap/DAP.scala
similarity index 55%
copy from src/classes/dfdlDebugger.ts
copy to debugger/src/main/scala-3/org.apache.daffodil.debugger.dap/DAP.scala
index a666ff9..6f3325c 100644
--- a/src/classes/dfdlDebugger.ts
+++ b/debugger/src/main/scala-3/org.apache.daffodil.debugger.dap/DAP.scala
@@ -15,8 +15,17 @@
  * limitations under the License.
  */
 
-import { LoggingConfig } from '../classes/loggingConfig'
+package org.apache.daffodil.debugger
 
-export interface DFDLDebugger {
-  logging: LoggingConfig
+/** The dap package object allows other files inside of the package to be able 
to use specific classes and types without
+  * directly importing them. This help us with the different versions of 
Daffodil where the classes have the same name
+  * but have moved to different import paths.
+  */
+
+package object dap {
+  type Debugger = org.apache.daffodil.api.debugger.Debugger
+  type Diagnostic = org.apache.daffodil.api.Diagnostic
+  type SDiagnostic = org.apache.daffodil.api.Diagnostic
+  type InfosetOutputter = org.apache.daffodil.api.infoset.InfosetOutputter
+  type DataProcessor = org.apache.daffodil.api.DataProcessor
 }
diff --git 
a/debugger/src/main/scala-3/org.apache.daffodil.debugger.dap/Support.scala 
b/debugger/src/main/scala-3/org.apache.daffodil.debugger.dap/Support.scala
new file mode 100644
index 0000000..5012e9b
--- /dev/null
+++ b/debugger/src/main/scala-3/org.apache.daffodil.debugger.dap/Support.scala
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** This file contains support code for making a majority of Scala shareable 
between different versions of Scala and
+  * Daffodil. The main difference comes in package names, converting certain 
variables, etc. This file has all the
+  * helper code for that for Scala 3.
+  */
+
+package org.apache.daffodil.debugger.dap
+
+import java.io._
+import java.nio.file.Path
+import org.apache.daffodil.api._
+import scala.jdk.CollectionConverters._
+
+object Support {
+  /* Daffodil DataProcessor wrapper methods */
+  def dataProcessorWithDebugger(p: DataProcessor, debugger: Debugger, 
variables: Map[String, String]): DataProcessor =
+    p.withDebugger(debugger)
+      .withExternalVariables(variables.asJava)
+      .withValidation("daffodil")
+
+  /* Daffodil infoset wrapper methods */
+  def getInputSourceDataInputStream(data: InputStream): 
InputSourceDataInputStream =
+    Daffodil.newInputSourceDataInputStream(data)
+  def getInfosetOutputter(infosetFormat: String, stream: OutputStream): 
InfosetOutputter =
+    infosetFormat match {
+      case "xml"  => Daffodil.newXMLTextInfosetOutputter(stream, true)
+      case "json" => Daffodil.newJsonInfosetOutputter(stream, true)
+    }
+
+  /* Daffodil ProcessorFactory wrapper methods */
+  def getProcessorFactory(
+      schema: Path,
+      rootName: Option[String],
+      rootNamespace: Option[String],
+      tunables: Map[String, String]
+  ): ProcessorFactory =
+    Daffodil
+      .compiler()
+      .withTunables(tunables.asJava)
+      .compileFile(schema.toFile(), rootName.orNull, rootNamespace.orNull)
+
+  /* Method to convert java list of diagnostics to a sequence of diagnostics */
+  def parseDiagnosticList(
+      dl: java.util.List[org.apache.daffodil.api.Diagnostic]
+  ): Seq[org.apache.daffodil.api.Diagnostic] =
+    dl.asScala.toSeq
+}
diff --git 
a/debugger/src/main/scala/org.apache.daffodil.debugger.dap/Compiler.scala 
b/debugger/src/main/scala/org.apache.daffodil.debugger.dap/Compiler.scala
index b597001..592bbf2 100644
--- a/debugger/src/main/scala/org.apache.daffodil.debugger.dap/Compiler.scala
+++ b/debugger/src/main/scala/org.apache.daffodil.debugger.dap/Compiler.scala
@@ -20,7 +20,6 @@ package org.apache.daffodil.debugger.dap
 import cats.effect.IO
 import cats.syntax.all._
 import java.nio.file.Path
-import org.apache.daffodil.sapi._
 
 trait DAPCompiler {
   def compile(
@@ -41,17 +40,10 @@ object DAPCompiler {
           tunables: Map[String, String]
       ): IO[DataProcessor] =
         IO.blocking(
-          Daffodil
-            .compiler()
-            .withTunables(tunables)
-            .compileFile(
-              schema.toFile(),
-              rootName,
-              rootNamespace
-            )
-        ).ensureOr(pf => CompilationFailed(pf.getDiagnostics))(!_.isError())
+          Support.getProcessorFactory(schema, rootName, rootNamespace, 
tunables)
+        ).ensureOr(pf => 
CompilationFailed(Support.parseDiagnosticList(pf.getDiagnostics)))(!_.isError())
           .map(_.onPath("/"))
     }
 
-  case class CompilationFailed(seq: Seq[Diagnostic]) extends 
Exception(seq.map(_.toString).mkString(", "))
+  case class CompilationFailed(seq: Seq[SDiagnostic]) extends 
Exception(seq.map(_.toString).mkString(", "))
 }
diff --git 
a/debugger/src/main/scala/org.apache.daffodil.debugger.dap/DAPodil.scala 
b/debugger/src/main/scala/org.apache.daffodil.debugger.dap/DAPodil.scala
index c5a2d91..d2458ac 100644
--- a/debugger/src/main/scala/org.apache.daffodil.debugger.dap/DAPodil.scala
+++ b/debugger/src/main/scala/org.apache.daffodil.debugger.dap/DAPodil.scala
@@ -35,12 +35,12 @@ import java.io._
 import java.net._
 import java.nio.file.Path
 import java.nio.file.Paths
+import org.apache.daffodil.lib.util.Misc
 import org.typelevel.log4cats.Logger
 import org.typelevel.log4cats.slf4j.Slf4jLogger
 
-import scala.jdk.CollectionConverters._
 import scala.concurrent.duration._
-
+import scala.jdk.CollectionConverters._
 import logging._
 
 /** Communication interface to a DAP server while in a connected session. */
@@ -379,7 +379,8 @@ class DAPodil(
               )
             ) { frame =>
               session.sendResponse(
-                request.respondSuccess(new 
Responses.ScopesResponseBody(frame.scopes.map(_.toDAP()).asJava))
+                request
+                  .respondSuccess(new 
Responses.ScopesResponseBody(frame.scopes.map(_.toDAP()).asJava))
               )
             }
         } yield ()
@@ -403,7 +404,9 @@ class DAPodil(
                 )
               )
             )(variables =>
-              session.sendResponse(request.respondSuccess(new 
Responses.VariablesResponseBody(variables.asJava)))
+              session.sendResponse(
+                request.respondSuccess(new 
Responses.VariablesResponseBody(variables.asJava))
+              )
             )
         } yield ()
       case s => DAPodil.InvalidState.raise(request, "Launched", s)
@@ -435,7 +438,7 @@ object DAPodil extends IOApp {
       Opts
         .option[Duration]("listenTimeout", "duration to wait for a DAP client 
connection (default: 10s)")
         .withDefault(10.seconds)
-    ).mapN(Options)
+    ).mapN(Options.apply)
 
   implicit val logger: Logger[IO] = Slf4jLogger.getLogger
 
@@ -446,11 +449,11 @@ object DAPodil extends IOApp {
         |
         |Build info:
         |  version: ${BuildInfo.version}
-        |  daffodilVersion: ${BuildInfo.daffodilVersion}
         |  scalaVersion: ${BuildInfo.scalaVersion}
         |  sbtVersion: ${BuildInfo.sbtVersion}
         |Runtime info:
         |  JVM version: ${System.getProperty("java.version")} 
(${System.getProperty("java.home")})
+        |  Daffodil Version: ${Misc.getDaffodilVersion}
         |******************************************************""".stripMargin
 
   def run(args: List[String]): IO[ExitCode] =
@@ -468,7 +471,9 @@ object DAPodil extends IOApp {
       _ <- Logger[IO].info(header)
       _ <- options match {
         case Options(listenPort, listenTimeout) =>
-          Logger[IO].info(s"launched with options listenPort: $listenPort, 
listenTimeout: $listenTimeout")
+          Logger[IO].info(
+            s"launched with options listenPort: $listenPort, listenTimeout: 
$listenTimeout"
+          )
       }
 
       _ <- Ref[IO].of[State](State.Uninitialized) // state for the DAPodil 
instance
diff --git 
a/debugger/src/main/scala/org.apache.daffodil.debugger.dap/Parse.scala 
b/debugger/src/main/scala/org.apache.daffodil.debugger.dap/Parse.scala
index 6846390..810dc7a 100644
--- a/debugger/src/main/scala/org.apache.daffodil.debugger.dap/Parse.scala
+++ b/debugger/src/main/scala/org.apache.daffodil.debugger.dap/Parse.scala
@@ -33,16 +33,12 @@ import java.net.URI
 import java.nio.file._
 import org.apache.commons.io.FileUtils
 import org.apache.daffodil.debugger.dap.{BuildInfo => DAPBuildInfo}
-import org.apache.daffodil.runtime1.debugger.Debugger
 import org.apache.daffodil.runtime1.infoset.{DIDocument, DIElement, 
InfosetWalker}
 import org.apache.daffodil.runtime1.processors._
 import org.apache.daffodil.runtime1.processors.dfa.DFADelimiter
 import org.apache.daffodil.runtime1.processors.parsers._
 import org.apache.daffodil.lib.exceptions.SchemaFileLocation
 import org.apache.daffodil.lib.util.Misc
-import org.apache.daffodil.sapi.{Diagnostic => SDiagnostic, ValidationMode}
-import org.apache.daffodil.sapi.infoset._
-import org.apache.daffodil.sapi.io.InputSourceDataInputStream
 import org.apache.daffodil.tdml.TDML
 import org.typelevel.log4cats.Logger
 import org.typelevel.log4cats.slf4j.Slf4jLogger
@@ -86,12 +82,7 @@ object Parse {
     for {
       dp <- DAPCompiler()
         .compile(schema, rootName, rootNamespace, tunables)
-        .map(p =>
-          p.withDebugger(debugger)
-            .withDebugging(true)
-            .withExternalVariables(variables)
-            .withValidationMode(ValidationMode.Limited)
-        )
+        .map(p => Support.dataProcessorWithDebugger(p, debugger, variables))
       done <- Ref[IO].of(false)
       pleaseStop <- Deferred[IO, Unit]
     } yield new Parse {
@@ -104,18 +95,13 @@ object Parse {
             val stopper =
               pleaseStop.get *> IO.canceled // will cancel the concurrent 
parse effect
 
-            val infosetOutputter = infosetFormat match {
-              case "xml"  => new XMLTextInfosetOutputter(os, true)
-              case "json" => new JsonInfosetOutputter(os, true)
-            }
-
             val dataSize = data.available()
 
             val parse =
               IO.interruptibleMany(
                 dp.parse(
-                  new InputSourceDataInputStream(data),
-                  infosetOutputter
+                  Support.getInputSourceDataInputStream(data),
+                  Support.getInfosetOutputter(infosetFormat, os)
                 )
                 // WARNING: parse doesn't close the OutputStream, so closed 
below
               ).flatTap { res =>
@@ -123,7 +109,9 @@ object Parse {
                   dapEvents
                     .send(
                       Parse.Event.Error(
-                        res.getDiagnostics.toList
+                        Support
+                          .parseDiagnosticList(res.getDiagnostics)
+                          .toList
                           .map(d => d.toString)
                           .mkString("\n")
                       )
@@ -131,7 +119,7 @@ object Parse {
                     .void
                 } else IO.unit
               }.ensureOr { res =>
-                new Parse.Exception(res.getDiagnostics.toList)
+                new 
Parse.Exception(Support.parseDiagnosticList(res.getDiagnostics).toList)
               }(res => !res.isError())
                 .flatMap { parseResult =>
                   val loc = parseResult.location()
@@ -167,9 +155,9 @@ object Parse {
 
   class Debugee(
       schema: DAPodil.Source,
-      data: DAPodil.Source,
+      dataIn: DAPodil.Source,
       outputData: Signal[IO, DAPodil.Data],
-      events: Stream[IO, Events.DebugEvent],
+      eventsIn: Stream[IO, Events.DebugEvent],
       breakpoints: Breakpoints,
       control: Control,
       parseEvents: Channel[IO, Parse.Event]
@@ -178,13 +166,13 @@ object Parse {
       outputData
 
     def events(): Stream[IO, Events.DebugEvent] =
-      events
+      eventsIn
 
     /** We return only the "static" sources of the schema and data file, and 
notify the debugger of additional sources,
       * if any, via source change events, which only subsequently fetch the 
content directly via `sourceContent`.
       */
     def sources(): IO[List[DAPodil.Source]] =
-      IO.pure(List(schema, data))
+      IO.pure(List(schema, dataIn))
 
     def sourceContent(ref: DAPodil.Source.Ref): 
IO[Option[DAPodil.Source.Content]] =
       IO.pure(None) // no additional source content available; infoset and 
data position info sent as custom events
@@ -1054,14 +1042,14 @@ object Parse {
         schemaLocation: SchemaFileLocation,
         pointsOfUncertainty: List[PointOfUncertainty],
         delimiterStack: List[Delimiter],
-        diagnostics: List[org.apache.daffodil.lib.api.Diagnostic],
+        diagnostics: List[Diagnostic],
         infoset: Option[InfosetEvent]
     ) extends Event {
       // PState is mutable, so we copy all the information we might need 
downstream.
       def this(pstate: PState, infoset: Option[InfosetEvent]) =
         this(
           pstate.copyStateForDebugger,
-          pstate.currentNode.toOption.map(element => 
ElementName(element.name)),
+          Convert.daffodilMaybeToOption(pstate.currentNode).map(node => 
ElementName(node.asElement.name)),
           pstate.schemaFileLocation,
           pstate.pointsOfUncertainty.iterator.toList.map(mark =>
             PointOfUncertainty(
@@ -1074,7 +1062,7 @@ object Parse {
           pstate.mpstate.delimiters.toList.zipWithIndex.map { case (delimiter, 
i) =>
             Delimiter(if (i < pstate.mpstate.delimitersLocalIndexStack.top) 
"remote" else "local", delimiter)
           },
-          pstate.diagnostics,
+          pstate.diagnostics.toList,
           infoset
         )
     }
@@ -1168,7 +1156,7 @@ object Parse {
         }
     }
 
-    case class BuildInfo(version: String, daffodilVersion: String, 
scalaVersion: String)
+    case class BuildInfo(version: String, scalaVersion: String)
 
     def apply(launchArgs: Debugee.LaunchArgs): ConfigEvent =
       ConfigEvent(
@@ -1201,7 +1189,6 @@ object Parse {
         },
         BuildInfo(
           DAPBuildInfo.version,
-          DAPBuildInfo.daffodilVersion,
           DAPBuildInfo.scalaVersion
         )
       )
@@ -1222,14 +1209,10 @@ object Parse {
 
     private def infosetToString(format: String, ie: DIElement): String = {
       val bos = new java.io.ByteArrayOutputStream()
-      val infosetOutputter = format match {
-        case "xml"  => new XMLTextInfosetOutputter(bos, true)
-        case "json" => new JsonInfosetOutputter(bos, true)
-      }
 
       val iw = InfosetWalker(
         ie.asInstanceOf[DIElement],
-        infosetOutputter,
+        Support.getInfosetOutputter(format, bos),
         walkHidden = false,
         ignoreBlocks = true,
         releaseUnneededInfoset = false
diff --git 
a/debugger/src/main/scala/org.apache.daffodil.debugger.dap/Utils.scala 
b/debugger/src/main/scala/org.apache.daffodil.debugger.dap/Utils.scala
index 3dced22..8d27225 100644
--- a/debugger/src/main/scala/org.apache.daffodil.debugger.dap/Utils.scala
+++ b/debugger/src/main/scala/org.apache.daffodil.debugger.dap/Utils.scala
@@ -17,7 +17,7 @@
 
 package org.apache.daffodil.debugger.dap
 
-import java.io.RandomAccessFile
+import java.io._
 import java.nio.ByteBuffer
 import java.nio.file.Path
 import org.apache.daffodil.io.DataDumper
diff --git a/debugger/src/main/scala/org.apache.daffodil.tdml/TDML.scala 
b/debugger/src/main/scala/org.apache.daffodil.tdml/TDML.scala
index e450b39..66366cd 100644
--- a/debugger/src/main/scala/org.apache.daffodil.tdml/TDML.scala
+++ b/debugger/src/main/scala/org.apache.daffodil.tdml/TDML.scala
@@ -23,8 +23,8 @@ import javax.xml.bind.JAXBContext
 import javax.xml.bind.JAXBElement
 import javax.xml.bind.Marshaller
 import javax.xml.namespace.QName
-import scala.jdk.CollectionConverters._
 import org.apache.daffodil.lib.xml.XMLUtils
+import scala.jdk.CollectionConverters._
 
 object TDML {
   def getDefaultTDMLTestCaseDescription(): String =
@@ -189,14 +189,14 @@ object TDML {
   def execute(tdmlName: String, tdmlPath: String): Option[(Path, Path)] = {
     val basePath = Paths.get(tdmlPath).toAbsolutePath().getParent().toString()
 
-    val testCaseList = JAXBContext
-      .newInstance(classOf[TestSuite])
-      .createUnmarshaller()
-      .unmarshal(new File(tdmlPath))
-      .asInstanceOf[TestSuite]
-      .getTutorialOrParserTestCaseOrDefineSchema()
-      .asScala
-      .toList
+    val testCaseList =
+      JAXBContext
+        .newInstance(classOf[TestSuite])
+        .createUnmarshaller()
+        .unmarshal(new File(tdmlPath))
+        .asInstanceOf[TestSuite]
+        .getTutorialOrParserTestCaseOrDefineSchema()
+        .asScala
 
     testCaseList.collectFirst {
       case (ptc: ParserTestCaseType) if ptc.getName() == tdmlName =>
diff --git a/debugger/src/templates/bash-template 
b/debugger/src/templates/bash-template
index dbf7ba1..fb7d01b 100644
--- a/debugger/src/templates/bash-template
+++ b/debugger/src/templates/bash-template
@@ -212,6 +212,8 @@ process_args () {
 
      -java-home) require_arg path "$1" "$2" && jre=`eval echo $2` && 
java_cmd="$jre/bin/java" && shift 2 ;;
 
+     --daffodilPath) daffodil_path="$2" && shift 2 ;;
+
  -D*|-agentlib*|-XX*) addJava "$1" && shift ;;
                  -J*) addJava "${1:2}" && shift ;;
                    *) addResidual "$1" && shift ;;
@@ -263,10 +265,18 @@ run() {
     java_opts="${JAVA_OPTS}"
   fi
 
+  # add daffodil jars to classpath
+  if [[ -z $daffodil_path ]]; then
+    echo "No Daffodil path provided using the --daffodilPath flag. Exiting as 
debugger can't run without the Daffodil path"
+    exit 1
+  fi
+
+  daffodil_lib_dir=$(echo $daffodil_path/lib | sed 's/ /\ /g')
+  daffodil_jars="$daffodil_lib_dir/"'*'
+  new_classpath="$app_classpath:$daffodil_jars"
+
   if [[ "$DAFFODIL_DEBUG_CLASSPATH" != "" ]]; then
-    new_classpath="$app_classpath:$DAFFODIL_DEBUG_CLASSPATH"
-  else
-    new_classpath="$app_classpath"
+    new_classpath="$new_classpath:$DAFFODIL_DEBUG_CLASSPATH"
   fi
 
   # run sbt
@@ -376,4 +386,3 @@ declare java_cmd=$(get_java_cmd)
 [[ -f "$script_conf_file" ]] && set -- $(loadConfigFile "$script_conf_file") 
"$@"
 
 run "$@"
-stupid thing
diff --git a/debugger/src/templates/bat-template 
b/debugger/src/templates/bat-template
index 1c21cf7..403f2f2 100644
--- a/debugger/src/templates/bat-template
+++ b/debugger/src/templates/bat-template
@@ -114,12 +114,19 @@ if defined CUSTOM_MAIN_CLASS (
     set MAIN_CLASS=!APP_MAIN_CLASS!
 )
 
-if defined DAFFODIL_DEBUG_CLASSPATH (
-    set NEW_CLASSPATH="%APP_CLASSPATH%;%DAFFODIL_DEBUG_CLASSPATH%"
-) else (
-    set NEW_CLASSPATH="%APP_CLASSPATH%"
+set NEW_CLASSPATH=%APP_CLASSPATH%
+
+rem ensure daffodil path provided
+if not defined DAFFODIL_PATH (
+  echo "No Daffodil path provided using the --daffodilPath flag. Exiting as 
debugger can't run without the Daffodil path"
+  exit 1
 )
 
+set NEW_CLASSPATH=%NEW_CLASSPATH%;!DAFFODIL_PATH!\lib\*
+
+if defined DAFFODIL_DEBUG_CLASSPATH (
+    set NEW_CLASSPATH="%NEW_CLASSPATH%;%DAFFODIL_DEBUG_CLASSPATH%"
+)
 
 rem Call the application and pass all arguments unchanged.
 "%_JAVACMD%" !_JAVA_OPTS! !DAFFODIL_DEBUGGER_OPTS! -cp "%NEW_CLASSPATH%" 
%MAIN_CLASS% !_APP_ARGS!
@@ -194,7 +201,12 @@ rem Processes incoming arguments and places them in 
appropriate global variables
       call set CUSTOM_MAIN_CLASS=%%2
       shift
     ) else (
-      set _APP_ARGS=!_APP_ARGS! !_PARAM1!
+      if "!_TEST_PARAM!"=="--daffodilPath" (
+        call set DAFFODIL_PATH=%%2
+        shift
+      ) else (
+        set _APP_ARGS=!_APP_ARGS! !_PARAM1!
+      )
     )
   )
   shift
diff --git 
a/debugger/src/test/scala/org.apache.daffodil.debugger/ParseSuite.scala 
b/debugger/src/test/scala/org.apache.daffodil.debugger/ParseSuite.scala
index 1caabb0..843f3d4 100644
--- a/debugger/src/test/scala/org.apache.daffodil.debugger/ParseSuite.scala
+++ b/debugger/src/test/scala/org.apache.daffodil.debugger/ParseSuite.scala
@@ -25,8 +25,8 @@ class ParseSuite extends FunSuite {
   val request = "launch"
   val launchType = "dfdl"
   val schema = new JsonObject()
-  var schemaPath = 
Paths.get("./src/test/data/emptySchema.dfdl.xsd").toAbsolutePath().toString()
-  var data = 
Paths.get("./src/test/data/emptyData.xml").toAbsolutePath().toString()
+  var schemaPath = 
Paths.get("../../../debugger/src/test/data/emptySchema.dfdl.xsd").toAbsolutePath().toString()
+  var data = 
Paths.get("../../../debugger/src/test/data/emptyData.xml").toAbsolutePath().toString()
   val debugServer = 4711
   val infosetFormat = "xml"
   var infosetOutputType = "none"
@@ -46,8 +46,8 @@ class ParseSuite extends FunSuite {
   var testTDMLObject = new JsonObject()
 
   override def beforeEach(context: BeforeEach): Unit = {
-    schemaPath = 
Paths.get("./src/test/data/emptySchema.dfdl.xsd").toAbsolutePath().toString()
-    data = 
Paths.get("./src/test/data/emptyData.xml").toAbsolutePath().toString()
+    schemaPath = 
Paths.get("../../../debugger/src/test/data/emptySchema.dfdl.xsd").toAbsolutePath().toString()
+    data = 
Paths.get("../../../debugger/src/test/data/emptyData.xml").toAbsolutePath().toString()
     infosetOutputType = "none"
     infosetOutputPath = "testPath/infoset.xml"
     tdmlAction = ""
diff --git a/package.json b/package.json
index 443010e..2dc2b17 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,6 @@
   "displayName": "Apache Daffodil™ Extension for Visual Studio Code",
   "description": "Apache Daffodil™ Extension for Visual Studio Code providing 
DFDL syntax highlighting, DFDL code completion, DFDL schema debugging, and data 
editor",
   "version": "1.4.2-SNAPSHOT",
-  "daffodilVersion": "3.11.0",
   "publisher": "asf",
   "author": "Apache Daffodil",
   "license": "Apache-2.0",
@@ -30,7 +29,7 @@
     "check-license-compatibility": "run-func build/yarn-scripts.ts 
checkLicenseCompatibility",
     "clean": "yarn nodeclean && yarn scalaclean",
     "precompile": "yarn nodeclean && yarn gen-version-ts && yarn svelte:build 
&& yarn compile:tdmlEditorJS",
-    "compile": "tsc -p ./ && yarn sbt",
+    "compile": "tsc -p ./ && yarn sbt && yarn vite:dev",
     "lint": "yarn prettier src -c",
     "lint:fix": "yarn prettier src -w",
     "prewatch": "yarn gen-version-ts && yarn sbt",
@@ -44,7 +43,7 @@
     "pretest": "yarn compile && yarn vite:dev",
     "test": "sbt test && yarn test:svelte && node ./out/tests/runTest.js",
     "test:svelte": "mocha -r ts-node/register ./src/svelte/tests/**/*.test.ts",
-    "sbt": "sbt debugger/Universal/packageBin",
+    "sbt": "sbt Universal/packageBin",
     "svelte:check": "svelte-check --tsconfig ./src/svelte/tsconfig.json",
     "svelte:build": "cd src/svelte && vite build --config ./vite.config.mjs 
--mode production --emptyOutDir",
     "update-version": "run-func build/yarn-scripts.ts updateVersion",
@@ -685,8 +684,18 @@
               },
               "dfdlDebugger": {
                 "type": "object",
-                "description": "Configuration for debugger. Settings are 
logging (level and file)",
+                "description": "Configuration for debugger. Settings are 
daffodilVersion, timeout and logging (level and file)",
                 "properties": {
+                  "daffodilVersion": {
+                    "type": "string",
+                    "default": "3.11.0",
+                    "description": "Daffodil version to use."
+                  },
+                  "timeout": {
+                    "type": "string",
+                    "default": "10s",
+                    "description": "Debugger connection timeout in seconds(s), 
minutes(m) or hours(h)"
+                  },
                   "logging": {
                     "type": "object",
                     "description": "DFDL Debugger Logging Configuration",
@@ -716,6 +725,8 @@
                   }
                 },
                 "default": {
+                  "daffodilVersion": "3.11.0",
+                  "timeout": "10s",
                   "logging": {
                     "level": "INFO",
                     "file": "${workspaceFolder}/daffodil-debugger.log"
@@ -760,6 +771,8 @@
               }
             },
             "dfdlDebugger": {
+              "daffodilVersion": "3.11.0",
+              "timeout": "10s",
               "logging": {
                 "level": "INFO",
                 "file": "${workspaceFolder}/daffodil-debugger.log"
@@ -806,6 +819,8 @@
                 }
               },
               "dfdlDebugger": {
+                "daffodilVersion": "3.11.0",
+                "timeout": "10s",
                 "logging": {
                   "level": "INFO",
                   "file": "^\"\\${workspaceFolder}/daffodil-debugger.log\""
@@ -926,8 +941,10 @@
           },
           "dfdlDebugger": {
             "type": "object",
-            "description": "Configuration for debugger. Settings are logging 
(level and file)",
+            "description": "Configuration for debugger. Settings are 
daffodilVersion, timeout and logging (level and file)",
             "default": {
+              "daffodilVersion": "3.11.0",
+              "timeout": "10s",
               "logging": {
                 "level": "INFO",
                 "file": "${workspaceFolder}/daffodil-debugger.log"
diff --git a/project/plugins.sbt b/project/plugins.sbt
index bd53581..d43fc8c 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -21,3 +21,4 @@ addSbtPlugin("org.typelevel" % "sbt-tpolecat" % "0.5.2")
 addSbtPlugin("org.musigma" % "sbt-rat" % "0.7.0")
 addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.5")
 addSbtPlugin("org.scala-sbt.plugins" % "sbt-xjc" % "0.10")
+addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.11.0")
diff --git a/src/adapter/activateDaffodilDebug.ts 
b/src/adapter/activateDaffodilDebug.ts
index a655f4b..02ef6cf 100644
--- a/src/adapter/activateDaffodilDebug.ts
+++ b/src/adapter/activateDaffodilDebug.ts
@@ -8,13 +8,13 @@
 import * as fs from 'fs'
 import * as path from 'path'
 import * as vscode from 'vscode'
-import * as infoset from 'infoset'
-import * as launchWizard from 'launchWizard'
-import * as dfdlLang from 'language/dfdl'
-import * as dfdlExt from 'language/semantics/dfdlExt'
-import * as dataEditClient from 'dataEditor'
-import * as tdmlEditor from 'tdmlEditor'
-import * as rootCompletion from 'rootCompletion'
+import * as infoset from '../infoset'
+import * as launchWizard from '../launchWizard/launchWizard'
+import * as dfdlLang from '../language/dfdl'
+import * as dfdlExt from '../language/semantics/dfdlExt'
+import * as dataEditClient from '../dataEditor'
+import * as tdmlEditor from '../tdmlEditor'
+import * as rootCompletion from '../rootCompletion'
 import { tmpdir } from 'os'
 
 import {
@@ -34,11 +34,11 @@ import {
   getTmpTDMLFilePath,
   copyTestCase,
   TMP_TDML_FILENAME,
-} from 'tdmlEditor/utilities/tdmlXmlUtils'
+} from '../tdmlEditor/utilities/tdmlXmlUtils'
 import xmlFormat from 'xml-formatter'
 import { CommandsProvider } from '../views/commands'
 import * as daffodilDebugErrors from './daffodilDebugErrors'
-import { TDMLProvider } from 'tdmlEditor/TDMLProvider'
+import { TDMLProvider } from '../tdmlEditor/TDMLProvider'
 
 export const outputChannel: vscode.OutputChannel =
   vscode.window.createOutputChannel('Daffodil')
@@ -217,6 +217,9 @@ export function activateDaffodilDebug(
 ) {
   setupViews(context)
 
+  context.subscriptions.push(
+    vscode.commands.registerCommand('getContext', () => context)
+  )
   context.subscriptions.push(
     vscode.commands.registerCommand(
       'extension.dfdl-debug.runEditorContents',
diff --git a/src/adapter/daffodilDebugErrors.ts 
b/src/adapter/daffodilDebugErrors.ts
index 66c160d..fe5d9c5 100644
--- a/src/adapter/daffodilDebugErrors.ts
+++ b/src/adapter/daffodilDebugErrors.ts
@@ -24,6 +24,7 @@ import {
   extractDaffodilEvent,
   parseErrorEvent,
 } from '../daffodilDebugger/daffodil'
+import { displayModalError } from '../utils'
 
 export const activate = () =>
   vscode.debug.onDidReceiveDebugSessionCustomEvent(async (e) => {
@@ -51,6 +52,6 @@ export const activate = () =>
     if (message !== '') {
       // Make the message have two line breaks between each line to make the 
message look better.
       message = message.replaceAll('\n', '\n\n')
-      vscode.window.showErrorMessage(message, { modal: true })
+      displayModalError(message)
     }
   })
diff --git a/src/adapter/daffodilEvent.ts b/src/adapter/daffodilEvent.ts
index 6990f7e..97a7aaa 100644
--- a/src/adapter/daffodilEvent.ts
+++ b/src/adapter/daffodilEvent.ts
@@ -19,7 +19,7 @@ import * as vscode from 'vscode'
 import * as fs from 'fs'
 
 import * as daf from '../daffodilDebugger'
-import { ensureFile, tmpFile } from '../utils'
+import { displayModalError, ensureFile, tmpFile } from '../utils'
 import { outputChannel } from './activateDaffodilDebug'
 
 export function handleDebugEvent(e: vscode.DebugSessionCustomEvent) {
@@ -33,7 +33,7 @@ export function handleDebugEvent(e: 
vscode.DebugSessionCustomEvent) {
     // this allows for any error event to be caught in this case
     case e.event.startsWith('daffodil.error') ? e.event : '':
       if (!e.body.message.startsWith('Schema Definition Error:')) {
-        vscode.window.showErrorMessage(e.body.message, { modal: true })
+        displayModalError(e.body.message)
       }
       outputChannel.appendLine(e.body.message)
       outputChannel.show(true)
diff --git a/src/classes/dfdlDebugger.ts b/src/classes/dfdlDebugger.ts
index a666ff9..5327d70 100644
--- a/src/classes/dfdlDebugger.ts
+++ b/src/classes/dfdlDebugger.ts
@@ -18,5 +18,7 @@
 import { LoggingConfig } from '../classes/loggingConfig'
 
 export interface DFDLDebugger {
+  daffodilVersion: string
+  timeout: string
   logging: LoggingConfig
 }
diff --git a/src/daffodilDebugger/daffodil.ts b/src/daffodilDebugger/daffodil.ts
index 132094d..3baa498 100644
--- a/src/daffodilDebugger/daffodil.ts
+++ b/src/daffodilDebugger/daffodil.ts
@@ -15,8 +15,6 @@
  * limitations under the License.
  */
 
-import * as fs from 'fs'
-import { parse as jsoncParse } from 'jsonc-parser'
 import { DebugSession, DebugSessionCustomEvent, debug } from 'vscode'
 import { SchemaData } from '../classes/schemaData'
 
@@ -71,10 +69,6 @@ export interface BuildInfo {
   scalaVersion: string
 }
 
-export function getDaffodilVersion(filePath: fs.PathLike) {
-  return jsoncParse(fs.readFileSync(filePath).toString())['daffodilVersion']
-}
-
 export interface IDaffodilEvent {
   readonly type: DaffodilEventType
   readonly body: DaffodilDataType
diff --git a/src/daffodilDebugger/daffodilJars.ts 
b/src/daffodilDebugger/daffodilJars.ts
new file mode 100644
index 0000000..f6c8d8c
--- /dev/null
+++ b/src/daffodilDebugger/daffodilJars.ts
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as path from 'path'
+import * as fs from 'fs'
+import * as vscode from 'vscode'
+import { outputChannel } from '../adapter/activateDaffodilDebug'
+import { downloadAndExtract } from '../utils'
+
+/**
+ * The daffodil debugger doesn't bundle any of daffodil jars because we want 
to support
+ * many versions of daffodil and do not want to bundle them all. The CLI zip 
is an official
+ * daffodil release that contains all the daffodil jars (and their transitive 
dependencies)
+ * that the debugger needs for a particular Daffodil version. The debugger 
doesn't need all
+ * the jars that it bundles (e.g. daffodil-cli.jar) but those will just be 
ignored.
+ */
+export async function checkIfDaffodilJarsNeeded(
+  daffodilVersion: string
+): Promise<string> {
+  const context = (await vscode.commands.executeCommand(
+    'getContext'
+  )) as vscode.ExtensionContext
+
+  /**
+   * Global storage paths:
+   *  Mac: /Users/<username>/Library/Application 
Support/Code/User/globalStorage/asf.apache-daffodil-vscode
+   *  Windows: %APPDATA%\Code\User\globalStorage\asf.apache-daffodil-vscode
+   *  Linux: 
/home/<username>/.config/Code/User/globalStorage/asf.apache-daffodil-vscode
+   */
+
+  const destFolder = path.join(context.globalStorageUri.fsPath, 'daffodil')
+  const binFolder = path.join(
+    destFolder,
+    `apache-daffodil-${daffodilVersion}-bin`
+  )
+
+  if (!fs.existsSync(binFolder)) {
+    const url = 
`https://www.apache.org/dyn/closer.lua/download/daffodil/${daffodilVersion}/bin/apache-daffodil-${daffodilVersion}-bin.zip`
+    try {
+      await downloadAndExtract('Daffodil CLI JARs', url, destFolder)
+    } catch (err) {
+      console.error(err)
+    }
+  } else {
+    outputChannel.appendLine(
+      `[INFO] Daffodil CLI JARs already exists. Skipping download.`
+    )
+  }
+
+  return binFolder
+}
diff --git a/src/daffodilDebugger/debugger.ts b/src/daffodilDebugger/debugger.ts
index 02bcddd..ca95f8d 100644
--- a/src/daffodilDebugger/debugger.ts
+++ b/src/daffodilDebugger/debugger.ts
@@ -210,13 +210,19 @@ export async function getDebugger(
     setCurrentConfig(config)
 
     if (!config.useExistingServer) {
-      await runDebugger(
+      let newDebugger = await runDebugger(
         context.asAbsolutePath('./'),
         daffodilDebugClasspath,
         context.asAbsolutePath('./package.json'),
         config.debugServer,
         config.dfdlDebugger
       )
+
+      // This ensures the extension doesn't try to connect to the debugger if 
the debugger
+      // wasn't created
+      if (!newDebugger) {
+        return await stopDebugging().then((_) => undefined)
+      }
     }
   }
 }
diff --git a/src/daffodilDebugger/utils.ts b/src/daffodilDebugger/utils.ts
index 68aad43..b65620c 100644
--- a/src/daffodilDebugger/utils.ts
+++ b/src/daffodilDebugger/utils.ts
@@ -23,14 +23,11 @@ import { IJavaHomeInfo } from 
'@viperproject/locate-java-home/js/es5/lib/interfa
 import * as semver from 'semver'
 import { LIB_VERSION } from '../version'
 import { deactivate } from '../adapter/extension'
-import { getDaffodilVersion } from './daffodil'
 import { Artifact } from '../classes/artifact'
 import { DFDLDebugger } from '../classes/dfdlDebugger'
-import { osCheck, runScript, terminalName } from '../utils'
-
-export const daffodilVersion = (filePath: string): string => {
-  return getDaffodilVersion(filePath)
-}
+import { displayModalError, osCheck, runScript, terminalName } from '../utils'
+import { outputChannel } from '../adapter/activateDaffodilDebug'
+import { checkIfDaffodilJarsNeeded } from './daffodilJars'
 
 export const daffodilArtifact = (version: string): Artifact => {
   return new Artifact('daffodil-debugger', version, 'daffodil-debugger')
@@ -39,7 +36,12 @@ export const daffodilArtifact = (version: string): Artifact 
=> {
 export const stopDebugger = async (id: number | undefined = undefined) =>
   child_process.exec(osCheck(`taskkill /F /PID ${id}`, `kill -9 ${id}`))
 
-export const shellArgs = (port: number, isAtLeastJdk17: boolean) => {
+export const shellArgs = (
+  port: number,
+  timeout: string,
+  daffodilPath: string,
+  isAtLeastJdk17: boolean
+) => {
   // Workaround: certain reflection (used by JAXB) isn't allowed by default in 
JDK 17:
   //   
https://docs.oracle.com/en/java/javase/17/migrate/migrating-jdk-8-later-jdk-releases.html#GUID-7BB28E4D-99B3-4078-BDC4-FC24180CE82B
   const extraArgs = isAtLeastJdk17
@@ -48,7 +50,22 @@ export const shellArgs = (port: number, isAtLeastJdk17: 
boolean) => {
         ['-J--add-opens', '-Jjava.base/java.lang=ALL-UNNAMED']
       )
     : []
-  return ['--listenPort', `${port}`].concat(extraArgs)
+  return [
+    '--listenPort',
+    `${port}`,
+    '--listenTimeout',
+    `${timeout}`,
+    '--daffodilPath',
+    `"${daffodilPath}"`,
+  ].concat(extraArgs)
+}
+
+export async function getScalaVersion(
+  daffodilVersion: string
+): Promise<string> {
+  if (semver.satisfies(daffodilVersion, '>=4.0.0')) return '3'
+  else if (semver.satisfies(daffodilVersion, '>=3.11.0')) return '2.13'
+  else return '2.12'
 }
 
 export async function runDebugger(
@@ -58,34 +75,59 @@ export async function runDebugger(
   serverPort: number,
   dfdlDebugger: DFDLDebugger,
   createTerminal: boolean = false
-): Promise<vscode.Terminal> {
-  const dfdlVersion = daffodilVersion(filePath)
+): Promise<vscode.Terminal | undefined> {
+  if (
+    !dfdlDebugger.timeout.endsWith('s') &&
+    !dfdlDebugger.timeout.endsWith('m') &&
+    !dfdlDebugger.timeout.endsWith('s')
+  ) {
+    vscode.window.showErrorMessage(
+      `DFDL Debugger Timeout ${dfdlDebugger.timeout} does not end in either s 
for seconds, m for minutes or h for hours.
+      Appending s to end of timeout string.`
+    )
+    dfdlDebugger.timeout = `${dfdlDebugger.timeout}s`
+  }
+
+  // Locates the $JAVA_HOME, or if not defined, the highest version available.
+  const javaHome: IJavaHomeInfo | undefined = await getJavaHome()
+
+  const isAtLeastJdk17: boolean = parseFloat(javaHome?.version ?? '0') >= 17
+  outputChannel.appendLine(
+    `[DEBUG] Choosing java home at ${javaHome?.path}, version 
${javaHome?.version}, is at least JDK 17: ${isAtLeastJdk17}`
+  )
+
+  let dfdlVersion = dfdlDebugger.daffodilVersion
+  let scalaVersion = await getScalaVersion(dfdlDebugger.daffodilVersion)
+
+  if (scalaVersion == '') {
+    displayModalError(
+      `Daffodil Version ${dfdlDebugger.daffodilVersion} does not satisfy any 
version requirements`
+    )
+    return undefined
+  }
+
+  outputChannel.appendLine(
+    `[INFO] Using Scala ${scalaVersion} + Daffodil ${dfdlVersion} debugger`
+  )
+
+  /**
+   * The Scala 3 with Daffodil >= 4.0.0 debugger can only be ran on JDK 17 or 
greater. So if the java version
+   * being used is less than 17, fallback to the Scala 2.13 and Daffodil 
3.11.0 debugger and notify the user.
+   */
+  if (semver.satisfies(dfdlVersion, '>=4.0.0') && !isAtLeastJdk17) {
+    displayModalError(`Daffodil Versions 4.0.0+ requires JDK >= 17`)
+    return undefined
+  }
+
+  // Download the daffodil CLI jars if needed
+  const daffodilPath = await checkIfDaffodilJarsNeeded(dfdlVersion)
+
   const artifact = daffodilArtifact(dfdlVersion)
   const scriptPath = path.join(
     rootPath,
-    `daffodil-debugger-${dfdlVersion}-${LIB_VERSION}`
-  )
-  // Locates the $JAVA_HOME, or if not defined, the highest version available.
-  const javaHome: IJavaHomeInfo | undefined = await new Promise(
-    (resolve, reject) => {
-      _locateJavaHome({ version: '>=1.8' }, (error, javaHomes) => {
-        console.log(`detected java homes: ${JSON.stringify(javaHomes)}`)
-        javaHomes
-          ? resolve(
-              process.env.JAVA_HOME
-                ? javaHomes.find(
-                    (home, idx, obj) => home.path == process.env.JAVA_HOME
-                  )
-                : latestJdk(javaHomes)
-            )
-          : undefined
-      })
-    }
-  )
-  const isAtLeastJdk17: boolean = parseFloat(javaHome?.version ?? '0') >= 17
-  console.log(
-    `choosing java home at ${javaHome?.path}, version ${javaHome?.version}, is 
at least JDK 17: ${isAtLeastJdk17}`
+    `daffodil-debugger-${scalaVersion}-${LIB_VERSION}`
   )
+
   // The backend's launch script honors $JAVA_HOME, but if not set it assumes 
java is available on the path.
   const env = javaHome
     ? {
@@ -108,12 +150,30 @@ export async function runDebugger(
     scriptPath,
     artifact.scriptName,
     createTerminal,
-    shellArgs(serverPort, isAtLeastJdk17),
+    shellArgs(serverPort, dfdlDebugger.timeout, daffodilPath, isAtLeastJdk17),
     env,
     'daffodil'
   )
 }
 
+export const getJavaHome = async (): Promise<IJavaHomeInfo | undefined> =>
+  await new Promise((resolve, reject) => {
+    _locateJavaHome({ version: '>=1.8' }, (error, javaHomes) => {
+      outputChannel.appendLine(
+        `[DEBUG] Detected java homes: ${JSON.stringify(javaHomes)}`
+      )
+      javaHomes
+        ? resolve(
+            process.env.JAVA_HOME
+              ? javaHomes.find(
+                  (home, idx, obj) => home.path == process.env.JAVA_HOME
+                )
+              : latestJdk(javaHomes)
+          )
+        : undefined
+    })
+  })
+
 function latestJdk(jdkHomes: IJavaHomeInfo[]): IJavaHomeInfo | undefined {
   if (jdkHomes.length > 0) {
     return jdkHomes.sort((a, b) => {
diff --git a/src/launchWizard/launchWizard.ts b/src/launchWizard/launchWizard.ts
index aa87ede..4ca6753 100644
--- a/src/launchWizard/launchWizard.ts
+++ b/src/launchWizard/launchWizard.ts
@@ -535,10 +535,10 @@ class LaunchWizard {
     })
 
     let dfdlDebugger: DFDLDebugger = defaultValues.dfdlDebugger
+
     let debuggerLogLevelSelect = ''
     let debuggerLogLevelTypes = getAllowedLogLevels()
     let debuggerLogLevel = dfdlDebugger.logging.level
-
     debuggerLogLevelTypes.forEach((type) => {
       if (type === debuggerLogLevel) {
         debuggerLogLevelSelect += `<option selected 
value="${type}">${type}</option>`
@@ -646,6 +646,12 @@ class LaunchWizard {
         <p class="setting-description">Port debug server running on.</p>
         <input class="file-input" value="${defaultValues.debugServer}" 
id="debugServer"/>
 
+        <p id="dfdlDaffodilVersionLabel" style="margin-top: 10px;" 
class="setting-description">Version (Daffodil Version):</p>
+        <input class="file-input" value="${dfdlDebugger.daffodilVersion}" 
id="dfdlDaffodilVersion">
+
+        <p id="dfdlDebuggerTimeoutLabel" style="margin-top: 10px;" 
class="setting-description">Timeout (should end with s, m or h):</p>
+        <input class="file-input" value="${dfdlDebugger.timeout}" 
id="dfdlDebuggerTimeout">
+
         <p id="dfdlDebuggerLogFileLabel" style="margin-top: 10px;" 
class="setting-description">Log File:</p>
         <input class="file-input" value="${dfdlDebugger.logging.file}" 
id="dfdlDebuggerLogFile">
         
diff --git a/src/launchWizard/script.js b/src/launchWizard/script.js
index 2704fde..c7356db 100644
--- a/src/launchWizard/script.js
+++ b/src/launchWizard/script.js
@@ -72,6 +72,12 @@ function getConfigValues() {
   )
   const dataEditorLogFile = document.getElementById('dataEditorLogFile').value
   const dataEditorLogLevel = 
document.getElementById('dataEditorLogLevel').value
+  const dfdlDaffodilVersion = document.getElementById(
+    'dfdlDaffodilVersion'
+  ).value
+  const dfdlDebuggerTimeout = document.getElementById(
+    'dfdlDebuggerTimeout'
+  ).value
   const dfdlDebuggerLogFile = document.getElementById(
     'dfdlDebuggerLogFile'
   ).value
@@ -109,6 +115,8 @@ function getConfigValues() {
     dataEditorPort,
     dataEditorLogFile,
     dataEditorLogLevel,
+    dfdlDaffodilVersion,
+    dfdlDebuggerTimeout,
     dfdlDebuggerLogFile,
     dfdlDebuggerLogLevel,
     daffodilDebugClasspath,
@@ -306,6 +314,8 @@ function save() {
           },
         },
         dfdlDebugger: {
+          daffodilVersion: configValues.dfdlDaffodilVersion,
+          timeout: configValues.dfdlDebuggerTimeout,
           logging: {
             file: configValues.dfdlDebuggerLogFile,
             level: configValues.dfdlDebuggerLogLevel,
@@ -380,6 +390,8 @@ function copyConfig() {
           },
         },
         dfdlDebugger: {
+          daffodilVersion: configValues.dfdlDaffodilVersion,
+          timeout: configValues.dfdlDebuggerTimeout,
           logging: {
             file: configValues.dfdlDebuggerLogFile,
             level: configValues.dfdlDebuggerLogLevel,
@@ -444,6 +456,10 @@ async function updateConfigValues(config) {
   document.getElementById('dataEditorLogLevel').value =
     config.dataEditor.logging.level
 
+  document.getElementById('dfdlDaffodilVersion').value =
+    config.dfdlDebugger.daffodilVersion
+  document.getElementById('dfdlDebuggerTimeout').value =
+    config.dfdlDebugger.timeout
   document.getElementById('dfdlDebuggerLogFile').value =
     config.dfdlDebugger.logging.file
   document.getElementById('dfdlDebuggerLogLevel').value =
diff --git a/src/tests/suite/daffodil.test.ts b/src/tests/suite/daffodil.test.ts
index 09019cc..f485192 100644
--- a/src/tests/suite/daffodil.test.ts
+++ b/src/tests/suite/daffodil.test.ts
@@ -18,27 +18,13 @@
 import * as vscode from 'vscode'
 import * as assert from 'assert'
 import * as daffodil from '../../daffodilDebugger'
-import * as fs from 'fs'
 import * as path from 'path'
 import { Artifact } from '../../classes/artifact'
 import { LIB_VERSION } from '../../version'
-import { before, after } from 'mocha'
-import { PROJECT_ROOT, TDML_PATH, TEST_SCHEMA } from './common'
+import { TDML_PATH, TEST_SCHEMA } from './common'
 import { osCheck } from '../../utils'
 
 suite('Daffodfil', () => {
-  const packageFile = path.join(PROJECT_ROOT, 'package-test.json')
-
-  // Create test package.json before anything else happens
-  before(() => {
-    fs.writeFileSync(packageFile, JSON.stringify({ daffodilVersion: '0.0.0' }))
-  })
-
-  // Delete test package.json after all tests are done
-  after(() => {
-    fs.unlinkSync(packageFile)
-  })
-
   // suite to test all functions work properly
   suite('interfaces', () => {
     test('DaffodilData functions properly', () => {
@@ -166,72 +152,67 @@ suite('Daffodfil', () => {
     })
   })
 
-  suite('getDaffodilVersion', () => {
-    test('getDaffodilVersion returns same version as file', () => {
-      var daffodilVersion = daffodil.getDaffodilVersion(packageFile)
-      assert.strictEqual(daffodilVersion, '0.0.0')
-    })
-  })
-
-  suite('non-debug specific commands', () => {
-    const nonDebugSpecificCmds = [
-      'extension.dfdl-debug.debugEditorContents',
-      'extension.dfdl-debug.runEditorContents',
-      'extension.dfdl-debug.debugLastEditorContents',
-      'extension.dfdl-debug.executeTDML',
-    ]
-
-    // This breaks when the omega-edit tests run for some reason
-    // test('Available by default', () => {
-    //   nonDebugSpecificCmds.forEach(async (cmd) => {
-    //     assert.strictEqual(
-    //       (await vscode.commands.getCommands()).includes(cmd),
-    //       true
-    //     )
-    //   })
-    // })
-
-    test('Not available when inDebugMode', () => {
-      vscode.commands.executeCommand('setContext', 'inDebugMode', true)
-
-      nonDebugSpecificCmds.forEach(async (cmd) => {
-        assert.strictEqual(
-          (await vscode.commands.getCommands()).includes(cmd),
-          false
-        )
-      })
-    })
-  })
-
-  suite('debug specific commands', () => {
-    const debugSpecificCmds = [
-      'extension.dfdl-debug.toggleFormatting',
-      'infoset.display',
-      'infoset.diff',
-      'infoset.save',
-    ]
-
-    test('Not available by default', () => {
-      debugSpecificCmds.forEach(async (cmd) => {
-        assert.strictEqual(
-          (await vscode.commands.getCommands()).includes(cmd),
-          false
-        )
-      })
-    })
-
-    // This breaks when the omega-edit tests run for some reason
-    // test('Available when inDebugMode', () => {
-    //   vscode.commands.executeCommand('setContext', 'inDebugMode', true)
-
-    //   debugSpecificCmds.forEach(async (cmd) => {
-    //     assert.strictEqual(
-    //       (await vscode.commands.getCommands()).includes(cmd),
-    //       true
-    //     )
-    //   })
-    // })
-  })
+  // This breaks when the omega-edit tests run for some reason
+  // suite('non-debug specific commands', () => {
+  //   const nonDebugSpecificCmds = [
+  //     'extension.dfdl-debug.debugEditorContents',
+  //     'extension.dfdl-debug.runEditorContents',
+  //     'extension.dfdl-debug.debugLastEditorContents',
+  //     'extension.dfdl-debug.executeTDML',
+  //   ]
+
+  //   test('Available by default', () => {
+  //     nonDebugSpecificCmds.forEach(async (cmd) => {
+  //       assert.strictEqual(
+  //         (await vscode.commands.getCommands()).includes(cmd),
+  //         true
+  //       )
+  //     })
+  //   })
+
+  //   test('Not available when inDebugMode', () => {
+  //     vscode.commands.executeCommand('setContext', 'inDebugMode', true)
+
+  //     nonDebugSpecificCmds.forEach(async (cmd) => {
+  //       assert.strictEqual(
+  //         (await vscode.commands.getCommands()).includes(cmd),
+  //         false
+  //       )
+  //     })
+  //   })
+  // })
+
+  // This breaks when the omega-edit tests run for some reason
+  // suite('debug specific commands', () => {
+  //   const debugSpecificCmds = [
+  //     'extension.dfdl-debug.toggleFormatting',
+  //     'infoset.display',
+  //     'infoset.diff',
+  //     'infoset.save',
+  //   ]
+
+  //   // This breaks when the omega-edit tests run for some reason
+  //   test('Not available by default', () => {
+  //     debugSpecificCmds.forEach(async (cmd) => {
+  //       assert.strictEqual(
+  //         (await vscode.commands.getCommands()).includes(cmd),
+  //         false
+  //       )
+  //     })
+  //   })
+
+  //   // This breaks when the omega-edit tests run for some reason
+  //   test('Available when inDebugMode', () => {
+  //     vscode.commands.executeCommand('setContext', 'inDebugMode', true)
+
+  //     debugSpecificCmds.forEach(async (cmd) => {
+  //       assert.strictEqual(
+  //         (await vscode.commands.getCommands()).includes(cmd),
+  //         true
+  //       )
+  //     })
+  //   })
+  // })
 
   suite('getCommands', () => {
     test('getSchemaName file exists', async () => {
diff --git a/src/tests/suite/daffodilDebugger.test.ts 
b/src/tests/suite/daffodilDebugger.test.ts
index 26f6c5f..4969811 100644
--- a/src/tests/suite/daffodilDebugger.test.ts
+++ b/src/tests/suite/daffodilDebugger.test.ts
@@ -23,17 +23,27 @@ import * as os from 'os'
 import { PROJECT_ROOT, PACKAGE_PATH, TEST_SCHEMA } from './common'
 import { getConfig, killProcess } from '../../utils'
 import { runDebugger, stopDebugging } from '../../daffodilDebugger'
-import { before, after } from 'mocha'
+import { before, after, Suite, Test } from 'mocha'
 import { DFDLDebugger } from '../../classes/dfdlDebugger'
 import { DataEditorConfig } from '../../classes/dataEditor'
+import { getJavaHome } from '../../daffodilDebugger'
+import { delay } from '../../utils'
+import { TDMLConfig } from 'classes/tdmlConfig'
 
-// Not using the debug adapter like adapter.test.ts as it will not fully 
connect the debugger
-suite('Daffodil Debugger', () => {
+interface TestDebuggerConfig {
+  debugger: DFDLDebugger
+  port: number
+  infosetFormat: string
+}
+
+const debuggers: vscode.Terminal[] = []
+const dfdlDebuggerConfigs: Array<TestDebuggerConfig> = []
+
+suite('Daffodil Debugger', function (this: Suite) {
   // debugger options
   const DATA = path.join(PROJECT_ROOT, 'src/tests/data/test.txt')
   const XML_INFOSET_PATH = path.join(PROJECT_ROOT, 'testinfoset.xml')
   const JSON_INFOSET_PATH = path.join(PROJECT_ROOT, 'testinfoset.json')
-  const debuggers: vscode.Terminal[] = []
 
   const tdmlConf = {
     action: 'none',
@@ -47,41 +57,44 @@ suite('Daffodil Debugger', () => {
     },
   }
 
-  const dfdlDebuggers: Array<DFDLDebugger> = [
-    {
-      logging: {
-        level: 'INFO',
-        file: path.join(os.tmpdir(), 'yarn-test-daffodil-debugger-4711.log'),
-      },
-    },
-    {
-      logging: {
-        level: 'INFO',
-        file: path.join(os.tmpdir(), 'yarn-test-daffodil-debugger-4712.log'),
-      },
-    },
-  ]
-
   before(async () => {
-    debuggers.push(
-      await runDebugger(
+    await getDebuggerConfigs()
+
+    /**
+     * When testing locally running all debuggers before running the tests 
caused the tests to complete
+     * 1 second faster and the whole "yarn test" process completed 1 second 
faster than when running a
+     * single debugger instance right before the debugging was started. So 
currently sticking with this
+     * but if wanting to shift to running the instance right before starting 
the debug process it is not
+     * too dramatic of performance decrease and time increase.
+     */
+    for (var i = 0; i < dfdlDebuggerConfigs.length; i++) {
+      let newDebugger = await runDebugger(
         PROJECT_ROOT,
         [],
         PACKAGE_PATH,
-        4711,
-        dfdlDebuggers[0],
+        dfdlDebuggerConfigs[i].port,
+        dfdlDebuggerConfigs[i].debugger,
         true
       )
+
+      newDebugger ? debuggers.push(newDebugger) : true
+    }
+
+    await addDebuggerRunningTests(
+      this,
+      dfdlDebuggerConfigs,
+      XML_INFOSET_PATH,
+      JSON_INFOSET_PATH,
+      DATA,
+      tdmlConf,
+      dataEditor
     )
-    debuggers.push(
-      await runDebugger(
-        PROJECT_ROOT,
-        [],
-        PACKAGE_PATH,
-        4712,
-        dfdlDebuggers[1],
-        true
-      )
+  })
+
+  test('debugger config size is correct', async () => {
+    assert.strictEqual(
+      (await getDaffodilVersionsToTest()).length * 2,
+      dfdlDebuggerConfigs.length
     )
   })
 
@@ -91,66 +104,141 @@ suite('Daffodil Debugger', () => {
       const pid = await d.processId
       await killProcess(pid)
     }
+
     // No need to deleted the debugging server because upon re-run, webpack 
cleans and re-extracts it.
-    if (fs.existsSync(XML_INFOSET_PATH)) fs.rmSync(XML_INFOSET_PATH)
-    if (fs.existsSync(JSON_INFOSET_PATH)) fs.rmSync(JSON_INFOSET_PATH)
+    ;(await getDaffodilVersionsToTest()).forEach((version) => {
+      const xmlPath = XML_INFOSET_PATH.replace('.xml', `${version}.xml`)
+      const jsonPath = JSON_INFOSET_PATH.replace('.json', `${version}.json`)
+      if (fs.existsSync(xmlPath)) fs.rmSync(xmlPath)
+      if (fs.existsSync(jsonPath)) fs.rmSync(jsonPath)
+    })
   })
+})
 
-  test('should output xml infoset', async () => {
-    await vscode.debug.startDebugging(
-      undefined,
-      getConfig({
-        name: 'Run',
-        request: 'launch',
-        type: 'dfdl',
-        schema: {
-          path: TEST_SCHEMA,
-        },
-        data: DATA,
-        debugServer: 4711,
-        infosetFormat: 'xml',
-        infosetOutput: {
-          type: 'file',
-          path: XML_INFOSET_PATH,
-        },
-        tdmlConfig: tdmlConf,
-        dataEditor: dataEditor,
-        dfdlDebugger: dfdlDebuggers[0],
-      }),
-      {
-        noDebug: true,
-      }
-    )
+// Gets all debugger version to test based on if JDK is >= 17
+async function getDaffodilVersionsToTest(): Promise<Array<string>> {
+  const javaHome = await getJavaHome()
+  const isAtLeastJdk17: boolean = parseFloat(javaHome?.version ?? '0') >= 17
 
-    assert.strictEqual(fs.existsSync(XML_INFOSET_PATH), true)
-  })
+  const dfdlVersions = ['3.10.0', '3.11.0']
+  if (isAtLeastJdk17) dfdlVersions.push('4.0.0')
+  return dfdlVersions
+}
+
+/**
+ * Populates the array of debugger configs with all debugger configs. If JDK 
>= 17 there
+ * should be 6 and if JDK < 17 there should be 4. Each version of the debugger 
has two
+ * configs, one for XML and one for JSON.
+ */
+async function getDebuggerConfigs() {
+  const debuggerVersionsToTest = await getDaffodilVersionsToTest()
+  let versionIndex = 0
 
-  test('should output json infoset', async () => {
-    await vscode.debug.startDebugging(
-      undefined,
-      getConfig({
-        name: 'Run',
-        request: 'launch',
-        type: 'dfdl',
-        schema: {
-          path: TEST_SCHEMA,
-        },
-        data: DATA,
-        debugServer: 4712,
-        infosetFormat: 'json',
-        infosetOutput: {
-          type: 'file',
-          path: JSON_INFOSET_PATH,
-        },
-        tdmlConfig: tdmlConf,
-        dataEditor: dataEditor,
-        dfdlDebugger: dfdlDebuggers[1],
-      }),
-      {
-        noDebug: true,
-      }
+  for (var i = 0; i < debuggerVersionsToTest.length * 2; i++) {
+    if (i > 0 && i % 2 == 0) versionIndex++
+
+    const port = 4711 + i
+    const infosetFormat = i % 2 == 0 ? 'xml' : 'json'
+    const dfdlDebugger: DFDLDebugger = {
+      daffodilVersion: debuggerVersionsToTest[versionIndex],
+      timeout: '4m',
+      logging: {
+        level: 'INFO',
+        file: path.join(os.tmpdir(), 
`yarn-test-daffodil-debugger-${port}.log`),
+      },
+    }
+
+    dfdlDebuggerConfigs.push({
+      debugger: dfdlDebugger,
+      port: port,
+      infosetFormat: infosetFormat,
+    })
+  }
+}
+
+// This method is for starting the debug and making sure the infoset file was 
created
+async function checkDebug(
+  data: string,
+  port: number,
+  infosetFormat: string,
+  infosetPath: string,
+  tdmlConfig: TDMLConfig,
+  dataEditor: DataEditorConfig,
+  dfdlDebugger: DFDLDebugger
+) {
+  await vscode.debug.startDebugging(
+    undefined,
+    getConfig({
+      name: 'Run',
+      request: 'launch',
+      type: 'dfdl',
+      schema: {
+        path: TEST_SCHEMA,
+      },
+      data: data,
+      debugServer: port,
+      infosetFormat: infosetFormat,
+      infosetOutput: {
+        type: 'file',
+        path: infosetPath,
+      },
+      tdmlConfig: tdmlConfig,
+      dataEditor: dataEditor,
+      dfdlDebugger: dfdlDebugger,
+      stopOnEntry: false,
+    }),
+    {
+      noDebug: true,
+    }
+  )
+
+  await delay(1000)
+
+  assert.strictEqual(fs.existsSync(infosetPath), true)
+}
+
+/**
+ * This function adds a number of tests to the suite for connecting to the 
debuggers.
+ * Since we have 3 different versions of the debugger, each needs to be 
connected to
+ * twice. Once for outputting XML and one for outputting JSON. Not sure if 
this is
+ * a common way to add tests to a test suite but it seemed better and more 
efficient
+ * than making a single separate test for each combination of the debugger 
plus infoset
+ * format. Especially since two of the combinations can only be ran if the JDK 
version
+ * being used is >= 17.
+ */
+async function addDebuggerRunningTests(
+  suite: Suite,
+  dfdlDebuggerConfigs: Array<TestDebuggerConfig>,
+  xmlInfosetPath: string,
+  jsonInfosetPath: string,
+  data: string,
+  tdmlConfig: TDMLConfig,
+  dataEditor: DataEditorConfig
+) {
+  for (var i = 0; i < dfdlDebuggerConfigs.length; i++) {
+    const debuggerConfig = dfdlDebuggerConfigs[i]
+    const baseInfosetPath =
+      debuggerConfig.infosetFormat == 'xml' ? xmlInfosetPath : jsonInfosetPath
+    const infosetPath = baseInfosetPath.replace(
+      `.${debuggerConfig.infosetFormat}`,
+      
`${debuggerConfig.debugger.daffodilVersion}.${debuggerConfig.infosetFormat}`
     )
 
-    assert.strictEqual(fs.existsSync(JSON_INFOSET_PATH), true)
-  })
-})
+    suite.addTest(
+      new Test(
+        `should output ${debuggerConfig.infosetFormat} infoset - debugger 
version ${debuggerConfig.debugger.daffodilVersion}`,
+        async function () {
+          await checkDebug(
+            data,
+            debuggerConfig.port,
+            debuggerConfig.infosetFormat,
+            infosetPath,
+            tdmlConfig,
+            dataEditor,
+            debuggerConfig.debugger
+          )
+        }
+      )
+    )
+  }
+}
diff --git a/src/tests/suite/utils.test.ts b/src/tests/suite/utils.test.ts
index 291d3c6..9649161 100644
--- a/src/tests/suite/utils.test.ts
+++ b/src/tests/suite/utils.test.ts
@@ -60,6 +60,8 @@ suite('Utils Test Suite', () => {
       },
     },
     dfdlDebugger: {
+      daffodilVersion: '3.11.0',
+      timeout: '10s',
       logging: {
         level: 'INFO',
         file: '${workspaceFolder}/daffodil-debugger.log',
diff --git a/src/utils.ts b/src/utils.ts
index 735d58a..9a3aee8 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -23,7 +23,9 @@ import path from 'path'
 import { VSCodeLaunchConfigArgs } from './classes/vscode-launch'
 import { InfosetOutput } from './daffodilDebugger'
 import { XMLParser } from 'fast-xml-parser'
-
+import * as unzip from 'unzip-stream'
+import { pipeline } from 'stream/promises'
+import { Transform } from 'stream'
 let currentConfig: vscode.DebugConfiguration
 
 export const terminalName = 'daffodil-debugger'
@@ -114,6 +116,37 @@ function checkInfosetFileExtension(
   }
 }
 
+function checkSettingValue<T>(target: unknown, defaults: T): T {
+  if (
+    typeof defaults !== 'object' ||
+    defaults === null ||
+    Array.isArray(defaults)
+  ) {
+    return target === undefined ? defaults : (target as T)
+  }
+
+  if (typeof target !== 'object' || target === null) {
+    return defaults
+  }
+
+  const result: Record<string, any> = {}
+
+  for (const key of Object.keys(defaults)) {
+    result[key] = checkSettingValue(
+      (target as any)[key],
+      (defaults as any)[key]
+    )
+  }
+
+  for (const key of Object.keys(target as any)) {
+    if (!(key in result)) {
+      result[key] = (target as any)[key]
+    }
+  }
+
+  return result as T
+}
+
 export function getConfig(jsonArgs: object): vscode.DebugConfiguration {
   const launchConfigArgs: VSCodeLaunchConfigArgs = JSON.parse(
     JSON.stringify(jsonArgs)
@@ -157,6 +190,8 @@ export function getConfig(jsonArgs: object): 
vscode.DebugConfiguration {
       },
     }),
     dfdlDebugger: defaultConf.get('dfdlDebugger', {
+      daffodilVersion: '3.11.0',
+      timeout: '10s',
       logging: {
         level: 'INFO',
         file: '${workspaceFolder}/daffodil-debugger.log',
@@ -164,13 +199,9 @@ export function getConfig(jsonArgs: object): 
vscode.DebugConfiguration {
     }),
   }
 
-  Object.entries(defaultValues).map(
-    ([key, defaultValue]) =>
-      (launchConfigArgs[key] =
-        launchConfigArgs[key] !== undefined
-          ? launchConfigArgs[key]
-          : defaultValue)
-  )
+  for (const [key, defaults] of Object.entries(defaultValues)) {
+    launchConfigArgs[key] = checkSettingValue(launchConfigArgs[key], defaults)
+  }
 
   if (launchConfigArgs.infosetOutput?.type == 'file') {
     checkInfosetFileExtension(
@@ -277,7 +308,7 @@ export async function runScript(
   type: string = '',
   hideTerminal: boolean = false,
   port: number | undefined = undefined
-) {
+): Promise<vscode.Terminal> {
   // Get the full path to the script
   const scriptFullPath = path.join(scriptPath, 'bin', scriptName)
 
@@ -316,6 +347,7 @@ export async function runScript(
     const wait_port = require('wait-port')
     await wait_port({ host: '127.0.0.1', port: port, output: 'silent' })
   }
+
   return terminal
 }
 
@@ -444,3 +476,87 @@ export function getTDMLTestCaseItems(path: string): 
string[] {
       : []
   return testCaseArr.map((item) => item['@_name'])
 }
+
+/**
+ * Displays a VSCode error message as modal. In some cases such as when 
running tests, using modal
+ * can cause an error because the dialog service is not available. If this 
happens when trying
+ * to display the message, this error is grabbed and then message is displayed 
with modal disabled.
+ *
+ * @param message - The message to display
+ * @param items — A set of items that will be rendered as actions in the 
message.
+ * @returns — A thenable that resolves to the selected item or undefined when 
being dismissed.
+ */
+export const displayModalError = async (
+  message: string,
+  ...items: string[]
+): Promise<Thenable<string | undefined>> =>
+  vscode.window
+    .showErrorMessage(message, { modal: true }, ...items)
+    .then(undefined, () =>
+      vscode.window.showErrorMessage(message, { modal: false })
+    )
+
+/**
+ * Download and extract a files with a progress bar
+ *
+ * @param title A title to use for printing to the user what is being 
downloaded
+ * @param url The url to donwload the binary from
+ * @param targetDir The directory to target for extraction
+ */
+export async function downloadAndExtract(
+  title: string,
+  url: string,
+  targetDir: string
+): Promise<void> {
+  await vscode.window.withProgress(
+    {
+      location: vscode.ProgressLocation.Notification,
+      title: `Downloading ${title}...`,
+      cancellable: false,
+    },
+    async (progress) => {
+      progress.report({ increment: 0, message: 'Starting download...' })
+
+      const res = await fetch(url)
+      if (!res.ok || !res.body) {
+        throw new Error(
+          `Failed to download ${url}: ${res.status} ${res.statusText}`
+        )
+      }
+
+      const totalBytes = Number(res.headers.get('content-length')) || 0
+      let downloaded = 0
+      let lastPercent = 0
+
+      // Transform stream to track download progress
+      const progressStream = new Transform({
+        transform(chunk: Buffer, _encoding, callback) {
+          downloaded += chunk.length
+          if (totalBytes > 0) {
+            const percent = (downloaded / totalBytes) * 100
+            const increment = percent - lastPercent
+            lastPercent = percent
+            progress.report({
+              increment,
+              message: `${percent.toFixed(1)}%`,
+            })
+          }
+          callback(null, chunk)
+        },
+      })
+
+      await fs.promises.mkdir(targetDir, { recursive: true })
+
+      await pipeline(
+        res.body as any,
+        progressStream,
+        unzip.Extract({ path: targetDir })
+      )
+
+      progress.report({
+        increment: 100 - lastPercent,
+        message: 'Extracting complete!',
+      })
+    }
+  )
+}
diff --git a/vite/dev.vite.config.mjs b/vite/dev.vite.config.mjs
index 944c156..0ca10ef 100644
--- a/vite/dev.vite.config.mjs
+++ b/vite/dev.vite.config.mjs
@@ -43,34 +43,43 @@ const packageData = jsoncParse(
   fs.readFileSync(path.resolve('package.json'), 'utf8')
 )
 const pkg_version = packageData['version']
-const daffodilVersion = packageData['daffodilVersion']
-const serverPackage = `daffodil-debugger-${daffodilVersion}-${pkg_version}`
-const zipFilePath = path.resolve(
-  `debugger/target/universal/${serverPackage}.zip`
-)
+const scalaVersions = ['2.12', '2.13', '3']
 
 function unzipAfterBuild() {
   return {
     name: 'unzip-server-package',
     apply: 'build',
     async closeBundle() {
-      const serverPackageFolder = path.join(
-        path.resolve('dist/package'),
-        serverPackage
-      )
-
-      // remove debugger package folder if exists
-      if (fs.existsSync(serverPackageFolder)) {
-        fs.rmSync(serverPackageFolder, { recursive: true, force: true })
-      }
-
-      await new Promise((resolve, reject) => {
-        const stream = fs
-          .createReadStream(zipFilePath)
-          // @ts-ignore types for unzip-stream
-          .pipe(unzip.Extract({ path: '.' }))
-        stream.on('close', () => resolve())
-        stream.on('error', (err) => reject(err))
+      scalaVersions.forEach(async (scalaVersion) => {
+        const serverPackage = 
`daffodil-debugger-${scalaVersion}-${pkg_version}`
+        const jvmFolderName = `jvm-${scalaVersion}`
+        const zipFilePath = path.resolve(
+          `debugger/target/${jvmFolderName}/universal/${serverPackage}.zip`
+        )
+
+        const serverPackageFolder = path.join(
+          path.resolve('dist/package'),
+          serverPackage
+        )
+
+        // remove debugger package folder if exists
+        if (fs.existsSync(serverPackageFolder)) {
+          fs.rmSync(serverPackageFolder, { recursive: true, force: true })
+        }
+
+        // if the debugger package doesn't exist continue
+        if (!fs.existsSync(zipFilePath)) {
+          return
+        }
+
+        await new Promise((resolve, reject) => {
+          const stream = fs
+            .createReadStream(zipFilePath)
+            // @ts-ignore types for unzip-stream
+            .pipe(unzip.Extract({ path: '.' }))
+          stream.on('close', () => resolve())
+          stream.on('error', (err) => reject(err))
+        })
       })
     },
   }
diff --git a/vite/package.vite.config.mjs b/vite/package.vite.config.mjs
index 497ca04..0e5c650 100644
--- a/vite/package.vite.config.mjs
+++ b/vite/package.vite.config.mjs
@@ -43,34 +43,43 @@ const packageData = jsoncParse(
   fs.readFileSync(path.resolve('package.json'), 'utf8')
 )
 const pkg_version = packageData['version']
-const daffodilVersion = packageData['daffodilVersion']
-const serverPackage = `daffodil-debugger-${daffodilVersion}-${pkg_version}`
-const zipFilePath = path.resolve(
-  `debugger/target/universal/${serverPackage}.zip`
-)
+const scalaVersions = ['2.12', '2.13', '3']
 
 function unzipAfterBuild() {
   return {
     name: 'unzip-server-package',
     apply: 'build',
     async closeBundle() {
-      const serverPackageFolder = path.join(
-        path.resolve('dist/package'),
-        serverPackage
-      )
-
-      // remove debugger package folder if exists
-      if (fs.existsSync(serverPackageFolder)) {
-        fs.rmSync(serverPackageFolder, { recursive: true, force: true })
-      }
-
-      await new Promise((resolve, reject) => {
-        const stream = fs
-          .createReadStream(zipFilePath)
-          // @ts-ignore types for unzip-stream
-          .pipe(unzip.Extract({ path: 'dist/package' }))
-        stream.on('close', () => resolve())
-        stream.on('error', (err) => reject(err))
+      scalaVersions.forEach(async (scalaVersion) => {
+        const serverPackage = 
`daffodil-debugger-${scalaVersion}-${pkg_version}`
+        const jvmFolderName = `jvm-${scalaVersion}`
+        const zipFilePath = path.resolve(
+          `debugger/target/${jvmFolderName}/universal/${serverPackage}.zip`
+        )
+
+        const serverPackageFolder = path.join(
+          path.resolve('dist/package'),
+          serverPackage
+        )
+
+        // remove debugger package folder if exists
+        if (fs.existsSync(serverPackageFolder)) {
+          fs.rmSync(serverPackageFolder, { recursive: true, force: true })
+        }
+
+        // if the debugger package doesn't exist continue
+        if (!fs.existsSync(zipFilePath)) {
+          return
+        }
+
+        await new Promise((resolve, reject) => {
+          const stream = fs
+            .createReadStream(zipFilePath)
+            // @ts-ignore types for unzip-stream
+            .pipe(unzip.Extract({ path: 'dist/package' }))
+          stream.on('close', () => resolve())
+          stream.on('error', (err) => reject(err))
+        })
       })
     },
   }
@@ -109,23 +118,34 @@ function copyToPkgDirPlugin() {
     { from: 'src/styles/styles.css', to: `${pkg_dir}/src/styles/styles.css` },
     { from: 'src/tdmlEditor/', to: `${pkg_dir}/src/tdmlEditor` },
   ]
-  const serverPackageFolder = path.join(
-    path.resolve('dist/package'),
-    serverPackage
-  )
-
-  console.debug(`== [Vite] | serverPackageFolder: ${serverPackageFolder}`)
-  // remove debugger package folder if exists
-  if (fs.existsSync(serverPackageFolder)) {
-    fs.rmSync(serverPackageFolder, { recursive: true })
-  }
+
+  const serverPackageFolders = []
+
+  scalaVersions.forEach((scalaVersion) => {
+    serverPackageFolders.push(
+      path.join(
+        path.resolve('dist/package'),
+        `daffodil-debugger-${scalaVersion}-${pkg_version}`
+      )
+    )
+  })
+
+  serverPackageFolders.forEach((serverPackageFolder) => {
+    console.debug(`== [Vite] | serverPackageFolder: ${serverPackageFolder}`)
+    // remove debugger package folder if exists
+    if (fs.existsSync(serverPackageFolder)) {
+      fs.rmSync(serverPackageFolder, { recursive: true })
+    }
+  })
 
   return {
     name: 'copy-patterns-plugin',
     apply: 'build',
     async buildStart(opts) {
       if (!fs.existsSync(pkg_dir)) {
-        fs.mkdirSync(serverPackageFolder, { recursive: true })
+        serverPackageFolders.forEach((serverPackageFolder) => {
+          fs.mkdirSync(serverPackageFolder, { recursive: true })
+        })
         fs.mkdirSync(pkg_dir + '/dist')
         fs.mkdirSync(pkg_dir + '/src/language', {
           recursive: true,


Reply via email to