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

scholarsmate 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 09d89ce  - upgraded the Ωedit™ integration to `v2.0.0 - updated the 
data editor to the Ωedit™ 2.x client APIs, including heartbeat and viewport 
subscription helpers - adjusted server metrics and packaging to the new native 
runtime fields and `@omega-edit/server/out` layout - refreshed the Ωedit™ test 
startup call sites for the 2.x `startServer(..., { logConfigFile })` signature
09d89ce is described below

commit 09d89ce37de51deff841e6b5e8ffe4c07580bd01
Author: Davin Shearer <[email protected]>
AuthorDate: Fri Apr 17 15:34:50 2026 -0400

    - upgraded the Ωedit™ integration to `v2.0.0
    - updated the data editor to the Ωedit™ 2.x client APIs, including 
heartbeat and viewport subscription helpers
    - adjusted server metrics and packaging to the new native runtime fields 
and `@omega-edit/server/out` layout
    - refreshed the Ωedit™ test startup call sites for the 2.x 
`startServer(..., { logConfigFile })` signature
    
    Ωedit™ `v2.0.0` changes the client/server package layout and several APIs. 
This PR aligns the VS Code extension with the migration guide and the new 
helper APIs so we can validate the upgrade in CI.
    
    - keeps the data editor building and packaging against the RC release
    - updates the runtime metrics surfaced in the UI to match the new server 
fields
    - ensures the packaged VSIX includes the server binaries from the new 
`out/` directory structure
    
    - `yarn install`
    - `yarn compile`
    - `yarn test:svelte`
    - `yarn vite:pkg`
    - `yarn package`
    - Manual run through of the data editor features in the extension, 
including:
      - opening a data editor and confirming it loads successfully
      - confirming the server info shows the new heartbeat messages
      - confirming the file type information in the profile view is correct
      - confirming the server shuts down after the idle timeout when no editor 
is open
      - confirming search and replace features work as expected with the new 
client APIs
      - confirming the viewport subscription helper correctly updates the 
visible range in the UI as we scroll
      - others _as needed_ to validate the upgrade and new features
    
    - [x] I have determined that no documentation updates are needed for these 
changes
    
    Closes: #1667
---
 build/package/LICENSE                              |  62 +++
 build/package/NONOTICE                             |   6 +
 build/yarn-scripts.ts                              | 234 ++++++++-
 package.json                                       |  24 +-
 src/dataEditor/config/Config.ts                    |  56 +-
 src/dataEditor/config/Extract.ts                   |  13 +-
 src/dataEditor/dataEditorClient.ts                 | 571 +++++++++++++++------
 src/dataEditor/include/server/LogbackConfig.ts     |  53 ++
 src/dataEditor/include/server/ServerInfo.ts        |  43 +-
 src/dataEditor/include/server/Sessions.ts          |  49 +-
 .../include/server/heartbeat/HeartBeatInfo.ts      |   9 +-
 src/dataEditor/include/server/heartbeat/index.ts   |  36 +-
 src/dataEditor/svelteWebviewInitializer.ts         |  22 +-
 src/svelte/src/App.svelte                          |  20 +-
 .../CustomByteDisplay/DataLineFeed.svelte          |   2 +-
 .../src/components/DataMetrics/DataMetrics.svelte  |  14 +-
 .../components/ServerMetrics/ServerMetrics.svelte  |  54 +-
 src/svelte/src/components/dataEditor.svelte        |  19 +-
 src/svelte/src/utilities/message.ts                |   1 +
 src/tests/omegaEditServerLifecycle.ts              | 147 ++++++
 src/tests/suite/dataEditor.test.ts                 |  44 +-
 src/tests/suite/omegaEditClientLogger.test.ts      |  47 ++
 src/tests/suite/utils.test.ts                      |   2 +-
 src/utils.ts                                       |   2 +-
 vite.config.mjs                                    |  10 +-
 yarn.lock                                          | 182 ++-----
 26 files changed, 1255 insertions(+), 467 deletions(-)

diff --git a/build/package/LICENSE b/build/package/LICENSE
index bbff97d..06041f3 100644
--- a/build/package/LICENSE
+++ b/build/package/LICENSE
@@ -1103,6 +1103,41 @@ conditions of the following licenses.
     TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
+- '@protobuf-ts/runtime' in extension/dist/ext/extension.js
+- '@protobuf-ts/runtime' in node_modules/@omega-edit/client
+  This product bundles '@protobuf-ts/runtime' from the above files.
+  This package is available under the BSD-3-Clause License and Apache License 
2.0:
+
+    BSD 3-Clause License
+
+    Copyright (c) Timo Stamm
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice, 
this
+    list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright 
notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+    3. Neither the name of the copyright holder nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
LIABLE
+    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
LIABILITY,
+    OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 
USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 - 'protobufjs' in extension/dist/ext/extension.js
 - 'protobufjs' in node_modules/@omega-edit/client
   This product bundles 'protobufjs' from the above files.
@@ -5214,6 +5249,33 @@ conditions of the following licenses.
     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
THE
     SOFTWARE.
+
+- '@pinojs/redact' in extension/dist/ext/extension.js
+- '@pinojs/redact' in node_modules/@omega-edit/client
+  This product bundles '@pinojs/redact' from the above files.
+  These files are available under the MIT License:
+
+    MIT License
+
+    Copyright (c) 2025 pinojs contributors
+
+    Permission is hereby granted, free of charge, to any person obtaining a 
copy
+    of this software and associated documentation files (the "Software"), to 
deal
+    in the Software without restriction, including without limitation the 
rights
+    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in 
all
+    copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
THE
+    SOFTWARE.
   
 - 'unicorn-magic' in extension/dist/ext/extension.js
   This produces bundles 'unicorn-magic' from the above files
diff --git a/build/package/NONOTICE b/build/package/NONOTICE
index ba89c90..40d0ef4 100644
--- a/build/package/NONOTICE
+++ b/build/package/NONOTICE
@@ -33,6 +33,12 @@ The following binary components distributed with this 
project are licensed under
   This package is available under the Apache License v2 without a NOTICE:
         Repository at: https://github.com/dcodeIO/long.js
 
+- '@protobuf-ts/runtime-rpc' in extension/dist/ext/extension.js
+- '@protobuf-ts/runtime-rpc' in node_modules/@omega-edit/client
+  This product bundles '@protobuf-ts/runtime-rpc' from the above files.
+  This package is available under the Apache License v2 without a NOTICE:
+        Repository at: https://github.com/timostamm/protobuf-ts
+
 - com.fasterxml.woodstox.woodstox-core-<VERSION>.jar in 
daffodil-debugger-<VERSION>.zip
   This product bundles 'woodstox-core' from the above files.
   These packages are available under the Apache License v2 without a NOTICE:
diff --git a/build/yarn-scripts.ts b/build/yarn-scripts.ts
index ab798a1..1acaf66 100644
--- a/build/yarn-scripts.ts
+++ b/build/yarn-scripts.ts
@@ -95,13 +95,236 @@ function package() {
 # limitations under the License.
 
 **/node_modules/**/*
-!node_modules/@omega-edit/server/bin
-!node_modules/@omega-edit/server/lib
-!node_modules/@vscode/webview-ui-toolkit/**/*
+!node_modules/
+!node_modules/**/*
 `
   )
 }
 
+function packageNamePath(packageName) {
+  return path.join(...packageName.split('/'))
+}
+
+function readPackageVersion(packageRoot) {
+  const packageJsonPath = path.join(packageRoot, 'package.json')
+  if (!fs.existsSync(packageJsonPath)) return undefined
+
+  try {
+    return JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')).version
+  } catch (err) {
+    console.warn(
+      `[omega-edit] Unable to read package version for ${packageRoot}: 
${String(
+        err
+      )}`
+    )
+    return undefined
+  }
+}
+
+function shouldPatchOmegaEditPackage(packageRoot, expectedVersion, label) {
+  const version = readPackageVersion(packageRoot)
+  if (version === expectedVersion) {
+    return true
+  }
+
+  const versionLabel = version ?? 'unknown'
+  console.warn(
+    `[omega-edit] Skipping ${label} patch for ${packageRoot}; expected 
${expectedVersion}, found ${versionLabel}.`
+  )
+  return false
+}
+
+function patchOmegaEditClientLogger(
+  packageRoot = 'node_modules/@omega-edit/client'
+) {
+  if (!shouldPatchOmegaEditPackage(packageRoot, '2.0.0', 'client logger')) {
+    return
+  }
+
+  const loggerTargets = [
+    path.join(packageRoot, 'dist/cjs/logger.js'),
+    path.join(packageRoot, 'dist/esm/logger.js'),
+  ]
+  const transportPattern =
+    
/setLogger\(buildLogger\(pino(?:_1\.default)?\.transport\(\{[\s\S]*?\}\)\)\);/
+
+  loggerTargets.forEach((loggerPath) => {
+    if (!fs.existsSync(loggerPath)) {
+      console.warn(`[omega-edit] Client logger not found: ${loggerPath}`)
+      return
+    }
+
+    const source = fs.readFileSync(loggerPath, 'utf-8')
+    const patched = source.replace(
+      transportPattern,
+      'setLogger(buildLogger(process.stderr));'
+    )
+
+    if (patched === source) {
+      if (!source.includes('setLogger(buildLogger(process.stderr));')) {
+        console.warn(
+          `[omega-edit] Unable to patch OmegaEdit client logger at 
${loggerPath}; leaving upstream source unchanged.`
+        )
+      }
+      return
+    }
+
+    fs.writeFileSync(loggerPath, patched, 'utf-8')
+  })
+}
+
+function patchOmegaEditServerLocator(searchRoot = 'node_modules') {
+  const serverTargets = glob.sync('**/@omega-edit/server/out/index.js', {
+    cwd: searchRoot,
+    absolute: true,
+    nodir: true,
+  })
+  const buggyLocator = '.replace("node_modules","")'
+  const knownFixedLocators = [
+    '.slice(0,-"node_modules".length)',
+    '.slice(0,-12)',
+  ]
+
+  if (serverTargets.length === 0) {
+    return
+  }
+
+  serverTargets.forEach((serverPath) => {
+    const packageRoot = path.dirname(path.dirname(serverPath))
+    if (!shouldPatchOmegaEditPackage(packageRoot, '2.0.0', 'server locator')) {
+      return
+    }
+
+    const source = fs.readFileSync(serverPath, 'utf-8')
+    const patched = source.replaceAll(buggyLocator, knownFixedLocators[0])
+
+    if (patched === source) {
+      if (!knownFixedLocators.some((locator) => source.includes(locator))) {
+        console.warn(
+          `[omega-edit] Unable to patch OmegaEdit server locator at 
${serverPath}; leaving upstream source unchanged.`
+        )
+      }
+      return
+    }
+
+    fs.writeFileSync(serverPath, patched, 'utf-8')
+  })
+}
+
+function patchOmegaEditRuntime(
+  packageRoot = 'node_modules/@omega-edit/client',
+  searchRoot = 'node_modules'
+) {
+  patchOmegaEditClientLogger(packageRoot)
+  patchOmegaEditServerLocator(searchRoot)
+}
+
+function copyPackageRuntimeTree(
+  packageName,
+  sourcePackageDir,
+  destinationPackageDir,
+  seen = new Set()
+) {
+  const visitKey = `${sourcePackageDir}|${destinationPackageDir}`
+  if (seen.has(visitKey)) return
+  seen.add(visitKey)
+
+  if (!fs.existsSync(sourcePackageDir)) {
+    throw new Error(
+      `Package source not found for ${packageName}: ${sourcePackageDir}`
+    )
+  }
+
+  rmFileOrDirectory(destinationPackageDir)
+  fs.mkdirSync(path.dirname(destinationPackageDir), { recursive: true })
+  fs.cpSync(sourcePackageDir, destinationPackageDir, {
+    recursive: true,
+    force: true,
+  })
+
+  const packageJsonPath = path.join(sourcePackageDir, 'package.json')
+  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))
+  const dependencies = Object.keys(packageJson.dependencies || {})
+
+  if (dependencies.length === 0) return
+
+  const destinationNodeModulesDir = path.join(
+    destinationPackageDir,
+    'node_modules'
+  )
+
+  dependencies.forEach((dependencyName) => {
+    const sourceDependencyDirCandidates = [
+      path.join(
+        sourcePackageDir,
+        'node_modules',
+        packageNamePath(dependencyName)
+      ),
+      path.join('node_modules', packageNamePath(dependencyName)),
+    ]
+    const sourceDependencyDir = sourceDependencyDirCandidates.find(
+      (candidate) => fs.existsSync(candidate)
+    )
+
+    if (!sourceDependencyDir) {
+      throw new Error(
+        `Unable to resolve runtime dependency ${dependencyName} for 
${packageName}`
+      )
+    }
+
+    copyPackageRuntimeTree(
+      dependencyName,
+      sourceDependencyDir,
+      path.join(destinationNodeModulesDir, packageNamePath(dependencyName)),
+      seen
+    )
+  })
+}
+
+function syncOmegaEditClientRuntime() {
+  const clientPackageName = '@omega-edit/client'
+  const sourceClientDir = path.join(
+    'node_modules',
+    packageNamePath(clientPackageName)
+  )
+  const destinationClientDir = path.join(
+    'dist/package/node_modules',
+    packageNamePath(clientPackageName)
+  )
+
+  patchOmegaEditRuntime(sourceClientDir, 'node_modules')
+  copyPackageRuntimeTree(
+    clientPackageName,
+    sourceClientDir,
+    destinationClientDir
+  )
+  patchOmegaEditRuntime(destinationClientDir, 'dist/package/node_modules')
+}
+
+function packageVsix() {
+  const vsceCommand =
+    process.platform === 'win32'
+      ? path.resolve('node_modules', '.bin', 'vsce.cmd')
+      : path.resolve('node_modules', '.bin', 'vsce')
+
+  const result = child_process.spawnSync(
+    vsceCommand,
+    ['package', '--out', '../../'],
+    {
+      cwd: 'dist/package',
+      stdio: 'inherit',
+      shell: process.platform === 'win32',
+    }
+  )
+
+  if (result.error) {
+    console.error(result.error)
+    process.exit(1)
+  }
+
+  process.exit(result.status === null ? 1 : result.status)
+}
+
 /* START SECTION: Update version */
 // helper function to get the version passed in
 function parseArgs() {
@@ -257,6 +480,11 @@ module.exports = {
   updateVersion: updateVersion,
   watch: watch,
   package: package,
+  patchOmegaEditClientLogger: patchOmegaEditClientLogger,
+  patchOmegaEditServerLocator: patchOmegaEditServerLocator,
+  patchOmegaEditRuntime: patchOmegaEditRuntime,
+  syncOmegaEditClientRuntime: syncOmegaEditClientRuntime,
+  packageVsix: packageVsix,
   checkMissingLicenseData: checkMissingLicenseData,
   checkLicenseCompatibility: checkLicenseCompatibility,
 }
diff --git a/package.json b/package.json
index 492da1a..0b0af77 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
     "gen-version-ts": "run-func build/yarn-scripts.ts genVersionTS",
     "nodeclean": "run-func build/yarn-scripts.ts nodeclean",
     "scalaclean": "run-func build/yarn-scripts.ts scalaclean",
+    "postinstall": "node -e \"const fs=require('fs'); const 
cp=require('child_process'); if (fs.existsSync('build/yarn-scripts.ts')) { 
const cmd=process.platform === 'win32' ? 'yarn.cmd' : 'yarn'; const 
result=cp.spawnSync(cmd, ['run-func', 'build/yarn-scripts.ts', 
'patchOmegaEditRuntime'], { stdio: 'inherit', shell: process.platform === 
'win32' }); if (result.error) { console.error(result.error); process.exit(1); } 
process.exit(result.status === null ? 1 : result.status); }\"",
     "check-missing-license-data": "run-func build/yarn-scripts.ts 
checkMissingLicenseData",
     "check-license-compatibility": "run-func build/yarn-scripts.ts 
checkLicenseCompatibility",
     "clean": "yarn nodeclean && yarn scalaclean",
@@ -41,9 +42,9 @@
     "watch:tdmlEditorJS": "esbuild src/tdmlEditor/webview/webview.js 
--outfile=dist/views/tdmlEditor/webview/webview.js --bundle --platform=node 
--format=cjs --watch",
     "compile:tdmlEditorJS": "esbuild src/tdmlEditor/webview/webview.js 
--outfile=dist/views/tdmlEditor/webview/webview.js --bundle --platform=node 
--format=cjs",
     "prepackage": "yarn install && yarn compile && yarn vite:pkg",
-    "package": "run-func build/yarn-scripts.ts package && yarn --cwd 
dist/package install && yarn --cwd dist/package vsce package --out ../../",
+    "package": "run-func build/yarn-scripts.ts package && yarn --cwd 
dist/package install --production && run-func build/yarn-scripts.ts 
syncOmegaEditClientRuntime && run-func build/yarn-scripts.ts packageVsix",
     "pretest": "yarn compile && yarn vite:dev",
-    "test": "sbt test && yarn test:svelte && node ./out/tests/runTest.js",
+    "test": "sbt test && yarn test:svelte && node 
./out/tests/omegaEditServerLifecycle.js && node ./out/tests/runTest.js",
     "test:svelte": "mocha --import=tsx ./src/svelte/tests/**/*.test.ts",
     "sbt": "sbt Universal/stage",
     "svelte:check": "svelte-check --tsconfig ./src/svelte/tsconfig.json",
@@ -54,7 +55,8 @@
     "watch:vite-dev": "yarn vite build --mode development -c ./vite.config.mjs 
--watch"
   },
   "dependencies": {
-    "@omega-edit/client": "^1.0.1",
+    "@omega-edit/client": "2.0.0",
+    "@omega-edit/server": "2.0.0",
     "@viperproject/locate-java-home": "1.1.17",
     "@vscode/debugadapter": "1.67.0",
     "@vscode/webview-ui-toolkit": "^1.2.2",
@@ -110,6 +112,7 @@
     "vscode-extension-tester": "5.9.1"
   },
   "resolutions": {
+    "@omega-edit/server": "2.0.0",
     "cookie": ">=0.7.0",
     "diff": ">=8.0.3",
     "serialize-javascript": ">=7.0.3"
@@ -118,6 +121,7 @@
   "activationEvents": [
     "onDebugResolve:dfdl",
     "onDebugDynamicConfigurations:dfdl",
+    "onCommand:extension.data.edit",
     "onCommand:extension.dfdl-debug.getSchemaName",
     "onCommand:extension.dfdl-debug.getDataName",
     "onCommand:extension.dfdl-debug.getTDMLName",
@@ -662,8 +666,8 @@
                     "properties": {
                       "file": {
                         "type": "string",
-                        "description": "Path to file to save logs at",
-                        "default": 
"${workspaceFolder}/dataEditor-${omegaEditPort}.log"
+                        "description": "Path to file to save logs at. Leave 
empty to use the OS app-data/XDG path.",
+                        "default": ""
                       },
                       "level": {
                         "type": "string",
@@ -679,7 +683,7 @@
                       }
                     },
                     "default": {
-                      "file": 
"${workspaceFolder}/dataEditor-${omegaEditPort}.log",
+                      "file": "",
                       "level": "info"
                     }
                   }
@@ -687,7 +691,7 @@
                 "default": {
                   "port": 9000,
                   "logging": {
-                    "file": 
"${workspaceFolder}/dataEditor-${omegaEditPort}.log",
+                    "file": "",
                     "level": "info"
                   }
                 }
@@ -776,7 +780,7 @@
             "dataEditor": {
               "port": 9000,
               "logging": {
-                "file": "${workspaceFolder}/dataEditor-${omegaEditPort}.log",
+                "file": "",
                 "level": "info"
               }
             },
@@ -824,7 +828,7 @@
               "dataEditor": {
                 "port": 9000,
                 "logging": {
-                  "file": 
"^\"\\${workspaceFolder}/dataEditor-\\${omegaEditPort}.log\"",
+                  "file": "",
                   "level": "info"
                 }
               },
@@ -944,7 +948,7 @@
             "default": {
               "port": 9000,
               "logging": {
-                "file": "${workspaceFolder}/dataEditor-${omegaEditPort}.log",
+                "file": "",
                 "level": "info"
               }
             }
diff --git a/src/dataEditor/config/Config.ts b/src/dataEditor/config/Config.ts
index 74f6a68..eea54a6 100644
--- a/src/dataEditor/config/Config.ts
+++ b/src/dataEditor/config/Config.ts
@@ -20,11 +20,56 @@ import {
   configureIf,
 } from './ConfigKeyword'
 import { addToAppDataPath, rootPath } from './Extract'
+import path from 'path'
 
 const portDefault = 9000
 const logFileDefault = addToAppDataPath('dataEditor-${omegaEditPort}.log')
 const logLevelDefault = 'info'
-const checkpointPathDefault = addToAppDataPath(`.checkpoint-${portDefault}`)
+const legacyWorkspaceLogFileTemplate = 
`${WorkspaceKeyword}/dataEditor-${ServerPortKeyword}.log`
+
+export function getDefaultLogFilePath(port: number): string {
+  return addToAppDataPath(`dataEditor-${port}.log`)
+}
+
+export function getDefaultCheckpointPath(port: number): string {
+  return addToAppDataPath(`.checkpoint-${port}`)
+}
+
+export function isLegacyWorkspaceLogFile(
+  logFile: string,
+  port: number
+): boolean {
+  const normalized = path.normalize(logFile)
+  return (
+    normalized === path.normalize(legacyWorkspaceLogFileTemplate) ||
+    normalized === path.normalize(path.join(rootPath, 
`dataEditor-${port}.log`))
+  )
+}
+
+export function normalizeDataEditorLogFile(
+  logFile: string,
+  port: number
+): string {
+  const trimmed = logFile.trim()
+  if (trimmed.length === 0) {
+    return getDefaultLogFilePath(port)
+  }
+
+  const resolved = configureIf(trimmed, [
+    { keyword: WorkspaceKeyword, replacement: rootPath },
+    { keyword: ServerPortKeyword, replacement: port.toString() },
+  ])
+  const normalized = path.normalize(resolved)
+
+  if (
+    isLegacyWorkspaceLogFile(trimmed, port) ||
+    isLegacyWorkspaceLogFile(normalized, port)
+  ) {
+    return getDefaultLogFilePath(port)
+  }
+
+  return path.isAbsolute(normalized) ? normalized : 
addToAppDataPath(normalized)
+}
 
 export type ConfigJSON = {
   port: number
@@ -49,15 +94,12 @@ export class Config implements IConfig {
     port: portDefault,
     logFile: logFileDefault,
     logLevel: logLevelDefault,
-    checkpointPath: checkpointPathDefault,
+    checkpointPath: getDefaultCheckpointPath(portDefault),
   })
   private constructor(configuration: Required<IConfig>) {
     const { port, logFile, logLevel, checkpointPath } = configuration
     this.port = port
-    this.logFile = configureIf(logFile, [
-      { keyword: WorkspaceKeyword, replacement: rootPath },
-      { keyword: ServerPortKeyword, replacement: port.toString() },
-    ])
+    this.logFile = normalizeDataEditorLogFile(logFile, port)
     this.logLevel = logLevel
     this.checkpointPath = checkpointPath
   }
@@ -66,7 +108,7 @@ export class Config implements IConfig {
       port: json.port,
       logFile: json.logging.file,
       logLevel: json.logging.level,
-      checkpointPath: checkpointPathDefault,
+      checkpointPath: getDefaultCheckpointPath(json.port),
     })
   }
 }
diff --git a/src/dataEditor/config/Extract.ts b/src/dataEditor/config/Extract.ts
index 3cf6d67..dbfa372 100644
--- a/src/dataEditor/config/Extract.ts
+++ b/src/dataEditor/config/Extract.ts
@@ -15,7 +15,13 @@
  * limitations under the License.
  */
 import { Uri, workspace } from 'vscode'
-import { Config, ConfigJSON, IConfig } from './Config'
+import {
+  Config,
+  ConfigJSON,
+  IConfig,
+  getDefaultCheckpointPath,
+  getDefaultLogFilePath,
+} from './Config'
 import XDGAppPaths from 'xdg-app-paths'
 import path from 'path'
 import { substituteVSCodeEnvVariables } from '../../utils'
@@ -57,9 +63,10 @@ export function extractConfigurationVariables(): IConfig {
           substituteVSCodeEnvVariables(
             workspaceConfig.dataEditor?.logging?.file,
             APP_DATA_PATH
-          ).replaceAll(ServerPortKeyword, port)
+          ).replaceAll(ServerPortKeyword, port.toString())
         )
-      : Config.Default.logFile, // Get logging file path from settings.json if 
exists
+      : getDefaultLogFilePath(port), // Get logging file path from 
settings.json if exists
+    checkpointPath: getDefaultCheckpointPath(port),
   }
 
   if (configObjArray === undefined || configObjArray.length === 0)
diff --git a/src/dataEditor/dataEditorClient.ts 
b/src/dataEditor/dataEditorClient.ts
index def11f1..3d66829 100644
--- a/src/dataEditor/dataEditorClient.ts
+++ b/src/dataEditor/dataEditorClient.ts
@@ -17,7 +17,6 @@
 
 import {
   ALL_EVENTS,
-  beginSessionTransaction,
   clear,
   countCharacters,
   CountKind,
@@ -26,9 +25,6 @@ import {
   createViewport,
   del,
   edit,
-  EditorClient,
-  endSessionTransaction,
-  EventSubscriptionRequest,
   getByteOrderMark,
   getClient,
   getClientVersion,
@@ -37,6 +33,7 @@ import {
   getCounts,
   getLanguage,
   getLogger,
+  resetClient,
   getServerInfo,
   getViewportData,
   IOFlags,
@@ -45,15 +42,16 @@ import {
   profileSession,
   redo,
   replaceOneSession,
+  runSessionTransaction,
   saveSession,
   SaveStatus,
   searchSession,
   setLogger,
   startServer,
   stopProcessUsingPID,
+  subscribeViewportEvents,
   undo,
   ViewportDataResponse,
-  ViewportEvent,
   ViewportEventKind,
 } from '@omega-edit/client'
 import assert from 'assert'
@@ -80,10 +78,10 @@ import {
   addActiveSession,
   removeActiveSession,
 } from './include/server/Sessions'
+import { writeLogbackConfigFile } from './include/server/LogbackConfig'
 import { getCurrentHeartbeatInfo } from './include/server/heartbeat'
 import * as child_process from 'child_process'
 import { osCheck } from '../utils'
-import { isDFDLDebugSessionActive } from './include/utils'
 
 // 
*****************************************************************************
 // global constants
@@ -99,6 +97,8 @@ export const APP_DATA_PATH: string = XDGAppPaths({ name: 
'omega_edit' }).data()
 // 
*****************************************************************************
 
 const HEARTBEAT_INTERVAL_MS: number = 1000 // 1 second (1000 ms)
+const SERVER_SESSION_TIMEOUT_MS: number = 60 * 1000
+const SERVER_CLEANUP_INTERVAL_MS: number = 15 * 1000
 const MAX_LOG_FILES: number = 5 // Maximum number of log files to keep TODO: 
make this configurable
 const OPEN_EDITORS = new Map<string, vscode.WebviewPanel>()
 
@@ -107,8 +107,47 @@ const OPEN_EDITORS = new Map<string, vscode.WebviewPanel>()
 // 
*****************************************************************************
 let serverInfo: ServerInfo = new ServerInfo()
 let checkpointPath: string = ''
-let client: EditorClient
 let omegaEditPort: number = 0
+let configuredClientLogger:
+  | {
+      logFile: string
+      logLevel: string
+    }
+  | undefined
+
+function toMessageBytes(data: Uint8Array): number[] {
+  return Array.from(data)
+}
+
+function fromMessageBytes(data: unknown): Uint8Array {
+  if (data instanceof Uint8Array) {
+    return data
+  }
+  if (Array.isArray(data)) {
+    return Uint8Array.from(data)
+  }
+  if (
+    data &&
+    typeof data === 'object' &&
+    'data' in data &&
+    Array.isArray((data as { data?: unknown }).data)
+  ) {
+    return Uint8Array.from((data as { data: number[] }).data)
+  }
+  if (data && typeof data === 'object') {
+    const values = Object.entries(data as Record<string, unknown>)
+      .filter(
+        (entry): entry is [string, number] =>
+          /^\d+$/.test(entry[0]) && typeof entry[1] === 'number'
+      )
+      .sort((a, b) => Number(a[0]) - Number(b[0]))
+      .map(([, value]) => value)
+    if (values.length > 0) {
+      return Uint8Array.from(values)
+    }
+  }
+  return new Uint8Array(0)
+}
 
 // 
*****************************************************************************
 // exported functions
@@ -136,10 +175,19 @@ export class DataEditorClient implements 
vscode.Disposable {
   private displayState: DisplayState
   private currentViewportId: string
   private fileToEdit: string = ''
+  private fileInfoData: Record<string, any> | undefined = undefined
+  private hasReceivedWebviewReady = false
   private omegaSessionId = ''
   private sendHeartbeatIntervalId: NodeJS.Timeout | number | undefined =
     undefined
+  private viewportSubscription:
+    | {
+        cancel(): void
+      }
+    | undefined = undefined
   private disposables: vscode.Disposable[] = []
+  private readonly disposeCleanupComplete: Promise<void>
+  private resolveDisposeCleanup: (() => void) | undefined = undefined
 
   constructor(
     protected context: vscode.ExtensionContext,
@@ -150,6 +198,9 @@ export class DataEditorClient implements vscode.Disposable {
     panel: vscode.WebviewPanel
   ) {
     this.panel = panel
+    this.disposeCleanupComplete = new Promise((resolve) => {
+      this.resolveDisposeCleanup = resolve
+    })
     this.panel.webview.onDidReceiveMessage(this.messageReceiver, this)
 
     this.disposables = [
@@ -179,6 +230,8 @@ export class DataEditorClient implements vscode.Disposable {
       clearInterval(this.sendHeartbeatIntervalId)
       this.sendHeartbeatIntervalId = undefined
     }
+    this.viewportSubscription?.cancel()
+    this.viewportSubscription = undefined
 
     for (let i = 0; i < this.disposables.length; i++)
       this.disposables[i].dispose()
@@ -187,6 +240,11 @@ export class DataEditorClient implements vscode.Disposable 
{
   show(): void {
     this.panel.reveal()
   }
+
+  async waitForDisposeCleanup(): Promise<void> {
+    await this.disposeCleanupComplete
+  }
+
   public static async open(
     context: vscode.ExtensionContext,
     view: string,
@@ -212,27 +270,39 @@ export class DataEditorClient implements 
vscode.Disposable {
       panel
     )
 
-    await editor.initialize()
-
-    panel.onDidDispose(async () => {
+    panel.onDidDispose(() => {
       const pathKey = path.resolve(editor.fileToEdit).toLowerCase()
       OPEN_EDITORS.delete(pathKey)
-      await removeActiveSession(editor.sessionId())
-      await editor.dispose()
-    })
 
-    if (isDFDLDebugSessionActive()) {
-      editor.addDisposable(
-        vscode.debug.onDidTerminateDebugSession(async () => {
-          editor.dispose()
+      void (async () => {
+        try {
+          await editor.dispose()
+          await removeActiveSession(editor.sessionId())
+        } finally {
+          editor.resolveDisposeCleanup?.()
+        }
+      })().catch((err) => {
+        getLogger().warn({
+          fn: 'DataEditorClient::onDidDispose',
+          err: {
+            msg: `Failed to dispose data editor: ${String(err)}`,
+            stack: err instanceof Error ? err.stack : undefined,
+          },
         })
-      )
+      })
+    })
+
+    const initialized = await editor.initialize()
+    if (!initialized) {
+      return undefined
     }
+
     return editor
   }
 
-  public async initialize() {
+  public async initialize(): Promise<boolean> {
     checkpointPath = this.configVars.checkpointPath
+    let initialized = false
 
     if (this.fileToEdit !== '') {
       // Case: file passed in directly — check for duplicates now
@@ -244,10 +314,14 @@ export class DataEditorClient implements 
vscode.Disposable {
         )
         OPEN_EDITORS.get(realFilePath)?.reveal()
         this.panel.dispose()
-        return
+        return false
       }
 
-      await this.setupDataEditor()
+      initialized = await this.setupDataEditor()
+      if (!initialized) {
+        this.panel.dispose()
+        return false
+      }
       OPEN_EDITORS.set(realFilePath, this.panel)
     } else {
       // Case: no file passed in — prompt user
@@ -269,33 +343,66 @@ export class DataEditorClient implements 
vscode.Disposable {
           )
           OPEN_EDITORS.get(realFilePath)?.reveal()
           this.panel.dispose()
-          return
+          return false
         }
 
-        await this.setupDataEditor()
+        initialized = await this.setupDataEditor()
+        if (!initialized) {
+          this.panel.dispose()
+          return false
+        }
         OPEN_EDITORS.set(realFilePath, this.panel)
       } else {
         // User cancelled the dialog
         this.panel.dispose()
-        return
+        return false
       }
     }
     // send and initial heartbeat, then send the heartbeat to the webview at 
regular intervals
-    await this.sendHeartbeat()
-    this.sendHeartbeatIntervalId = setInterval(() => {
-      this.sendHeartbeat()
-    }, HEARTBEAT_INTERVAL_MS)
+    if (initialized) {
+      try {
+        await this.resyncWebview()
+      } catch (err) {
+        getLogger().warn({
+          fn: 'DataEditorClient::initialize',
+          err: {
+            msg: `Initial webview sync failed: ${String(err)}`,
+            stack: err instanceof Error ? err.stack : undefined,
+          },
+        })
+      }
+      this.sendHeartbeatIntervalId = setInterval(() => {
+        void (
+          this.hasReceivedWebviewReady
+            ? this.sendHeartbeat()
+            : this.resyncWebview()
+        ).catch((err) => {
+          getLogger().warn({
+            fn: 'DataEditorClient::heartbeatInterval',
+            err: {
+              msg: `Webview sync failed: ${String(err)}`,
+              stack: err instanceof Error ? err.stack : undefined,
+            },
+          })
+        })
+      }, HEARTBEAT_INTERVAL_MS)
+    }
+    return initialized
   }
 
   sessionId(): string {
     return this.omegaSessionId
   }
 
-  private async setupDataEditor() {
+  private async setupDataEditor(): Promise<boolean> {
     assert(
       checkpointPath && checkpointPath.length > 0,
       'checkpointPath is not set'
     )
+    getLogger().info(
+      { fn: 'DataEditorClient::setupDataEditor', fileToEdit: this.fileToEdit },
+      'Starting data editor session setup'
+    )
 
     let data = {
       byteOrderMark: '',
@@ -323,6 +430,14 @@ export class DataEditorClient implements vscode.Disposable 
{
         createSessionResponse.hasFileSize()
           ? (createSessionResponse.getFileSize() as number)
           : 0
+      getLogger().info(
+        {
+          fn: 'DataEditorClient::setupDataEditor',
+          sessionId: this.omegaSessionId,
+          fileSize: data.computedFileSize,
+        },
+        'Created data editor session'
+      )
 
       const contentTypeResponse = await getContentType(
         this.omegaSessionId,
@@ -360,7 +475,7 @@ export class DataEditorClient implements vscode.Disposable {
 
       const msg = isEmojiWindowsError
         ? `Unable to open ${this.fileToEdit}! Data editor doesn't support 
Emojis in filename on Windows.`
-        : `Failed to create session for ${this.fileToEdit}`
+        : `Failed to create session for ${this.fileToEdit}: ${String(err)}`
 
       getLogger().error({
         err: {
@@ -370,10 +485,9 @@ export class DataEditorClient implements vscode.Disposable 
{
       })
       vscode.window.showErrorMessage(msg)
 
-      if (isEmojiWindowsError) {
-        // fine to return early here and not remove session b/c 
addActiveSession doesn't get called for this error. createSession() errors out.
-        return
-      }
+      // fine to return early here and not remove session b/c addActiveSession
+      // doesn't get called when createSession() errors out.
+      return false
     }
 
     // create the viewport
@@ -387,10 +501,22 @@ export class DataEditorClient implements 
vscode.Disposable {
       )
       this.currentViewportId = viewportDataResponse.getViewportId()
       assert(this.currentViewportId.length > 0, 'currentViewportId is not set')
-      await viewportSubscribe(this.panel, this.currentViewportId)
+      this.viewportSubscription = await viewportSubscribe(
+        this.panel,
+        this.currentViewportId
+      )
       await sendViewportRefresh(this.panel, viewportDataResponse)
-    } catch {
-      const msg = `Failed to create viewport for ${this.fileToEdit}`
+      getLogger().info(
+        {
+          fn: 'DataEditorClient::setupDataEditor',
+          viewportId: this.currentViewportId,
+        },
+        'Created initial viewport'
+      )
+    } catch (err) {
+      const msg = `Failed to create viewport for ${this.fileToEdit}: ${String(
+        err
+      )}`
       getLogger().error({
         err: {
           msg: msg,
@@ -398,47 +524,73 @@ export class DataEditorClient implements 
vscode.Disposable {
         },
       })
       vscode.window.showErrorMessage(msg)
+      return false
     }
 
     // send the initial file info to the webview
+    this.fileInfoData = data
     await this.panel.webview.postMessage({
       command: MessageCommand.fileInfo,
       data: data,
     })
+    getLogger().info(
+      {
+        fn: 'DataEditorClient::setupDataEditor',
+        sessionId: this.omegaSessionId,
+        viewportId: this.currentViewportId,
+      },
+      'Posted initial file info to webview'
+    )
+    return true
   }
 
   private async sendHeartbeat() {
     const heartbeatInfo = getCurrentHeartbeatInfo()
 
-    await this.panel.webview.postMessage({
+    const delivered = await this.panel.webview.postMessage({
       command: MessageCommand.heartbeat,
       data: {
         latency: heartbeatInfo.latency,
         omegaEditPort: this.configVars.port,
+        serverCpuCount: heartbeatInfo.serverCpuCount,
         serverCpuLoadAverage: heartbeatInfo.serverCpuLoadAverage,
+        serverTimestamp: heartbeatInfo.serverTimestamp,
         serverUptime: heartbeatInfo.serverUptime,
-        serverUsedMemory: heartbeatInfo.serverUsedMemory,
+        serverResidentMemoryBytes: heartbeatInfo.serverResidentMemoryBytes,
+        serverVirtualMemoryBytes: heartbeatInfo.serverVirtualMemoryBytes,
+        serverPeakResidentMemoryBytes:
+          heartbeatInfo.serverPeakResidentMemoryBytes,
         sessionCount: heartbeatInfo.sessionCount,
         serverInfo: {
           omegaEditPort: this.configVars.port,
           serverVersion: serverInfo.serverVersion,
           serverHostname: serverInfo.serverHostname,
           serverProcessId: serverInfo.serverProcessId,
-          jvmVersion: serverInfo.jvmVersion,
-          jvmVendor: serverInfo.jvmVendor,
-          jvmPath: serverInfo.jvmPath,
+          runtimeKind: serverInfo.runtimeKind,
+          runtimeName: serverInfo.runtimeName,
+          platform: serverInfo.platform,
           availableProcessors: serverInfo.availableProcessors,
+          compiler: serverInfo.compiler,
+          buildType: serverInfo.buildType,
+          cppStandard: serverInfo.cppStandard,
         },
       },
     })
+    getLogger().debug({
+      fn: 'DataEditorClient::sendHeartbeat',
+      delivered,
+      hasReceivedWebviewReady: this.hasReceivedWebviewReady,
+      serverTimestamp: heartbeatInfo.serverTimestamp,
+      sessionCount: heartbeatInfo.sessionCount,
+    })
   }
 
   private async sendChangesInfo() {
     // get the counts from the server
     const counts = await getCounts(this.omegaSessionId, [
-      CountKind.COUNT_COMPUTED_FILE_SIZE,
-      CountKind.COUNT_CHANGE_TRANSACTIONS,
-      CountKind.COUNT_UNDO_TRANSACTIONS,
+      CountKind.COMPUTED_FILE_SIZE,
+      CountKind.CHANGE_TRANSACTIONS,
+      CountKind.UNDO_TRANSACTIONS,
     ])
 
     // accumulate the counts into a single object
@@ -450,18 +602,23 @@ export class DataEditorClient implements 
vscode.Disposable {
     }
     counts.forEach((count) => {
       switch (count.getKind()) {
-        case CountKind.COUNT_COMPUTED_FILE_SIZE:
+        case CountKind.COMPUTED_FILE_SIZE:
           data.computedFileSize = count.getCount()
           break
-        case CountKind.COUNT_CHANGE_TRANSACTIONS:
+        case CountKind.CHANGE_TRANSACTIONS:
           data.changeCount = count.getCount()
           break
-        case CountKind.COUNT_UNDO_TRANSACTIONS:
+        case CountKind.UNDO_TRANSACTIONS:
           data.undoCount = count.getCount()
           break
       }
     })
 
+    this.fileInfoData = {
+      ...this.fileInfoData,
+      ...data,
+    }
+
     // send the accumulated counts to the webview
     await this.panel.webview.postMessage({
       command: MessageCommand.fileInfo,
@@ -486,6 +643,19 @@ export class DataEditorClient implements vscode.Disposable 
{
         }
         break
 
+      case MessageCommand.webviewReady:
+        this.hasReceivedWebviewReady = true
+        getLogger().info(
+          {
+            fn: 'DataEditorClient::messageReceiver',
+            sessionId: this.omegaSessionId,
+            viewportId: this.currentViewportId,
+          },
+          'Received webviewReady from data editor'
+        )
+        await this.resyncWebview()
+        break
+
       case MessageCommand.scrollViewport:
         await this.scrollViewport(
           this.panel,
@@ -522,8 +692,8 @@ export class DataEditorClient implements vscode.Disposable {
         await edit(
           this.omegaSessionId,
           message.data.offset,
-          message.data.originalSegment,
-          message.data.editedSegment
+          fromMessageBytes(message.data.originalSegment),
+          fromMessageBytes(message.data.editedSegment)
         )
         await this.sendChangesInfo()
         break
@@ -648,7 +818,7 @@ export class DataEditorClient implements vscode.Disposable {
           await this.panel.webview.postMessage({
             command: MessageCommand.requestEditedData,
             data: {
-              data: Uint8Array.from(selectionData),
+              data: toMessageBytes(Uint8Array.from(selectionData)),
               dataDisplay: selectionDisplay,
             },
           })
@@ -730,6 +900,38 @@ export class DataEditorClient implements vscode.Disposable 
{
     }
   }
 
+  private async resyncWebview() {
+    getLogger().info({
+      fn: 'DataEditorClient::resyncWebview',
+      sessionId: this.omegaSessionId,
+      viewportId: this.currentViewportId,
+      hasReceivedWebviewReady: this.hasReceivedWebviewReady,
+      hasFileInfo: this.fileInfoData !== undefined,
+    })
+    await this.displayState.sendUIThemeUpdate()
+
+    if (this.fileInfoData) {
+      const delivered = await this.panel.webview.postMessage({
+        command: MessageCommand.fileInfo,
+        data: this.fileInfoData,
+      })
+      getLogger().debug({
+        fn: 'DataEditorClient::resyncWebview',
+        message: 'fileInfo',
+        delivered,
+      })
+    }
+
+    if (this.currentViewportId) {
+      await sendViewportRefresh(
+        this.panel,
+        await getViewportData(this.currentViewportId)
+      )
+    }
+
+    await this.sendHeartbeat()
+  }
+
   private async saveFileSegment(
     fileToSave: string,
     offset: number,
@@ -750,15 +952,15 @@ export class DataEditorClient implements 
vscode.Disposable {
         await del(this.omegaSessionId, 0, offset)
         await this.sendChangesInfo()
       } else {
-        // delete from length to the end of the file and from 0 to offset in a 
single transaction
-        await beginSessionTransaction(this.omegaSessionId)
-        await del(
-          this.omegaSessionId,
-          offset + length,
-          computedFileSize - length
-        )
-        await del(this.omegaSessionId, 0, offset)
-        await endSessionTransaction(this.omegaSessionId)
+        // Trim both sides atomically so undo/redo treats the segment save as 
one edit.
+        await runSessionTransaction(this.omegaSessionId, async () => {
+          await del(
+            this.omegaSessionId,
+            offset + length,
+            computedFileSize - offset - length
+          )
+          await del(this.omegaSessionId, 0, offset)
+        })
         await this.sendChangesInfo()
       }
       // save the segment to the file using the typical save method
@@ -771,7 +973,7 @@ export class DataEditorClient implements vscode.Disposable {
       const saveResponse = await saveSession(
         this.omegaSessionId,
         fileToSave,
-        IOFlags.IO_FLG_OVERWRITE,
+        IOFlags.OVERWRITE,
         offset,
         length
       )
@@ -789,7 +991,7 @@ export class DataEditorClient implements vscode.Disposable {
           const saveResponse2 = await saveSession(
             this.omegaSessionId,
             fileToSave,
-            IOFlags.IO_FLG_FORCE_OVERWRITE,
+            IOFlags.FORCE_OVERWRITE,
             offset,
             length
           )
@@ -819,7 +1021,7 @@ export class DataEditorClient implements vscode.Disposable 
{
     const saveResponse = await saveSession(
       this.omegaSessionId,
       fileToSave,
-      IOFlags.IO_FLG_OVERWRITE
+      IOFlags.OVERWRITE
     )
     if (saveResponse.getSaveStatus() === SaveStatus.MODIFIED) {
       // the file was modified since the session was created, query user to 
overwrite the modified file
@@ -835,7 +1037,7 @@ export class DataEditorClient implements vscode.Disposable 
{
         const saveResponse2 = await saveSession(
           this.omegaSessionId,
           fileToSave,
-          IOFlags.IO_FLG_FORCE_OVERWRITE
+          IOFlags.FORCE_OVERWRITE
         )
         saved = saveResponse2.getSaveStatus() === SaveStatus.SUCCESS
       } else {
@@ -928,17 +1130,25 @@ async function createDataEditorWebviewPanel(
   // Make sure the omega edit port is configured
   configureOmegaEditPort(launchConfigVars)
   omegaEditPort = launchConfigVars.port
+  checkpointPath = launchConfigVars.checkpointPath
+  await setupLogging(launchConfigVars)
 
   // Start the server if it's not already running
-  if (!(await checkServerListening(omegaEditPort, OMEGA_EDIT_HOST))) {
-    await setupLogging(launchConfigVars)
+  const serverListening = await checkServerListening(
+    omegaEditPort,
+    OMEGA_EDIT_HOST
+  )
+  if (!serverListening) {
+    resetOmegaEditConnectionState()
+    clearStoppedServerArtifacts()
     await serverStart()
-    client = await getClient(omegaEditPort, OMEGA_EDIT_HOST)
-    assert(
-      await checkServerListening(omegaEditPort, OMEGA_EDIT_HOST),
-      'server not listening'
-    )
   }
+  await getClient(omegaEditPort, OMEGA_EDIT_HOST)
+  assert(
+    await checkServerListening(omegaEditPort, OMEGA_EDIT_HOST),
+    'server not listening'
+  )
+  serverInfo = await getServerInfo()
 
   // Normalize workspace keyword if needed
   fileToEdit = fileToEdit.replace(
@@ -961,6 +1171,35 @@ function rotateLogFiles(logFile: string): void {
     ctime: Date
   }
 
+  function isRotatedLogFileName(fileName: string): boolean {
+    const parsed = path.parse(logFile)
+    const currentFileName = parsed.base
+    const legacyPrefix = `${currentFileName}.`
+
+    if (fileName === currentFileName) {
+      return false
+    }
+
+    if (fileName.startsWith(legacyPrefix)) {
+      return true
+    }
+
+    if (parsed.ext.length === 0) {
+      return false
+    }
+
+    return (
+      fileName.startsWith(`${parsed.name}.`) && fileName.endsWith(parsed.ext)
+    )
+  }
+
+  function getRotatedLogFileName(timestamp: string): string {
+    const parsed = path.parse(logFile)
+    return parsed.ext.length > 0
+      ? `${parsed.name}.${timestamp}${parsed.ext}`
+      : `${parsed.base}.${timestamp}`
+  }
+
   assert(
     MAX_LOG_FILES > 0,
     'Maximum number of log files must be greater than 0'
@@ -968,12 +1207,11 @@ function rotateLogFiles(logFile: string): void {
 
   if (fs.existsSync(logFile)) {
     const logDir = path.dirname(logFile)
-    const logFileName = path.basename(logFile)
 
     // Get list of existing log files
     const logFiles: LogFile[] = fs
       .readdirSync(logDir)
-      .filter((file) => file.startsWith(logFileName) && file !== logFileName)
+      .filter((file) => isRotatedLogFileName(file))
       .map((file) => ({
         path: path.join(logDir, file),
         ctime: fs.statSync(path.join(logDir, file)).ctime,
@@ -988,7 +1226,7 @@ function rotateLogFiles(logFile: string): void {
 
     // Rename current log file with timestamp and create a new empty file
     const timestamp = new Date().toISOString().replace(/:/g, '-')
-    fs.renameSync(logFile, path.join(logDir, `${logFileName}.${timestamp}`))
+    fs.renameSync(logFile, path.join(logDir, getRotatedLogFileName(timestamp)))
   }
 }
 
@@ -1002,8 +1240,15 @@ async function setupLogging(configVars: 
editor_config.Config): Promise<void> {
     process.env.OMEGA_EDIT_CLIENT_LOG_LEVEL ||
     process.env.OMEGA_EDIT_LOG_LEVEL ||
     configVars.logLevel
+  if (
+    configuredClientLogger?.logFile === logFile &&
+    configuredClientLogger.logLevel === logLevel
+  ) {
+    return
+  }
   rotateLogFiles(logFile)
   setLogger(createSimpleFileLogger(logFile, logLevel))
+  configuredClientLogger = { logFile, logLevel }
   vscode.window.showInformationMessage(`Logging (${logLevel}) to '${logFile}'`)
 }
 
@@ -1011,17 +1256,25 @@ async function sendViewportRefresh(
   panel: vscode.WebviewPanel,
   viewportDataResponse: ViewportDataResponse
 ): Promise<void> {
-  await panel.webview.postMessage({
+  const delivered = await panel.webview.postMessage({
     command: MessageCommand.viewportRefresh,
     data: {
       viewportId: viewportDataResponse.getViewportId(),
       fileOffset: viewportDataResponse.getOffset(),
       length: viewportDataResponse.getLength(),
       bytesLeft: viewportDataResponse.getFollowingByteCount(),
-      data: viewportDataResponse.getData_asU8(),
+      data: toMessageBytes(viewportDataResponse.getData_asU8()),
       capacity: VIEWPORT_CAPACITY_MAX,
     },
   })
+  getLogger().debug({
+    fn: 'sendViewportRefresh',
+    delivered,
+    viewportId: viewportDataResponse.getViewportId(),
+    offset: viewportDataResponse.getOffset(),
+    length: viewportDataResponse.getLength(),
+    bytesLeft: viewportDataResponse.getFollowingByteCount(),
+  })
 }
 
 /**
@@ -1033,28 +1286,17 @@ async function viewportSubscribe(
   panel: vscode.WebviewPanel,
   viewportId: string
 ) {
-  // subscribe to all viewport events
-  client
-    .subscribeToViewportEvents(
-      new EventSubscriptionRequest()
-        .setId(viewportId)
-        .setInterest(ALL_EVENTS & ~ViewportEventKind.VIEWPORT_EVT_MODIFY)
-    )
-    .on('data', async (event: ViewportEvent) => {
+  return await subscribeViewportEvents({
+    viewportId,
+    interest: ALL_EVENTS & ~ViewportEventKind.MODIFY,
+    onEvent: async (event) => {
       getLogger().debug({
         viewportId: event.getViewportId(),
         event: event.getViewportEventKind(),
       })
       await sendViewportRefresh(panel, await getViewportData(viewportId))
-    })
-    .on('error', (err) => {
-      // Call cancelled thrown sometimes when server is shutdown
-      if (
-        !err.message.includes('Call cancelled') &&
-        !err.message.includes('UNAVAILABLE')
-      )
-        throw err
-    })
+    },
+  })
 }
 
 class DisplayState {
@@ -1074,7 +1316,7 @@ class DisplayState {
     this.sendUIThemeUpdate()
   }
 
-  private sendUIThemeUpdate() {
+  public sendUIThemeUpdate() {
     return this.panel.webview.postMessage({
       command: MessageCommand.setUITheme,
       theme: this.colorThemeKind,
@@ -1210,71 +1452,87 @@ function removeDirectory(dirPath: string): void {
   }
 }
 
-export async function serverStop() {
+function resetOmegaEditConnectionState(): void {
+  resetClient()
+}
+
+function clearStoppedServerArtifacts(): void {
   const serverPidFile = getPidFile(omegaEditPort)
   if (fs.existsSync(serverPidFile)) {
-    const pid = parseInt(fs.readFileSync(serverPidFile).toString())
-    if (await stopProcessUsingPID(pid)) {
-      vscode.window.setStatusBarMessage(
-        `Ωedit server stopped on port ${omegaEditPort} with PID ${pid}`,
-        new Promise((resolve) => {
-          setTimeout(() => {
-            resolve(true)
-          }, 4000)
-        })
-      )
-      removeDirectory(checkpointPath)
-    } else {
-      // Check again if the process has stopped after a short delay
-      await new Promise((resolve) => setTimeout(resolve, 500))
-      if (!(await stopProcessUsingPID(pid))) {
-        vscode.window.showErrorMessage(
-          `Ωedit server on port ${omegaEditPort} with PID ${pid} failed to 
stop`
-        )
-      } else {
-        vscode.window.setStatusBarMessage(
-          `Ωedit server stopped on port ${omegaEditPort} with PID ${pid}`,
-          new Promise((resolve) => {
-            setTimeout(() => {
-              resolve(true)
-            }, 4000)
-          })
-        )
-        removeDirectory(checkpointPath)
-      }
+    fs.unlinkSync(serverPidFile)
+  }
+  if (checkpointPath.length > 0) {
+    removeDirectory(checkpointPath)
+  }
+}
+
+export async function serverStop() {
+  resetOmegaEditConnectionState()
+  const serverPidFile = getPidFile(omegaEditPort)
+  if (!fs.existsSync(serverPidFile)) {
+    if (!(await checkServerListening(omegaEditPort, OMEGA_EDIT_HOST))) {
+      clearStoppedServerArtifacts()
     }
+    return
+  }
+
+  const pid = parseInt(fs.readFileSync(serverPidFile).toString())
+  if (Number.isNaN(pid)) {
+    clearStoppedServerArtifacts()
+    return
+  }
+
+  let stopped = await stopProcessUsingPID(pid)
+  if (!stopped) {
+    await new Promise((resolve) => setTimeout(resolve, 500))
+    stopped = await stopProcessUsingPID(pid)
   }
+
+  const serverListening = await checkServerListening(
+    omegaEditPort,
+    OMEGA_EDIT_HOST
+  )
+
+  if (stopped || !serverListening) {
+    vscode.window.setStatusBarMessage(
+      `Ωedit server stopped on port ${omegaEditPort} with PID ${pid}`,
+      new Promise((resolve) => {
+        setTimeout(() => {
+          resolve(true)
+        }, 4000)
+      })
+    )
+    clearStoppedServerArtifacts()
+    return
+  }
+
+  vscode.window.showErrorMessage(
+    `Ωedit server on port ${omegaEditPort} with PID ${pid} failed to stop`
+  )
 }
 
 function generateLogbackConfigFile(
   logFile: string,
   logLevel: string = 'INFO'
 ): string {
-  const dirname = path.dirname(logFile)
-  if (!fs.existsSync(dirname)) {
-    fs.mkdirSync(dirname, { recursive: true })
-  }
-  logLevel = logLevel.toUpperCase()
-  const logbackConfig = `<?xml version="1.0" encoding="UTF-8"?>\n
-<configuration>
-    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
-        <file>${logFile}</file>
-        <encoder>
-            <pattern>[%date{ISO8601}] [%level] [%logger] [%marker] [%thread] - 
%msg MDC: {%mdc}%n</pattern>
-        </encoder>
-    </appender>
-    <root level="${logLevel}">
-        <appender-ref ref="FILE" />
-    </root>
-</configuration>
-`
   const logbackConfigFile = path.join(
     APP_DATA_PATH,
     `serv-${omegaEditPort}.logconf.xml`
   )
   rotateLogFiles(logFile)
-  fs.writeFileSync(logbackConfigFile, logbackConfig)
-  return logbackConfigFile // Return the path to the logback config file
+  return writeLogbackConfigFile(logbackConfigFile, logFile, logLevel)
+}
+
+function getProcessCommandLine(pid: number): string {
+  return child_process
+    .execSync(
+      osCheck(
+        `powershell -NoProfile -Command "(Get-CimInstance Win32_Process 
-Filter \\\"ProcessId = ${pid}\\\").CommandLine"`,
+        `ps -p ${pid} -o command=`
+      )
+    )
+    .toString('utf8')
+    .trim()
 }
 
 async function serverStart() {
@@ -1285,18 +1543,7 @@ async function serverStart() {
     if (!isNaN(pid)) {
       // Ensure PID isn't assigned to a different process before stopping 
process
       try {
-        if (
-          child_process
-            .execSync(
-              osCheck(
-                `wmic process where processid=${pid} get CommandLine`,
-                `ps -p ${pid} -o command=`
-              )
-            )
-            .toString('ascii')
-            .toLowerCase()
-            .includes('omega-edit')
-        ) {
+        if (getProcessCommandLine(pid).toLowerCase().includes('omega-edit')) {
           await serverStop()
         } else {
           fs.unlinkSync(serverPidFile)
@@ -1341,12 +1588,12 @@ async function serverStart() {
 
   // Start the server and wait up to 10 seconds for it to start
   const serverPid = (await Promise.race([
-    startServer(
-      omegaEditPort,
-      OMEGA_EDIT_HOST,
-      getPidFile(omegaEditPort),
-      logConfigFile
-    ),
+    startServer(omegaEditPort, OMEGA_EDIT_HOST, getPidFile(omegaEditPort), {
+      sessionTimeoutMs: SERVER_SESSION_TIMEOUT_MS,
+      cleanupIntervalMs: SERVER_CLEANUP_INTERVAL_MS,
+      shutdownWhenNoSessions: true,
+      logConfigFile,
+    }),
     new Promise((_resolve, reject) => {
       setTimeout(() => {
         reject((): Error => {
diff --git a/src/dataEditor/include/server/LogbackConfig.ts 
b/src/dataEditor/include/server/LogbackConfig.ts
new file mode 100644
index 0000000..282ab4b
--- /dev/null
+++ b/src/dataEditor/include/server/LogbackConfig.ts
@@ -0,0 +1,53 @@
+/*
+ * 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 fs from 'fs'
+import path from 'path'
+
+function ensureParentDirectory(filePath: string): void {
+  const dirname = path.dirname(filePath)
+  if (!fs.existsSync(dirname)) {
+    fs.mkdirSync(dirname, { recursive: true })
+  }
+}
+
+function buildLogbackConfig(logFile: string, logLevel: string): string {
+  return `<?xml version="1.0" encoding="UTF-8"?>\n
+<configuration>
+    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+        <file>${logFile}</file>
+        <encoder>
+            <pattern>[%date{ISO8601}] [%level] [%logger] [%marker] [%thread] - 
%msg MDC: {%mdc}%n</pattern>
+        </encoder>
+    </appender>
+    <root level="${logLevel.toUpperCase()}">
+        <appender-ref ref="FILE" />
+    </root>
+</configuration>
+`
+}
+
+export function writeLogbackConfigFile(
+  logbackConfigFile: string,
+  logFile: string,
+  logLevel: string = 'INFO'
+): string {
+  ensureParentDirectory(logFile)
+  ensureParentDirectory(logbackConfigFile)
+  fs.writeFileSync(logbackConfigFile, buildLogbackConfig(logFile, logLevel))
+  return logbackConfigFile
+}
diff --git a/src/dataEditor/include/server/ServerInfo.ts 
b/src/dataEditor/include/server/ServerInfo.ts
index db871da..4fcc89b 100644
--- a/src/dataEditor/include/server/ServerInfo.ts
+++ b/src/dataEditor/include/server/ServerInfo.ts
@@ -24,33 +24,36 @@ export class ServerInfo implements IServerInfo {
   serverHostname: string = 'unknown'
   serverProcessId: number = 0
   serverVersion: string = 'unknown'
-  jvmVersion: string = 'unknown'
-  jvmVendor: string = 'unknown'
-  jvmPath: string = 'unknown'
+  runtimeKind: string = 'unknown'
+  runtimeName: string = 'unknown'
+  platform: string = 'unknown'
   availableProcessors: number = 0
+  compiler: string = 'unknown'
+  buildType: string = 'unknown'
+  cppStandard: string = 'unknown'
 }
 
 const OMEGA_EDIT_MAX_PORT: number = 65535
 const OMEGA_EDIT_MIN_PORT: number = 1024
+
 export function configureOmegaEditPort(configVars: editor_config.Config): void 
{
-  let omegaEditPort = configVars.port
-  if (omegaEditPort === 0) {
-    if (
-      omegaEditPort <= OMEGA_EDIT_MIN_PORT ||
-      omegaEditPort > OMEGA_EDIT_MAX_PORT
-    ) {
-      const message = `Invalid port ${omegaEditPort} for Ωedit. Use a port 
between ${OMEGA_EDIT_MIN_PORT} and ${OMEGA_EDIT_MAX_PORT}`
-      omegaEditPort = 0
-      throw new Error(message)
-    }
-    if (!fs.existsSync(configVars.checkpointPath)) {
-      fs.mkdirSync(configVars.checkpointPath, { recursive: true })
-    }
-    assert(
-      fs.existsSync(configVars.checkpointPath),
-      'checkpoint path does not exist'
+  const omegaEditPort = configVars.port
+  if (
+    omegaEditPort <= OMEGA_EDIT_MIN_PORT ||
+    omegaEditPort > OMEGA_EDIT_MAX_PORT
+  ) {
+    throw new Error(
+      `Invalid port ${omegaEditPort} for Ωedit. Use a port between 
${OMEGA_EDIT_MIN_PORT} and ${OMEGA_EDIT_MAX_PORT}`
     )
-    assert(omegaEditPort !== 0, 'omegaEditPort is not set')
   }
+
+  if (!fs.existsSync(configVars.checkpointPath)) {
+    fs.mkdirSync(configVars.checkpointPath, { recursive: true })
+  }
+  assert(
+    fs.existsSync(configVars.checkpointPath),
+    'checkpoint path does not exist'
+  )
 }
+
 export type ServerStopPredicate = (context?: any) => boolean
diff --git a/src/dataEditor/include/server/Sessions.ts 
b/src/dataEditor/include/server/Sessions.ts
index d525cc6..9116357 100644
--- a/src/dataEditor/include/server/Sessions.ts
+++ b/src/dataEditor/include/server/Sessions.ts
@@ -14,12 +14,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { destroySession, getLogger, getSessionCount } from '@omega-edit/client'
+import { destroySession, getLogger, resetClient } from '@omega-edit/client'
 import { updateHeartbeatInterval } from './heartbeat'
-import { serverStop } from '../../dataEditorClient'
 
 let activeSessions: string[] = []
 
+function isServerUnavailableError(err: unknown): boolean {
+  if (!(err instanceof Error)) {
+    return false
+  }
+
+  const message = err.message.toLowerCase()
+  return (
+    message.includes('unavailable') ||
+    message.includes('econnrefused') ||
+    message.includes('connection refused') ||
+    message.includes('channel closed') ||
+    message.includes('socket closed')
+  )
+}
+
 export function addActiveSession(sessionId: string): void {
   if (!activeSessions.includes(sessionId)) {
     activeSessions.push(sessionId)
@@ -28,17 +42,32 @@ export function addActiveSession(sessionId: string): void {
   }
 }
 export async function removeActiveSession(sessionId: string) {
+  if (!sessionId) {
+    return
+  }
+
   const index = activeSessions.indexOf(sessionId)
+  if (index === -1) {
+    return
+  }
+
   activeSessions.splice(index, 1)
   updateHeartbeatInterval(activeSessions)
-  await destroySession(sessionId)
 
-  // Only stop the server if there are no active sessions
-  if ((await getSessionCount()) === 0) {
-    getLogger().info(
-      { fn: 'DataEditorClient::removeActiveSession' },
-      'Stopping server!'
-    )
-    await serverStop()
+  try {
+    await destroySession(sessionId)
+    if (activeSessions.length === 0) {
+      resetClient()
+    }
+  } catch (err) {
+    if (isServerUnavailableError(err)) {
+      resetClient()
+      getLogger().info(
+        { fn: 'DataEditorClient::removeActiveSession', sessionId },
+        'Omega Edit server was already stopped during session cleanup'
+      )
+      return
+    }
+    throw err
   }
 }
diff --git a/src/dataEditor/include/server/heartbeat/HeartBeatInfo.ts 
b/src/dataEditor/include/server/heartbeat/HeartBeatInfo.ts
index 5999dc3..e3a8092 100644
--- a/src/dataEditor/include/server/heartbeat/HeartBeatInfo.ts
+++ b/src/dataEditor/include/server/heartbeat/HeartBeatInfo.ts
@@ -17,14 +17,13 @@
 import { IServerHeartbeat } from '@omega-edit/client'
 
 export class HeartbeatInfo implements IServerHeartbeat {
-  omegaEditPort: number = 0 // Ωedit server port
   latency: number = 0 // latency in ms
-  serverCommittedMemory: number = 0 // committed memory in bytes
   serverCpuCount: number = 0 // cpu count
-  serverCpuLoadAverage: number = 0 // cpu load average
-  serverMaxMemory: number = 0 // max memory in bytes
+  serverCpuLoadAverage?: number = 0 // cpu load average
+  serverPeakResidentMemoryBytes?: number = 0 // peak resident memory in bytes
+  serverResidentMemoryBytes?: number = 0 // resident memory in bytes
   serverTimestamp: number = 0 // timestamp in ms
   serverUptime: number = 0 // uptime in ms
-  serverUsedMemory: number = 0 // used memory in bytes
+  serverVirtualMemoryBytes?: number = 0 // virtual memory in bytes
   sessionCount: number = 0 // session count
 }
diff --git a/src/dataEditor/include/server/heartbeat/index.ts 
b/src/dataEditor/include/server/heartbeat/index.ts
index 71c33b3..8378615 100644
--- a/src/dataEditor/include/server/heartbeat/index.ts
+++ b/src/dataEditor/include/server/heartbeat/index.ts
@@ -14,26 +14,36 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { getServerHeartbeat, IServerHeartbeat } from '@omega-edit/client'
+import {
+  IServerHeartbeat,
+  type ServerHeartbeatLoop,
+  startServerHeartbeatLoop,
+} from '@omega-edit/client'
 import { HeartbeatInfo } from './HeartBeatInfo'
 
 const HEARTBEAT_INTERVAL_MS: number = 1000 // 1 second (1000 ms)
 let heartbeatInfo: IServerHeartbeat = new HeartbeatInfo()
-let getHeartbeatIntervalId: NodeJS.Timeout | number | undefined = undefined
+let heartbeatLoop: ServerHeartbeatLoop | undefined = undefined
 
 export function updateHeartbeatInterval(activeSessions: string[]) {
-  if (getHeartbeatIntervalId) {
-    clearInterval(getHeartbeatIntervalId)
+  heartbeatLoop?.stop()
+  heartbeatLoop = undefined
+
+  if (activeSessions.length === 0) {
+    heartbeatInfo = new HeartbeatInfo()
+    return
   }
-  getHeartbeatIntervalId =
-    activeSessions.length > 0
-      ? setInterval(async () => {
-          heartbeatInfo = await getServerHeartbeat(
-            activeSessions,
-            HEARTBEAT_INTERVAL_MS * activeSessions.length
-          )
-        })
-      : undefined
+
+  heartbeatLoop = startServerHeartbeatLoop({
+    intervalMs: HEARTBEAT_INTERVAL_MS * activeSessions.length,
+    getSessionIds: () => [...activeSessions],
+    onHeartbeat: (nextHeartbeatInfo) => {
+      heartbeatInfo = nextHeartbeatInfo
+    },
+    onError: () => {
+      heartbeatInfo = new HeartbeatInfo()
+    },
+  })
 }
 
 export function getCurrentHeartbeatInfo() {
diff --git a/src/dataEditor/svelteWebviewInitializer.ts 
b/src/dataEditor/svelteWebviewInitializer.ts
index b3839e5..1e04a6b 100644
--- a/src/dataEditor/svelteWebviewInitializer.ts
+++ b/src/dataEditor/svelteWebviewInitializer.ts
@@ -40,17 +40,12 @@ export class SvelteWebviewInitializer {
       return webView.asWebviewUri(uri)
     })
     const indexPath = this.getResourceUri('index', context)
-    let indexHTML = this.injectNonce(
-      this.getIndexHTML(context),
-      webView,
-      nonce,
-      scriptUri
-    )!
-    indexHTML = fs
+    let indexHTML = fs
       .readFileSync(indexPath!.fsPath, 'utf-8')
       .replace(/src="\.\/index.js"/, `src="${scriptUri.toString()}"`)
       .replace(/href="\.\/style.css"/, `href="${stylesUri.toString()}"`)
-      .replaceAll(/nonce="__nonce__"/g, `nonce="${nonce}""`)
+      .replaceAll(/nonce="__nonce__"/g, `nonce="${nonce}"`)
+    indexHTML = this.injectNonce(indexHTML, webView, nonce, scriptUri)!
     return indexHTML
   }
   private injectNonce(
@@ -65,17 +60,6 @@ export class SvelteWebviewInitializer {
     )
     return ret
   }
-  private getIndexHTML(context: vscode.ExtensionContext) {
-    const indexFile = vscode.Uri.joinPath(
-      context.extensionUri,
-      'dist',
-      'views',
-      'dataEditor',
-      'index.html'
-    )
-    const indexContent = fs.readFileSync(indexFile.fsPath).toString()
-    return indexContent
-  }
   // get a nonce for use in a content security policy
   private getNonce(): string {
     let text = ''
diff --git a/src/svelte/src/App.svelte b/src/svelte/src/App.svelte
index dd79899..3b430c8 100644
--- a/src/svelte/src/App.svelte
+++ b/src/svelte/src/App.svelte
@@ -17,6 +17,7 @@ limitations under the License.
 
 <script lang="ts">
   import './app.css'
+  import { onMount } from 'svelte'
 
   import {
     bytesPerRow,
@@ -66,6 +67,12 @@ limitations under the License.
   import Help from './components/layouts/Help.svelte'
   import { viewportByteIndicators } from 'utilities/highlights'
 
+  onMount(() => {
+    vscode.postMessage({
+      command: MessageCommand.webviewReady,
+    })
+  })
+
   function requestEditedData() {
     if ($requestable) {
       vscode.postMessage({
@@ -224,8 +231,8 @@ limitations under the License.
       command: MessageCommand.applyChanges,
       data: {
         offset: editedOffset,
-        originalSegment: originalData,
-        editedSegment: editedData,
+        originalSegment: Array.from(originalData),
+        editedSegment: Array.from(editedData),
       },
     })
     clearDataDisplays()
@@ -286,10 +293,13 @@ limitations under the License.
 
       case MessageCommand.requestEditedData:
         $editorSelection = msg.data.data.dataDisplay
+        const editedBytes = Array.isArray(msg.data.data.data)
+          ? msg.data.data.data
+          : [msg.data.data.data]
         if ($editMode === EditByteModes.Multiple) {
-          $editedDataSegment = new Uint8Array(msg.data.data.data)
+          $editedDataSegment = new Uint8Array(editedBytes)
         } else {
-          $editedDataSegment[0] = msg.data.data.data
+          $editedDataSegment = new Uint8Array([editedBytes[0] ?? 0])
         }
         $selectionDataStore.endOffset =
           $selectionDataStore.startOffset + $editedDataSegment.byteLength - 1
@@ -305,7 +315,7 @@ limitations under the License.
         // the viewport has been refreshed, so the editor views need to be 
updated
         const { data, fileOffset, length, bytesLeft } = msg.data.data
         $viewport = {
-          data: data,
+          data: new Uint8Array(data ?? []),
           fileOffset: fileOffset,
           length: length,
           bytesLeft: bytesLeft,
diff --git 
a/src/svelte/src/components/DataDisplays/CustomByteDisplay/DataLineFeed.svelte 
b/src/svelte/src/components/DataDisplays/CustomByteDisplay/DataLineFeed.svelte
index c268fa6..3487144 100644
--- 
a/src/svelte/src/components/DataDisplays/CustomByteDisplay/DataLineFeed.svelte
+++ 
b/src/svelte/src/components/DataDisplays/CustomByteDisplay/DataLineFeed.svelte
@@ -457,7 +457,7 @@ limitations under the License.
       command: MessageCommand.editorOnChange,
       data: {
         fileOffset: $selectionDataStore.startOffset + viewportData.fileOffset,
-        selectionData: $editedDataSegment,
+        selectionData: Array.from($editedDataSegment),
         encoding: forcedEncoding ? forcedEncoding : $editorEncoding,
         selectionSize: $selectionSize,
         editMode: $editMode,
diff --git a/src/svelte/src/components/DataMetrics/DataMetrics.svelte 
b/src/svelte/src/components/DataMetrics/DataMetrics.svelte
index f54148d..acccab3 100644
--- a/src/svelte/src/components/DataMetrics/DataMetrics.svelte
+++ b/src/svelte/src/components/DataMetrics/DataMetrics.svelte
@@ -20,7 +20,7 @@ limitations under the License.
   import { MessageCommand } from '../../utilities/message'
   import { onMount } from 'svelte'
   import Input from '../Inputs/Input/Input.svelte'
-  import { addressRadix, viewport } from '../../stores'
+  import { addressRadix, fileMetrics, viewport } from '../../stores'
   import { DATA_PROFILE_MAX_LENGTH } from '../../stores/configuration'
   import { radixToString, regexEditDataTest } from '../../utilities/display'
   import ISO6391 from 'iso-639-1'
@@ -162,11 +162,21 @@ limitations under the License.
       `Profiling bytes from ${startOffset} to ${startOffset + length}...`,
       0
     )
+    length = length === $fileMetrics.diskSize ? Math.max(0, length - 1) : 
length
+
+    if (length < 0) {
+      vscode.postMessage({
+        command: MessageCommand.showMessage,
+        data: `Invalid data length ${length}. Cannot profile data.`,
+      })
+      return
+    }
+
     vscode.postMessage({
       command: MessageCommand.profile,
       data: {
         startOffset: startOffset,
-        length: length,
+        length: length <= 0 ? DATA_PROFILE_MAX_LENGTH : length,
       },
     })
   }
diff --git a/src/svelte/src/components/ServerMetrics/ServerMetrics.svelte 
b/src/svelte/src/components/ServerMetrics/ServerMetrics.svelte
index 97b03fa..d88456a 100644
--- a/src/svelte/src/components/ServerMetrics/ServerMetrics.svelte
+++ b/src/svelte/src/components/ServerMetrics/ServerMetrics.svelte
@@ -23,16 +23,21 @@ limitations under the License.
     serverCpuLoadAverage: 0,
     serverTimestamp: 0,
     serverUptime: 0,
-    serverUsedMemory: 0,
+    serverResidentMemoryBytes: 0,
+    serverVirtualMemoryBytes: 0,
+    serverPeakResidentMemoryBytes: 0,
     sessionCount: 0,
     omegaEditPort: 0,
     serverVersion: 'Unknown',
     serverHostname: 'Unknown',
     serverProcessId: 0,
-    jvmVersion: 'Unknown',
-    jvmVendor: 'Unknown',
-    jvmPath: 'Unknown',
+    runtimeKind: 'Unknown',
+    runtimeName: 'Unknown',
+    platform: 'Unknown',
     availableProcessors: 0,
+    compiler: 'Unknown',
+    buildType: 'Unknown',
+    cppStandard: 'Unknown',
   }
   let timerId: NodeJS.Timeout
 
@@ -62,9 +67,10 @@ limitations under the License.
       uptimeString +=
         minutes === 1 ? `${minutes} minute, ` : `${minutes} minutes, `
     }
-    return uptimeString + (seconds === 1)
-      ? `${seconds} second`
-      : `${seconds} seconds`
+    return (
+      uptimeString +
+      (seconds === 1 ? `${seconds} second` : `${seconds} seconds`)
+    )
   }
 
   window.addEventListener('message', (msg) => {
@@ -74,17 +80,25 @@ limitations under the License.
         heartbeat.serverCpuLoadAverage = msg.data.data.serverCpuLoadAverage
         heartbeat.serverTimestamp = msg.data.data.serverTimestamp
         heartbeat.serverUptime = msg.data.data.serverUptime
-        heartbeat.serverUsedMemory = msg.data.data.serverUsedMemory
+        heartbeat.serverResidentMemoryBytes =
+          msg.data.data.serverResidentMemoryBytes ?? 0
+        heartbeat.serverVirtualMemoryBytes =
+          msg.data.data.serverVirtualMemoryBytes ?? 0
+        heartbeat.serverPeakResidentMemoryBytes =
+          msg.data.data.serverPeakResidentMemoryBytes ?? 0
         heartbeat.sessionCount = msg.data.data.sessionCount
         heartbeat.omegaEditPort = msg.data.data.serverInfo.omegaEditPort
         heartbeat.serverVersion = msg.data.data.serverInfo.serverVersion
         heartbeat.serverHostname = msg.data.data.serverInfo.serverHostname
         heartbeat.serverProcessId = msg.data.data.serverInfo.serverProcessId
-        heartbeat.jvmVersion = msg.data.data.serverInfo.jvmVersion
-        heartbeat.jvmVendor = msg.data.data.serverInfo.jvmVendor
-        heartbeat.jvmPath = msg.data.data.serverInfo.jvmPath
+        heartbeat.runtimeKind = msg.data.data.serverInfo.runtimeKind
+        heartbeat.runtimeName = msg.data.data.serverInfo.runtimeName
+        heartbeat.platform = msg.data.data.serverInfo.platform
         heartbeat.availableProcessors =
           msg.data.data.serverInfo.availableProcessors
+        heartbeat.compiler = msg.data.data.serverInfo.compiler
+        heartbeat.buildType = msg.data.data.serverInfo.buildType
+        heartbeat.cppStandard = msg.data.data.serverInfo.cppStandard
 
         // set the serverTimestamp to 0 after 5 seconds of no heartbeat to 
indicate that no heartbeat has been received
         clearTimeout(timerId)
@@ -135,21 +149,21 @@ limitations under the License.
           <b>CPU Load Avg:</b>
           {heartbeat.serverCpuLoadAverage.toFixed(2)},
         {/if}
-        {#if heartbeat.serverUsedMemory > 0}
-          <b>Memory Usage:</b>
-          {heartbeat.serverUsedMemory},
+        {#if heartbeat.serverResidentMemoryBytes > 0}
+          <b>Resident Memory:</b>
+          {heartbeat.serverResidentMemoryBytes},
         {/if}
         {#if heartbeat.serverProcessId > 0}
           <b>Process ID:</b>
           {heartbeat.serverProcessId},
         {/if}
-        {#if heartbeat.jvmVersion.length > 0}
-          <b>JVM Version:</b>
-          {heartbeat.jvmVersion}
+        {#if heartbeat.runtimeName.length > 0}
+          <b>Runtime:</b>
+          {heartbeat.runtimeName}
         {/if}
-        {#if heartbeat.jvmVendor.length > 0}
-          <b>JVM Vendor:</b>
-          {heartbeat.jvmVendor}
+        {#if heartbeat.platform.length > 0}
+          <b>Platform:</b>
+          {heartbeat.platform}
         {/if}
       </div>
     </FlexContainer>
diff --git a/src/svelte/src/components/dataEditor.svelte 
b/src/svelte/src/components/dataEditor.svelte
index 33b0133..a070d12 100644
--- a/src/svelte/src/components/dataEditor.svelte
+++ b/src/svelte/src/components/dataEditor.svelte
@@ -223,8 +223,8 @@ limitations under the License.
       command: MessageCommand.applyChanges,
       data: {
         offset: editedOffset,
-        originalSegment: originalData,
-        editedSegment: editedData,
+        originalSegment: Array.from(originalData),
+        editedSegment: Array.from(editedData),
       },
     })
     clearDataDisplays()
@@ -283,10 +283,13 @@ limitations under the License.
 
       case MessageCommand.requestEditedData:
         $editorSelection = msg.data.data.dataDisplay
+        const editedBytes = Array.isArray(msg.data.data.data)
+          ? msg.data.data.data
+          : [msg.data.data.data]
         if ($editMode === EditByteModes.Multiple) {
-          $editedDataSegment = new Uint8Array(msg.data.data.data)
+          $editedDataSegment = new Uint8Array(editedBytes)
         } else {
-          $editedDataSegment[0] = msg.data.data.data
+          $editedDataSegment = new Uint8Array([editedBytes[0] ?? 0])
         }
         $selectionDataStore.endOffset =
           $selectionDataStore.startOffset + $editedDataSegment.byteLength - 1
@@ -298,10 +301,10 @@ limitations under the License.
       case MessageCommand.viewportRefresh:
         // the viewport has been refreshed, so the editor views need to be 
updated
         $viewport = {
-          data: msg.data.data.viewportData,
-          fileOffset: msg.data.data.viewportOffset,
-          length: msg.data.data.viewportLength,
-          bytesLeft: msg.data.data.viewportFollowingByteCount,
+          data: new Uint8Array(msg.data.data.data ?? []),
+          fileOffset: msg.data.data.fileOffset,
+          length: msg.data.data.length,
+          bytesLeft: msg.data.data.bytesLeft,
         } as ViewportData_t
 
         break
diff --git a/src/svelte/src/utilities/message.ts 
b/src/svelte/src/utilities/message.ts
index 749cf98..caef95f 100644
--- a/src/svelte/src/utilities/message.ts
+++ b/src/svelte/src/utilities/message.ts
@@ -37,6 +37,7 @@ export enum MessageCommand {
   undoChange,
   updateLogicalDisplay,
   viewportRefresh,
+  webviewReady,
 }
 
 export enum MessageLevel {
diff --git a/src/tests/omegaEditServerLifecycle.ts 
b/src/tests/omegaEditServerLifecycle.ts
new file mode 100644
index 0000000..2c9e0eb
--- /dev/null
+++ b/src/tests/omegaEditServerLifecycle.ts
@@ -0,0 +1,147 @@
+/*
+ * 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 assert from 'assert'
+import * as net from 'net'
+import * as path from 'path'
+import fs from 'fs'
+import {
+  createSession,
+  getClient,
+  resetClient,
+  startServer,
+  stopProcessUsingPID,
+} from '@omega-edit/client'
+import { TEST_SCHEMA } from './suite/common'
+import XDGAppPaths from 'xdg-app-paths'
+import { writeLogbackConfigFile } from 
'../dataEditor/include/server/LogbackConfig'
+
+const reapPort = 9010
+const logLevel = 'debug'
+const OMEGA_EDIT_HOST = '127.0.0.1'
+const APP_DATA_PATH = XDGAppPaths({ name: 'omega_edit' }).data()
+
+function getTestPidFile(serverPort: number) {
+  return path.join(APP_DATA_PATH, `test-serv-${serverPort}.pid`)
+}
+
+function isServerListening(port: number, host: string): Promise<boolean> {
+  return new Promise((resolve) => {
+    const socket = new net.Socket()
+    socket.setTimeout(500)
+    socket.on('connect', () => {
+      socket.destroy()
+      resolve(true)
+    })
+    socket.on('timeout', () => {
+      socket.destroy()
+      resolve(false)
+    })
+    socket.on('error', () => {
+      socket.destroy()
+      resolve(false)
+    })
+    socket.connect(port, host)
+  })
+}
+
+async function waitForServerListeningState(
+  port: number,
+  host: string,
+  expected: boolean,
+  timeoutMs: number
+): Promise<void> {
+  const start = Date.now()
+  while (Date.now() - start < timeoutMs) {
+    if ((await isServerListening(port, host)) === expected) {
+      return
+    }
+    await new Promise((resolve) => setTimeout(resolve, 100))
+  }
+  throw new Error(
+    `Timed out waiting for server on ${host}:${port} to become ${expected ? 
'available' : 'unavailable'}`
+  )
+}
+
+async function cleanupArtifacts(
+  pidFile: string,
+  logConfigFile: string,
+  serverLogFile: string,
+  checkpointDir: string
+): Promise<void> {
+  resetClient()
+
+  if (fs.existsSync(pidFile)) {
+    const pid = parseInt(fs.readFileSync(pidFile).toString())
+    if (!Number.isNaN(pid)) {
+      await stopProcessUsingPID(pid)
+    }
+    fs.rmSync(pidFile, { force: true })
+  }
+
+  if (fs.existsSync(logConfigFile)) {
+    fs.rmSync(logConfigFile, { force: true })
+  }
+  if (fs.existsSync(serverLogFile)) {
+    fs.rmSync(serverLogFile, { force: true })
+  }
+  if (fs.existsSync(checkpointDir)) {
+    fs.rmSync(checkpointDir, { recursive: true, force: true })
+  }
+}
+
+async function main(): Promise<void> {
+  const pidFile = getTestPidFile(reapPort)
+  const serverLogFile = path.join(APP_DATA_PATH, `test-serv-${reapPort}.log`)
+  const logConfigFile = writeLogbackConfigFile(
+    path.join(APP_DATA_PATH, `test-serv-${reapPort}.logconf.xml`),
+    serverLogFile,
+    logLevel
+  )
+  const checkpointDir = path.join(APP_DATA_PATH, `.checkpoint-${reapPort}`)
+
+  resetClient()
+
+  try {
+    const serverPid = await startServer(reapPort, OMEGA_EDIT_HOST, pidFile, {
+      sessionTimeoutMs: 1000,
+      cleanupIntervalMs: 250,
+      shutdownWhenNoSessions: true,
+      logConfigFile,
+    })
+
+    assert.ok(serverPid)
+    await waitForServerListeningState(reapPort, OMEGA_EDIT_HOST, true, 5000)
+
+    await getClient(reapPort, OMEGA_EDIT_HOST)
+    const createSessionResponse = await createSession(
+      TEST_SCHEMA,
+      undefined,
+      checkpointDir
+    )
+    assert.ok(createSessionResponse.getSessionId().length > 0)
+
+    await waitForServerListeningState(reapPort, OMEGA_EDIT_HOST, false, 10000)
+  } finally {
+    await cleanupArtifacts(pidFile, logConfigFile, serverLogFile, 
checkpointDir)
+  }
+}
+
+main().catch((err) => {
+  console.error(err)
+  process.exit(1)
+})
diff --git a/src/tests/suite/dataEditor.test.ts 
b/src/tests/suite/dataEditor.test.ts
index 4dc502f..fbc577d 100644
--- a/src/tests/suite/dataEditor.test.ts
+++ b/src/tests/suite/dataEditor.test.ts
@@ -36,6 +36,7 @@ import {
   OMEGA_EDIT_HOST,
   SERVER_START_TIMEOUT,
 } from '../../dataEditor/dataEditorClient'
+import { writeLogbackConfigFile } from 
'../../dataEditor/include/server/LogbackConfig'
 
 const testPort = 9009 // use a different port than the default for testing to 
avoid conflicts with running servers
 const logLevel = 'debug'
@@ -44,36 +45,6 @@ function getTestPidFile(serverPort: number) {
   return path.join(APP_DATA_PATH, `test-serv-${serverPort}.pid`)
 }
 
-function generateTestLogbackConfigFile(
-  logFile: string,
-  logLevel: string
-): string {
-  const dirname = path.dirname(logFile)
-  if (!fs.existsSync(dirname)) {
-    fs.mkdirSync(dirname, { recursive: true })
-  }
-  logLevel = logLevel.toUpperCase()
-  const logbackConfig = `<?xml version="1.0" encoding="UTF-8"?>\n
-<configuration>
-    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
-        <file>${logFile}</file>
-        <encoder>
-            <pattern>[%date{ISO8601}] [%level] [%logger] [%marker] [%thread] - 
%msg MDC: {%mdc}%n</pattern>
-        </encoder>
-    </appender>
-    <root level="${logLevel}">
-        <appender-ref ref="FILE" />
-    </root>
-</configuration>
-`
-  const logbackConfigFile = path.join(
-    APP_DATA_PATH,
-    `test-serv-${testPort}.logconf.xml`
-  )
-  fs.writeFileSync(logbackConfigFile, logbackConfig)
-  return logbackConfigFile // Return the path to the logback config file
-}
-
 suite('Data Editor Test Suite', () => {
   // NOTE: Currently failing after glob update. Maybe add back in later?
   // test('data edit command exists', async () => {
@@ -86,13 +57,18 @@ suite('Data Editor Test Suite', () => {
   suite('Editor Service', () => {
     const pidFile = getTestPidFile(testPort)
     const serverLogFile = path.join(APP_DATA_PATH, `test-serv-${testPort}.log`)
-    const logConfigFile = generateTestLogbackConfigFile(serverLogFile, 
logLevel)
     const logFile = path.join(APP_DATA_PATH, `test-dataEditor-${testPort}.log`)
+    let logConfigFile = ''
     setLogger(createSimpleFileLogger(logFile, logLevel))
 
     before(async () => {
+      logConfigFile = writeLogbackConfigFile(
+        path.join(APP_DATA_PATH, `test-serv-${testPort}.logconf.xml`),
+        serverLogFile,
+        logLevel
+      )
       const serverPid = (await Promise.race([
-        startServer(testPort, OMEGA_EDIT_HOST, pidFile, logConfigFile),
+        startServer(testPort, OMEGA_EDIT_HOST, pidFile, { logConfigFile }),
         new Promise((resolve, reject) => {
           setTimeout(
             () =>
@@ -115,6 +91,9 @@ suite('Data Editor Test Suite', () => {
       const pid = parseInt(fs.readFileSync(pidFile).toString())
       console.log(pid)
       assert.strictEqual(await stopProcessUsingPID(pid), true)
+      if (fs.existsSync(logConfigFile)) {
+        fs.rmSync(logConfigFile, { force: true })
+      }
     })
 
     test('is running', async () => {
@@ -161,6 +140,7 @@ suite('Data Editor Test Suite', () => {
 
       // Close the data editor panel
       await dataEditWebView.panel.dispose()
+      await dataEditWebView.waitForDisposeCleanup()
 
       // Verify that the panel has been disposed
       assert.strictEqual(isDisposed, true)
diff --git a/src/tests/suite/omegaEditClientLogger.test.ts 
b/src/tests/suite/omegaEditClientLogger.test.ts
new file mode 100644
index 0000000..151338b
--- /dev/null
+++ b/src/tests/suite/omegaEditClientLogger.test.ts
@@ -0,0 +1,47 @@
+/*
+ * 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 assert from 'assert'
+import * as path from 'path'
+import { PROJECT_ROOT } from './common'
+
+suite('OmegaEdit Client Logger Test Suite', () => {
+  test('getLogger works without explicit setup', () => {
+    const loggerModulePath = path.join(
+      PROJECT_ROOT,
+      'node_modules',
+      '@omega-edit',
+      'client',
+      'dist',
+      'cjs',
+      'logger.js'
+    )
+    delete require.cache[require.resolve(loggerModulePath)]
+
+    const loggerModule = require(loggerModulePath) as {
+      getLogger: () => {
+        info: (...args: unknown[]) => void
+        isLevelEnabled: (level: string) => boolean
+      }
+    }
+
+    const logger = loggerModule.getLogger()
+    assert.ok(logger)
+    assert.strictEqual(typeof logger.info, 'function')
+    assert.strictEqual(logger.isLevelEnabled('info'), true)
+  })
+})
diff --git a/src/tests/suite/utils.test.ts b/src/tests/suite/utils.test.ts
index 9649161..17f06c9 100644
--- a/src/tests/suite/utils.test.ts
+++ b/src/tests/suite/utils.test.ts
@@ -55,7 +55,7 @@ suite('Utils Test Suite', () => {
     dataEditor: {
       port: 9000,
       logging: {
-        file: '${workspaceFolder}/dataEditor-${omegaEditPort}.log',
+        file: '',
         level: 'info',
       },
     },
diff --git a/src/utils.ts b/src/utils.ts
index 8854bc7..1626a7b 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -186,7 +186,7 @@ export function getConfig(jsonArgs: object): 
vscode.DebugConfiguration {
       port: 9000,
       logging: {
         level: 'info',
-        file: '${workspaceFolder}/dataEditor-${omegaEditPort}.log',
+        file: '',
       },
     }),
     dfdlDebugger: defaultConf.get('dfdlDebugger', {
diff --git a/vite.config.mjs b/vite.config.mjs
index 2728db0..8a63489 100644
--- a/vite.config.mjs
+++ b/vite.config.mjs
@@ -172,12 +172,8 @@ async function copyToPkgDirPlugin() {
     { from: 'package.json', to: `${pkg_dir}/package.json` },
     { from: 'yarn.lock', to: `${pkg_dir}/yarn.lock` },
     {
-      from: 'node_modules/@omega-edit/server/out/bin',
-      to: `${pkg_dir}/node_modules/@omega-edit/server/out/bin`,
-    },
-    {
-      from: 'node_modules/@omega-edit/server/out/lib',
-      to: `${pkg_dir}/node_modules/@omega-edit/server/out/lib`,
+      from: 'node_modules/@omega-edit/server/out',
+      to: `${pkg_dir}/node_modules/@omega-edit/server/out`,
     },
     {
       from: 'node_modules/@vscode/webview-ui-toolkit',
@@ -249,7 +245,7 @@ export default defineConfig(({ mode }) => {
         input: {
           extension: path.resolve(__dirname, 'src/adapter/extension.ts'),
         },
-        external: ['vscode', ...builtinModules, /^node:.*/],
+        external: ['vscode', '@omega-edit/client', ...builtinModules, 
/^node:.*/],
         output: {
           entryFileNames: 'extension.js',
           format: 'cjs',
diff --git a/yarn.lock b/yarn.lock
index f12f1e6..98865df 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -532,23 +532,22 @@
     "@nodelib/fs.scandir" "2.1.5"
     fastq "^1.6.0"
 
-"@omega-edit/client@^1.0.1":
-  version "1.0.1"
-  resolved 
"https://registry.yarnpkg.com/@omega-edit/client/-/client-1.0.1.tgz#8234b9427b3c62d022c59421a68683083cb53a67";
-  integrity 
sha512-M2K23JtdRyZO+gm1nXG642bhk4vM9DxoZWvBVKTjndSaBNc6cJgpUkRwxPMLr7lyvvU+AWyJcV+XyI9pfYTT0Q==
+"@omega-edit/[email protected]":
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/@omega-edit/client/-/client-2.0.0.tgz#7f0a4c0237d87b5b9f99c04d7f5351b17b816943";
+  integrity 
sha512-CuRKUASQg1bkGdjxQgaOKhfIyuuRw3CiEfXSoyqe0vn3EgmxbnhOMfGh88qdwLcz4O1PM8uXLBWcnViwQzNoCQ==
   dependencies:
     "@grpc/grpc-js" "1.12.2"
-    "@omega-edit/server" "1.0.1"
-    "@types/google-protobuf" "3.15.12"
-    google-protobuf "3.21.4"
-    pid-port "2.0.0"
-    pino "10.0.0"
+    "@omega-edit/server" "2.0.0"
+    "@protobuf-ts/runtime" "^2.11.1"
+    "@protobuf-ts/runtime-rpc" "^2.11.1"
+    pino "10.3.1"
     wait-port "1.1.0"
 
-"@omega-edit/[email protected]":
-  version "1.0.1"
-  resolved 
"https://registry.yarnpkg.com/@omega-edit/server/-/server-1.0.1.tgz#8d71cc1c84f98211097e3596404ea42527c4311c";
-  integrity 
sha512-ayQer7NmvM2abjk56m4mjYbeW240Vt57Ia3i6xkiuGjg0Ddh6C9lRiDgqnYwFLxOwci5rBPQjF06fOMhLzsdoQ==
+"@omega-edit/[email protected]":
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/@omega-edit/server/-/server-2.0.0.tgz#eea4a07442b1eb22470151eaebf3dacffc24b67d";
+  integrity 
sha512-4Ivl96rGaaL5iYjaMN6ySTdjOOh0FqJMbX71qdGuKogsKZZ6+5QYyaoUkxIxamLcXgAOM6eBPDze4r0j3DVtfw==
 
 "@parcel/[email protected]":
   version "2.5.1"
@@ -639,6 +638,11 @@
     "@parcel/watcher-win32-ia32" "2.5.1"
     "@parcel/watcher-win32-x64" "2.5.1"
 
+"@pinojs/redact@^0.4.0":
+  version "0.4.0"
+  resolved 
"https://registry.yarnpkg.com/@pinojs/redact/-/redact-0.4.0.tgz#c3de060dd12640dcc838516aa2a6803cc7b2e9d6";
+  integrity 
sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==
+
 "@pkgjs/parseargs@^0.11.0":
   version "0.11.0"
   resolved 
"https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33";
@@ -649,6 +653,18 @@
   resolved 
"https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.29.tgz#5a40109a1ab5f84d6fd8fc928b19f367cbe7e7b1";
   integrity 
sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==
 
+"@protobuf-ts/runtime-rpc@^2.11.1":
+  version "2.11.1"
+  resolved 
"https://registry.yarnpkg.com/@protobuf-ts/runtime-rpc/-/runtime-rpc-2.11.1.tgz#a6eb2f384bceae8d23a01d0b0e37faf0af36c179";
+  integrity 
sha512-4CqqUmNA+/uMz00+d3CYKgElXO9VrEbucjnBFEjqI4GuDrEQ32MaI3q+9qPBvIGOlL4PmHXrzM32vBPWRhQKWQ==
+  dependencies:
+    "@protobuf-ts/runtime" "^2.11.1"
+
+"@protobuf-ts/runtime@^2.11.1":
+  version "2.11.1"
+  resolved 
"https://registry.yarnpkg.com/@protobuf-ts/runtime/-/runtime-2.11.1.tgz#ee2bf2fac6e2d8deac0ca63471a77481548e5553";
+  integrity 
sha512-KuDaT1IfHkugM2pyz+FwiY80ejWrkH1pAtOBOZFuR6SXEFTsnb/jiQWQ1rCIrcKx2BtyxnxW6BWwsVSA/Ie+WQ==
+
 "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
   version "1.1.2"
   resolved 
"https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf";
@@ -832,11 +848,6 @@
   resolved 
"https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz#4584a8a87b29188a4c1fe987a9fcf701e256d86c";
   integrity 
sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==
 
-"@sec-ant/readable-stream@^0.4.1":
-  version "0.4.1"
-  resolved 
"https://registry.yarnpkg.com/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz#60de891bb126abfdc5410fdc6166aca065f10a0c";
-  integrity 
sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==
-
 "@secretlint/config-creator@^10.2.2":
   version "10.2.2"
   resolved 
"https://registry.yarnpkg.com/@secretlint/config-creator/-/config-creator-10.2.2.tgz#5d646e83bb2aacfbd5218968ceb358420b4c2cb3";
@@ -949,11 +960,6 @@
   resolved 
"https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz#719df7fb41766bc143369eaa0dd56d8dc87c9958";
   integrity 
sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==
 
-"@sindresorhus/merge-streams@^4.0.0":
-  version "4.0.0"
-  resolved 
"https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz#abb11d99aeb6d27f1b563c38147a72d50058e339";
-  integrity 
sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==
-
 "@standard-schema/spec@^1.0.0":
   version "1.0.0"
   resolved 
"https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.0.0.tgz#f193b73dc316c4170f2e82a881da0f550d551b9c";
@@ -1098,11 +1104,6 @@
     "@types/minimatch" "^5.1.2"
     "@types/node" "*"
 
-"@types/[email protected]":
-  version "3.15.12"
-  resolved 
"https://registry.yarnpkg.com/@types/google-protobuf/-/google-protobuf-3.15.12.tgz#eb2ba0eddd65712211a2b455dc6071d665ccf49b";
-  integrity 
sha512-40um9QqwHjRS92qnOaDpL7RmDK15NuZYo9HihiJRbYkMQZlWnuH8AdvbMy8/o6lgLmKbDUKa+OALCltHdbOTpQ==
-
 "@types/http-cache-semantics@^4.0.2":
   version "4.0.4"
   resolved 
"https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4";
@@ -2309,24 +2310,6 @@ execa@^8.0.1:
     signal-exit "^4.1.0"
     strip-final-newline "^3.0.0"
 
-execa@^9.6.0:
-  version "9.6.0"
-  resolved 
"https://registry.yarnpkg.com/execa/-/execa-9.6.0.tgz#38665530e54e2e018384108322f37f35ae74f3bc";
-  integrity 
sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==
-  dependencies:
-    "@sindresorhus/merge-streams" "^4.0.0"
-    cross-spawn "^7.0.6"
-    figures "^6.1.0"
-    get-stream "^9.0.0"
-    human-signals "^8.0.1"
-    is-plain-obj "^4.1.0"
-    is-stream "^4.0.1"
-    npm-run-path "^6.0.0"
-    pretty-ms "^9.2.0"
-    signal-exit "^4.1.0"
-    strip-final-newline "^4.0.0"
-    yoctocolors "^2.1.1"
-
 exenv-es6@^1.1.1:
   version "1.1.1"
   resolved 
"https://registry.yarnpkg.com/exenv-es6/-/exenv-es6-1.1.1.tgz#80b7a8c5af24d53331f755bac07e84abb1f6de67";
@@ -2394,13 +2377,6 @@ fdir@^6.2.0, fdir@^6.4.4, fdir@^6.5.0:
   resolved 
"https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350";
   integrity 
sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
 
-figures@^6.1.0:
-  version "6.1.0"
-  resolved 
"https://registry.yarnpkg.com/figures/-/figures-6.1.0.tgz#935479f51865fa7479f6fa94fc6fc7ac14e62c4a";
-  integrity 
sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==
-  dependencies:
-    is-unicode-supported "^2.0.0"
-
 fill-range@^7.1.1:
   version "7.1.1"
   resolved 
"https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292";
@@ -2544,14 +2520,6 @@ get-stream@^8.0.1:
   resolved 
"https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2";
   integrity 
sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==
 
-get-stream@^9.0.0:
-  version "9.0.1"
-  resolved 
"https://registry.yarnpkg.com/get-stream/-/get-stream-9.0.1.tgz#95157d21df8eb90d1647102b63039b1df60ebd27";
-  integrity 
sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==
-  dependencies:
-    "@sec-ant/readable-stream" "^0.4.1"
-    is-stream "^4.0.1"
-
 get-tsconfig@^4.7.5:
   version "4.13.6"
   resolved 
"https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.13.6.tgz#2fbfda558a98a691a798f123afd95915badce876";
@@ -2639,11 +2607,6 @@ globby@^14.1.0:
     slash "^5.1.0"
     unicorn-magic "^0.3.0"
 
[email protected]:
-  version "3.21.4"
-  resolved 
"https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.21.4.tgz#2f933e8b6e5e9f8edde66b7be0024b68f77da6c9";
-  integrity 
sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==
-
 gopd@^1.0.1, gopd@^1.2.0:
   version "1.2.0"
   resolved 
"https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1";
@@ -2780,11 +2743,6 @@ human-signals@^5.0.0:
   resolved 
"https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28";
   integrity 
sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==
 
-human-signals@^8.0.1:
-  version "8.0.1"
-  resolved 
"https://registry.yarnpkg.com/human-signals/-/human-signals-8.0.1.tgz#f08bb593b6d1db353933d06156cedec90abe51fb";
-  integrity 
sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==
-
 [email protected], iconv-lite@^0.6.3:
   version "0.6.3"
   resolved 
"https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501";
@@ -2889,11 +2847,6 @@ is-plain-obj@^2.1.0:
   resolved 
"https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287";
   integrity 
sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
 
-is-plain-obj@^4.1.0:
-  version "4.1.0"
-  resolved 
"https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0";
-  integrity 
sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==
-
 is-plain-object@^2.0.4:
   version "2.0.4"
   resolved 
"https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677";
@@ -2913,11 +2866,6 @@ is-stream@^3.0.0:
   resolved 
"https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac";
   integrity 
sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==
 
-is-stream@^4.0.1:
-  version "4.0.1"
-  resolved 
"https://registry.yarnpkg.com/is-stream/-/is-stream-4.0.1.tgz#375cf891e16d2e4baec250b85926cffc14720d9b";
-  integrity 
sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==
-
 is-typed-array@^1.1.14:
   version "1.1.15"
   resolved 
"https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b";
@@ -3538,14 +3486,6 @@ npm-run-path@^5.1.0:
   dependencies:
     path-key "^4.0.0"
 
-npm-run-path@^6.0.0:
-  version "6.0.0"
-  resolved 
"https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-6.0.0.tgz#25cfdc4eae04976f3349c0b1afc089052c362537";
-  integrity 
sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==
-  dependencies:
-    path-key "^4.0.0"
-    unicorn-magic "^0.3.0"
-
 nth-check@^2.0.1:
   version "2.1.1"
   resolved 
"https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d";
@@ -3659,11 +3599,6 @@ parse-json@^8.0.0:
     index-to-position "^1.1.0"
     type-fest "^4.39.1"
 
-parse-ms@^4.0.0:
-  version "4.0.0"
-  resolved 
"https://registry.yarnpkg.com/parse-ms/-/parse-ms-4.0.0.tgz#c0c058edd47c2a590151a718990533fd62803df4";
-  integrity 
sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==
-
 parse-semver@^1.1.1:
   version "1.1.1"
   resolved 
"https://registry.yarnpkg.com/parse-semver/-/parse-semver-1.1.1.tgz#9a4afd6df063dc4826f93fba4a99cf223f666cb8";
@@ -3759,17 +3694,10 @@ picomatch@^4.0.2, picomatch@^4.0.3:
   resolved 
"https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042";
   integrity 
sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==
 
[email protected]:
-  version "2.0.0"
-  resolved 
"https://registry.yarnpkg.com/pid-port/-/pid-port-2.0.0.tgz#1f792be70c9d9a6dcb3f457cac02f4b65c731bb7";
-  integrity 
sha512-EDmfRxLl6lkhPjDI+19l5pkII89xVsiCP3aGjS808f7M16DyCKSXEWthD/hjyDLn5I4gKqTVw7hSgdvdXRJDTw==
-  dependencies:
-    execa "^9.6.0"
-
-pino-abstract-transport@^2.0.0:
-  version "2.0.0"
-  resolved 
"https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz#de241578406ac7b8a33ce0d77ae6e8a0b3b68a60";
-  integrity 
sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==
+pino-abstract-transport@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz#b21e5f33a297e8c4c915c62b3ce5dd4a87a52c23";
+  integrity 
sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==
   dependencies:
     split2 "^4.0.0"
 
@@ -3778,22 +3706,22 @@ pino-std-serializers@^7.0.0:
   resolved 
"https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz#7c625038b13718dbbd84ab446bd673dc52259e3b";
   integrity 
sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==
 
[email protected]:
-  version "10.0.0"
-  resolved 
"https://registry.yarnpkg.com/pino/-/pino-10.0.0.tgz#3d1a8abc7a700142edebf02a7b291834da199fbe";
-  integrity 
sha512-eI9pKwWEix40kfvSzqEP6ldqOoBIN7dwD/o91TY5z8vQI12sAffpR/pOqAD1IVVwIVHDpHjkq0joBPdJD0rafA==
[email protected]:
+  version "10.3.1"
+  resolved 
"https://registry.yarnpkg.com/pino/-/pino-10.3.1.tgz#6552c8f8d8481844c9e452e7bf0be90bff1939ce";
+  integrity 
sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==
   dependencies:
+    "@pinojs/redact" "^0.4.0"
     atomic-sleep "^1.0.0"
     on-exit-leak-free "^2.1.0"
-    pino-abstract-transport "^2.0.0"
+    pino-abstract-transport "^3.0.0"
     pino-std-serializers "^7.0.0"
     process-warning "^5.0.0"
     quick-format-unescaped "^4.0.3"
     real-require "^0.2.0"
     safe-stable-stringify "^2.3.1"
-    slow-redact "^0.3.0"
     sonic-boom "^4.0.1"
-    thread-stream "^3.0.0"
+    thread-stream "^4.0.0"
 
 pluralize@^2.0.0:
   version "2.0.0"
@@ -3888,13 +3816,6 @@ [email protected]:
   resolved 
"https://registry.yarnpkg.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393";
   integrity 
sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==
 
-pretty-ms@^9.2.0:
-  version "9.3.0"
-  resolved 
"https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-9.3.0.tgz#dd2524fcb3c326b4931b2272dfd1e1a8ed9a9f5a";
-  integrity 
sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==
-  dependencies:
-    parse-ms "^4.0.0"
-
 process-nextick-args@~2.0.0:
   version "2.0.1"
   resolved 
"https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2";
@@ -4376,11 +4297,6 @@ slice-ansi@^4.0.0:
     astral-regex "^2.0.0"
     is-fullwidth-code-point "^3.0.0"
 
-slow-redact@^0.3.0:
-  version "0.3.2"
-  resolved 
"https://registry.yarnpkg.com/slow-redact/-/slow-redact-0.3.2.tgz#d06e25195aa5c492d32631c53d9ae86043b8b0e2";
-  integrity 
sha512-MseHyi2+E/hBRqdOi5COy6wZ7j7DxXRz9NkseavNYSvvWC06D8a5cidVZX3tcG5eCW3NIyVU4zT63hw0Q486jw==
-
 sonic-boom@^4.0.1:
   version "4.2.0"
   resolved 
"https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-4.2.0.tgz#e59a525f831210fa4ef1896428338641ac1c124d";
@@ -4515,11 +4431,6 @@ strip-final-newline@^3.0.0:
   resolved 
"https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd";
   integrity 
sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==
 
-strip-final-newline@^4.0.0:
-  version "4.0.0"
-  resolved 
"https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-4.0.0.tgz#35a369ec2ac43df356e3edd5dcebb6429aa1fa5c";
-  integrity 
sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==
-
 strip-json-comments@^3.1.1:
   version "3.1.1"
   resolved 
"https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006";
@@ -4725,10 +4636,10 @@ textextensions@^6.11.0:
   dependencies:
     editions "^6.21.0"
 
-thread-stream@^3.0.0:
-  version "3.1.0"
-  resolved 
"https://registry.yarnpkg.com/thread-stream/-/thread-stream-3.1.0.tgz#4b2ef252a7c215064507d4ef70c05a5e2d34c4f1";
-  integrity 
sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==
+thread-stream@^4.0.0:
+  version "4.0.0"
+  resolved 
"https://registry.yarnpkg.com/thread-stream/-/thread-stream-4.0.0.tgz#732f007c24da7084f729d6e3a7e3f5934a7380b7";
+  integrity 
sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==
   dependencies:
     real-require "^0.2.0"
 
@@ -5244,11 +5155,6 @@ yocto-queue@^0.1.0:
   resolved 
"https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b";
   integrity 
sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
 
-yoctocolors@^2.1.1:
-  version "2.1.2"
-  resolved 
"https://registry.yarnpkg.com/yoctocolors/-/yoctocolors-2.1.2.tgz#d795f54d173494e7d8db93150cec0ed7f678c83a";
-  integrity 
sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==
-
 zimmerframe@^1.1.2:
   version "1.1.2"
   resolved 
"https://registry.yarnpkg.com/zimmerframe/-/zimmerframe-1.1.2.tgz#5b75f1fa83b07ae2a428d51e50f58e2ae6855e5e";

Reply via email to