diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
deleted file mode 100644
index 15771545..00000000
--- a/.devcontainer/Dockerfile
+++ /dev/null
@@ -1,36 +0,0 @@
-# FROM mcr.microsoft.com/devcontainers/java:1-21-bullseye
-FROM ubuntu:22.04
-
-# Add any tools that are needed beyond Java
-RUN apt-get update && \
- apt-get install -y sudo sed vim git make gcc zlib1g-dev zip unzip tree curl wget jq && \
- apt-get autoremove -y && \
- apt-get clean -y
-
-# Create a user for development
-ARG USERNAME=vscode
-ARG USER_UID=1000
-ARG USER_GID=$USER_UID
-
-# Create the user with passwordless sudo privileges
-RUN groupadd --gid $USER_GID $USERNAME \
- && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME -s /bin/bash \
- && usermod -aG sudo $USERNAME \
- && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
- && chmod 0440 /etc/sudoers.d/$USERNAME \
- && chown -R $USERNAME:$USERNAME /home/$USERNAME
-
-WORKDIR /codenet-minerva-code-analyzer
-
-USER $USERNAME
-
-# Install Java and Gradle via SDKMan
-RUN curl -s "https://get.sdkman.io" | bash
-
-# This SHELL command is needed to run using `source`
-SHELL ["/bin/bash", "-c"]
-RUN source "$HOME/.sdkman/bin/sdkman-init.sh" && \
- sdk install java 17.0.12-sem && \
- sdk use java 17.0.12-sem && \
- sdk install gradle 8.9
-
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
deleted file mode 100644
index 8845edf4..00000000
--- a/.devcontainer/devcontainer.json
+++ /dev/null
@@ -1,44 +0,0 @@
-// cspell: disable
-{
- "name": "CodeAnalyzer",
- "dockerFile": "Dockerfile",
- "context": "..",
- "remoteUser": "vscode",
- "workspaceFolder": "/codenet-minerva-code-analyzer",
- "workspaceMount": "source=${localWorkspaceFolder},target=/codenet-minerva-code-analyzer,type=bind,consistency=delegated",
- "runArgs": ["-h", "codenet"],
- "customizations": {
- "vscode": {
- "settings": {
- "markdown-preview-github-styles.colorTheme": "light",
- "makefile.extensionOutputFolder": "/tmp",
- "cSpell.words": [
- "northstar",
- "cyclomatic",
- "jgrapht",
- "stdlibs"
- ],
- "files.exclude": {
- "**/.git": true,
- "**/.DS_Store": true
- }
- },
- "extensions": [
- "vscjava.vscode-java-pack",
- "vscjava.vscode-java-test",
- "vscjava.vscode-java-debug",
- "vscjava.vscode-gradle",
- "donjayamanne.githistory",
- "bierner.github-markdown-preview",
- "yzhang.markdown-all-in-one",
- "hnw.vscode-auto-open-markdown-preview",
- "davidanson.vscode-markdownlint",
- "bierner.markdown-preview-github-styles",
- "streetsidesoftware.code-spell-checker",
- "ms-azuretools.vscode-docker"
- ]
- }
- },
- // Install the version of gradle the project uses
- "postCreateCommand": "./gradlew"
-}
\ No newline at end of file
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 00000000..3eb44261
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,46 @@
+name: Deploy docs to GitHub Pages
+
+on:
+ push:
+ branches: [docs]
+ workflow_dispatch:
+
+permissions:
+ contents: write
+
+concurrency:
+ group: pages-deploy
+ cancel-in-progress: true
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout docs
+ uses: actions/checkout@v6
+
+ - name: Set up Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: npm
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build site
+ run: npm run build
+
+ - name: Publish dist to gh-pages
+ run: |
+ set -euo pipefail
+ cd dist
+ touch .nojekyll
+ git init
+ git config user.name "github-actions[bot]"
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
+ git add -A
+ git commit -m "Deploy ${GITHUB_SHA}" || true
+ git branch -M gh-pages
+ git remote add origin "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git"
+ git push -f origin gh-pages
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
deleted file mode 100644
index d31e75cd..00000000
--- a/.github/workflows/release.yml
+++ /dev/null
@@ -1,58 +0,0 @@
-name: Java Release
-
-on:
- push:
- tags:
- - "v*.*.*"
-
-permissions:
- contents: write
-
-jobs:
- release:
- runs-on: ubuntu-latest
-
- env:
- JAVA_HOME: ${{ github.workspace }}/graalvm-ce-java11-22.3.3
-
- steps:
- - name: Check out code
- uses: actions/checkout@v4
-
- - name: Set up JDK 11 from GraalVM
- run: |
- echo "${{ env.JAVA_HOME }}/bin" >> $GITHUB_PATH
- wget https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-22.3.3/graalvm-ce-java11-linux-amd64-22.3.3.tar.gz
- tar -xvzf graalvm-ce-java11-linux-amd64-22.3.3.tar.gz
- ${{ env.JAVA_HOME }}/bin/gu install native-image
-
- - name: Make gradlew executable
- run: chmod +x ./gradlew
-
- - name: Build and Test
- id: build
- continue-on-error: true # Allow the workflow to continue if this fails
- run: ./gradlew clean fatJar
-
- - name: Delete tag on failure
- if: steps.build.outcome != 'success'
- run: |
- git push --delete origin ${GITHUB_REF#refs/tags/}
- exit 1 # Fail the workflow
-
- - name: Build Changelog
- id: gen_changelog
- uses: mikepenz/release-changelog-builder-action@v5
- with:
- failOnError: "true"
- configuration: .github/workflows/release_config.json
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Publish Release
- uses: softprops/action-gh-release@v1
- with:
- files: build/libs/*.jar
- body: ${{ steps.gen_changelog.outputs.changelog }}
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/release_config.json b/.github/workflows/release_config.json
deleted file mode 100644
index f0d4b5b2..00000000
--- a/.github/workflows/release_config.json
+++ /dev/null
@@ -1,39 +0,0 @@
-{
- "categories": [
- {
- "title": "## 🚀 Features",
- "labels": ["kind/feature", "enhancement"]
- },
- {
- "title": "## 🐛 Fixes",
- "labels": ["fix", "bug"]
- },
- {
- "title": "## ♻️ Refactoring",
- "labels": ["refactoring"]
- },
- {
- "title": "## ⚡️ Performance Improvements",
- "labels": ["performance"]
- },
- {
- "title": "## \uD83D\uDCDA Documentation",
- "labels": ["documentation", "doc"]
- },
- {
- "title": "## \uD83D\uDEA6 Tests",
- "labels": ["test"]
- },
- {
- "title": "## \uD83D\uDEE0 Other Updates",
- "labels": ["other", "kind/dependency-change"]
- },
- {
- "title": "## 🚨 Breaking Changes",
- "labels": ["breaking"]
- }
- ],
- "ignore_labels": [
- "ignore"
- ]
-}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index c4e7b66e..a9602669 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,198 +1,28 @@
-# User-specific stuff
-.idea
-.idea/**/workspace.xml
-.idea/**/tasks.xml
-.idea/**/usage.statistics.xml
-.idea/**/dictionaries
-.idea/**/shelf
-
-# AWS User-specific
-.idea/**/aws.xml
-
-# Generated files
-.idea/**/contentModel.xml
-
-# Sensitive or high-churn files
-.idea/**/dataSources/
-.idea/**/dataSources.ids
-.idea/**/dataSources.local.xml
-.idea/**/sqlDataSources.xml
-.idea/**/dynamic.xml
-.idea/**/uiDesigner.xml
-.idea/**/dbnavigator.xml
-
-# Gradle
-.idea/**/gradle.xml
-.idea/**/libraries
-
-# Gradle and Maven with auto-import
-# When using Gradle or Maven with auto-import, you should exclude module files,
-# since they will be recreated, and may cause churn. Uncomment if using
-# auto-import.
-# .idea/artifacts
-# .idea/compiler.xml
-# .idea/jarRepositories.xml
-# .idea/modules.xml
-# .idea/*.iml
-# .idea/modules
-# *.iml
-# *.ipr
-
-# CMake
-cmake-build-*/
-
-# Mongo Explorer plugin
-.idea/**/mongoSettings.xml
-
-# File-based project format
-*.iws
-
-# IntelliJ
-out/
-
-# mpeltonen/sbt-idea plugin
-.idea_modules/
-
-# JIRA plugin
-atlassian-ide-plugin.xml
-
-# Cursive Clojure plugin
-.idea/replstate.xml
-
-# SonarLint plugin
-.idea/sonarlint/
-
-# Crashlytics plugin (for Android Studio and IntelliJ)
-com_crashlytics_export_strings.xml
-crashlytics.properties
-crashlytics-build.properties
-fabric.properties
-
-# Editor-based Rest Client
-.idea/httpRequests
-
-# Android studio 3.1+ serialized cache file
-.idea/caches/build_file_checksums.ser
-
-### Intellij Patch ###
-# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
-
-# *.iml
-# modules.xml
-# .idea/misc.xml
-# *.ipr
-
-# Sonarlint plugin
-# https://plugins.jetbrains.com/plugin/7973-sonarlint
-.idea/**/sonarlint/
-
-# SonarQube Plugin
-# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
-.idea/**/sonarIssues.xml
-
-# Markdown Navigator plugin
-# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
-.idea/**/markdown-navigator.xml
-.idea/**/markdown-navigator-enh.xml
-.idea/**/markdown-navigator/
-
-# Cache file creation bug
-# See https://youtrack.jetbrains.com/issue/JBR-2257
-.idea/$CACHE_FILE$
-
-# CodeStream plugin
-# https://plugins.jetbrains.com/plugin/12206-codestream
-.idea/codestream.xml
-
-# Azure Toolkit for IntelliJ plugin
-# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
-.idea/**/azureSettings.xml
-
-### Java ###
-# Compiled class file
-*.class
-
-# Log file
-*.log
-
-# BlueJ files
-*.ctxt
-
-# Mobile Tools for Java (J2ME)
-.mtj.tmp/
-
-# Package Files #
-*.jar
-*.war
-*.nar
-*.ear
-*.zip
-*.tar.gz
-*.rar
-
-# Don't ignore the demo binaries
-!etc/demo/jar/*.jar
-!etc/demo/ear/*.ear
-!etc/demo/war/*.war
-
-# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
-hs_err_pid*
-replay_pid*
-
-### VisualStudioCode ###
-.vscode/*
-
-!src/test/resources/sample_apps/daytrader8/binaries/daytrader8.jar
-src/test/resources/sample_apps/daytrader8/output
-
-# Local History for Visual Studio Code
-.history/
-
-# Built Visual Studio Code Extensions
-*.vsix
-
-### VisualStudioCode Patch ###
-# Ignore all local history of files
-.history
-.ionide
-
-# Support for Project snippet scope
-.vscode/*.code-snippets
-
-# Ignore code-workspaces
-*.code-workspace
-
-### Gradle ###
-.gradle
-**/build/
-!src/**/build/
-
-# Ignore Gradle GUI config
-gradle-app.setting
+# macOS
+.DS_Store
-# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
-!gradle-wrapper.jar
+# environment files
+.env
+.env.production
-# Avoid ignore Gradle wrappper properties
-!gradle-wrapper.properties
+# Astro build output & generated types
+dist/
+.astro/
-# Cache of project
-.gradletasknamecache
+# dependencies
+node_modules/
-# Eclipse Gradle plugin generated files
-# Eclipse Core
-.project
-# JDT-specific (Eclipse Java Development Tools)
-.classpath
+# logs
+npm-debug.log*
+yarn-debug.log*
+pnpm-debug.log*
-### Gradle Patch ###
-# Java heap dump
-*.hprof
+# Python (API doc generation)
+__pycache__/
+*.py[cod]
+.python-version
+.venv/
+.venv-docs/
-# End of https://www.toptal.com/developers/gitignore/api/intellij,gradle,java,visualstudiocode
-.idea
-.DS_Store
-.vscode
-bin/
-etc/
-/src/test/resources/sample_apps/daytrader8/output/
+# cache
+.cache/
diff --git a/README.md b/README.md
index bf70d632..b198755c 100644
--- a/README.md
+++ b/README.md
@@ -1,191 +1,54 @@
-
+# codeanalyzer-java documentation
-Native WALA implementation of source code analysis tool for Enterprise Java Applications.
+This branch (`docs`) contains the documentation site for
+[**codeanalyzer-java**](https://github.com/codellm-devkit/codeanalyzer-java) — the
+WALA + Javaparser static-analysis backend behind CodeLLM-DevKit's Java support.
-## 1. Prerequisites
+The site is built with [Astro](https://astro.build/) and
+[Starlight](https://starlight.astro.build/), and is deployed to GitHub Pages.
-Before you begin, ensure you have met the following requirements:
+> **Looking for the analyzer source code?** It lives on the
+> [`main`](https://github.com/codellm-devkit/codeanalyzer-java/tree/main) branch.
-* You have a Linux/MacOS/WSL machine.
-* You have installed the latest version of [SDKMan!](sdkman.io/)
-
-### 1.1. Install SDKMan!
-1. Install SDKMan!
- Open your terminal and enter the following command:
-
- ```bash
- curl -s "https://get.sdkman.io" | bash
- ```
-
- Follow the on-screen instructions to complete the installation.
-
-2. Open a new terminal or source the SDKMan! scripts:
-
- ```bash
- source "$HOME/.sdkman/bin/sdkman-init.sh"
- ```
-
-## 2. Building `codeanalyzer`
-
-### 2.1. Install Java 11 or above
-
-1. You can list all available GraalVM versions with:
-
- ```bash
- sdk list java | grep sem
- ```
- You should see the following:
- ```
- Semeru | | 21.0.2 | sem | | 21.0.2-sem
- | | 21.0.1 | sem | | 21.0.1-sem
- | | 17.0.10 | sem | | 17.0.10-sem
- | | 17.0.9 | sem | | 17.0.9-sem
- | | 11.0.22 | sem | installed | 11.0.22-sem
- | | 11.0.21 | sem | | 11.0.21-sem
- ```
-
-2. Install Java 11 or above (we'll go with 17.0.10-sem):
-
- ```bash
- sdk install java 17.0.10-sem
- ```
-
-3. Set Java 17 as the current Java version:
-
- ```bash
- sdk use java 17.0.10-sem
- ```
-
-### 2.2. Build `codeanalyzer`
-
-Clone the repository (if you haven't already) and navigate into the cloned directory.
-
-Run the Gradle wrapper script to build the project. This will compile the project using GraalVM native image.
+## Local development
```bash
-./gradlew fatJar
+npm install
+npm run dev # start the dev server at http://localhost:4321
```
-### 2.3. Using `codeanalyzer`
-
-The jar will be built at `build/libs/codeanalyzer-1.0.jar`. It may be used as follows:
+| Command | Action |
+|---------|--------|
+| `npm install` | Install dependencies |
+| `npm run dev` | Start the local dev server |
+| `npm run build` | Build the production site to `./dist/` |
+| `npm run preview` | Preview the built site locally |
-```help
-Usage: java -jar /path/to/codeanalyzer.jar [-hvV] [--no-build] [-a=] [-b=]
- [-i= ] [-o=] [-s=]
-Convert java binary into a comprehensive system dependency graph.
- -i, --input= Path to the project root directory.
- -s, --source-analysis=
- Analyze a single string of java source code instead
- the project.
- -o, --output= Destination directory to save the output graphs. By
- default, the SDG formatted as a JSON will be
- printed to the console.
- -b, --build-cmd= Custom build command. Defaults to auto build.
- --no-build Do not build your application. Use this option if
- you have already built your application.
- -a, --analysis-level=
- Level of analysis to perform. Options: 1 (for just
- symbol table) or 2 (for call graph). Default: 1
- -v, --verbose Print logs to console.
- -h, --help Show this help message and exit.
- -V, --version Print version information and exit.
- -t, --target-files For each file user wants to perform source analysis on top of existing analysis.json
+## Structure
```
-
-
-## 3. Installing `codeanalyzer` as a native binary (once built, no JVM will be required for running `codeanalyzer`)
-
-To install `codeanalyzer`, follow these steps:
-
-### 3.1. Install GraalVM using SDKMan
-
-1. You can list all available GraalVM versions with:
-
- ```bash
- sdk list java | grep graal
- ```
-
-2. Install GraalVM 17 or above (we'll go with 21.0.2-graalce):
-
- ```bash
- sdk install java 21.0.2-graalce
- ```
-
-3. Set GraalVM 21 as the current Java version:
-
- ```bash
- sdk use java 21.0.2-graalce
- ```
-
-### 3.2. Build the Project
-
-Clone the repository (if you haven't already) and navigate into the cloned directory.
-
-Run the Gradle wrapper script to build the project. This will compile the project using GraalVM native image.
-
-```bash
-./gradlew nativeCompile -PbinDir=$HOME/.local/bin
+src/
+├── assets/ logos
+├── styles/docs.css theme overrides
+├── content.config.ts Starlight content collection
+└── content/docs/ the documentation pages (MDX)
+ ├── index.mdx landing page
+ ├── what-is-codeanalyzer.mdx
+ ├── quickstart.mdx
+ ├── installing.mdx
+ ├── guides/ architecture, analysis levels, build, incremental
+ ├── reference/ CLI options + examples
+ ├── schema/ output JSON schema
+ ├── frameworks/ entry points + CRUD detection
+ └── integration/ Python SDK (CLDK)
+astro.config.mjs site + sidebar configuration
```
-**Note: `-PbinDir` is optional. If not provided, this command places the binaries in `build/bin`.**
-
-### 3.3. Using `codeanalyzer`
-
-Assuming the path you provided in `-PbinDir` (in my case `$HOME/.local/bin`) is in your `$PATH`, after installation, you can use `codeanalyzer` by following the below format:
-
- ```help
- Usage: codeanalyzer [-hqV] [-d=] [-e=] -i=
- -o=
- Convert java binary (*.jar, *.ear, *.war) to a neo4j graph.
- -d, --app-deps= Path to the application dependencies.
- -e, --extra-libs=
- Path to the extra libraries.
- -h, --help Show this help message and exit.
- -i, --input= Path to the input jar(s).
- -o, --output= Destination directory to save the output graphs.
- -q, --quiet Don't print logs to console.
- -V, --version Print version information and exit.
- ```
-
-There is a sample application in `src/test/resources/sample_apps/daytrader8/binaries/`. You can use this to test the tool.
+## Deployment
- ```sh
- codeanalyzer -i src/test/resources/sample_apps/daytrader8/binaries/
- ```
+`.github/workflows/deploy.yml` builds the site and publishes it to GitHub Pages
+on every push to the `docs` branch.
-This will produce print the SDG on the console. Explore other flags to save the output to a JSON.
+## License
-## FAQ
-
-1. After making a few code changes, my native binary gives random exceptions. But, my code works perfectly with `java -jar`.
-
- The `reflect-config.json` is most likely out of date. Plese follow the below instructions:
-
- a. Build the fatjar using `./gradlew fatJar`
-
- b. Run the following
-
- ```sh
- java -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image-config -jar build/libs/codeanalyzer-1.0.jar -i src/test/resources/sample.applications/daytrader8/source -a 2 -v
- ```
-
- c. Then build using the instructions in [§3.3](./README.md#33-build-the-project).
-
- The problem should be resolved.
-
-## LICENSE
-
-```LICENSE
-Copyright IBM Corporation 2023, 2024
-
-Licensed under the Apache Public License 2.0, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-
-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.
-```
+Apache 2.0 — see [LICENSE](./LICENSE).
diff --git a/astro.config.mjs b/astro.config.mjs
new file mode 100644
index 00000000..bf46ade6
--- /dev/null
+++ b/astro.config.mjs
@@ -0,0 +1,142 @@
+import { defineConfig } from "astro/config";
+import starlight from "@astrojs/starlight";
+import mermaid from "astro-mermaid";
+import { pluginCollapsibleSections } from "@expressive-code/plugin-collapsible-sections";
+import { pluginLineNumbers } from "@expressive-code/plugin-line-numbers";
+
+// https://astro.build/config
+export default defineConfig({
+ site: "https://codellm-devkit.github.io",
+ base: "/codeanalyzer-java",
+ integrations: [
+ // Mermaid must run BEFORE Starlight so it can preprocess ```mermaid blocks.
+ mermaid({
+ theme: "neutral",
+ autoTheme: true,
+ mermaidConfig: {
+ flowchart: { curve: "basis" },
+ },
+ }),
+ starlight({
+ title: "codeanalyzer-java",
+ tagline: "WALA + Javaparser static analysis for enterprise Java — one JSON artifact or a queryable Neo4j graph.",
+ description:
+ "codeanalyzer-java is the JVM static-analysis backend behind CodeLLM-DevKit's Java support: a standalone JAR that turns a Java project into a symbol table and call graph, emitted as one versioned analysis JSON artifact or projected into a queryable Neo4j property graph.",
+ logo: {
+ src: "./src/assets/logo.png",
+ replacesTitle: true,
+ },
+ favicon: "/favicon.png",
+ customCss: ["./src/styles/docs.css"],
+ expressiveCode: {
+ plugins: [pluginCollapsibleSections(), pluginLineNumbers()],
+ styleOverrides: {
+ borderRadius: "0.4rem",
+ frames: {
+ shadowColor: "transparent",
+ },
+ },
+ defaultProps: {
+ showLineNumbers: false,
+ },
+ },
+ head: [
+ {
+ tag: "link",
+ attrs: { rel: "preconnect", href: "https://fonts.googleapis.com" },
+ },
+ {
+ tag: "link",
+ attrs: {
+ rel: "preconnect",
+ href: "https://fonts.gstatic.com",
+ crossorigin: "",
+ },
+ },
+ {
+ tag: "link",
+ attrs: {
+ rel: "stylesheet",
+ href: "https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap",
+ },
+ },
+ ],
+ social: [
+ {
+ icon: "github",
+ label: "codeanalyzer-java on GitHub",
+ href: "https://github.com/codellm-devkit/codeanalyzer-java",
+ },
+ {
+ icon: "seti:java",
+ label: "codeanalyzer-java releases",
+ href: "https://github.com/codellm-devkit/codeanalyzer-java/releases",
+ },
+ {
+ icon: "discord",
+ label: "CLDK on Discord",
+ href: "https://discord.gg/zEjz9YrmqN",
+ },
+ ],
+ editLink: {
+ baseUrl: "https://github.com/codellm-devkit/codeanalyzer-java/edit/docs/",
+ },
+ sidebar: [
+ {
+ label: "Start here",
+ items: [
+ { label: "What is codeanalyzer-java?", slug: "what-is-codeanalyzer" },
+ { label: "Quickstart", slug: "quickstart" },
+ { label: "Installation", slug: "installing" },
+ ],
+ },
+ {
+ label: "Guides",
+ items: [
+ { label: "Architecture", slug: "guides/architecture" },
+ { label: "Analysis levels", slug: "guides/analysis-levels" },
+ { label: "Build integration", slug: "guides/build-integration" },
+ { label: "Incremental analysis", slug: "guides/incremental-analysis" },
+ { label: "Neo4j output", slug: "guides/neo4j-output" },
+ ],
+ },
+ {
+ label: "CLI Reference",
+ items: [
+ { label: "Command-line options", slug: "reference/cli" },
+ { label: "Examples", slug: "reference/examples" },
+ ],
+ },
+ {
+ label: "Output Schema",
+ items: [
+ { label: "Overview", slug: "schema" },
+ { label: "Symbol table", slug: "schema/symbol-table" },
+ { label: "Call graph", slug: "schema/call-graph" },
+ { label: "Neo4j graph", slug: "schema/neo4j-graph" },
+ ],
+ },
+ {
+ label: "Framework Support",
+ items: [
+ { label: "Entry points", slug: "frameworks/entry-points" },
+ { label: "CRUD detection", slug: "frameworks/crud" },
+ ],
+ },
+ {
+ label: "Integration",
+ items: [
+ { label: "Python SDK (CLDK)", slug: "integration/python-sdk" },
+ ],
+ },
+ {
+ label: "Project",
+ items: [
+ { label: "Contributing", slug: "contributing" },
+ { label: "Troubleshooting", slug: "troubleshooting" },
+ ],
+ },
+ ],
+ }),
+ ],
+});
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index b2ffabf8..00000000
--- a/build.gradle
+++ /dev/null
@@ -1,298 +0,0 @@
- /*
-Copyright IBM Corporation 2023, 2024
-
-Licensed under the Apache Public License 2.0, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-
-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.
-*/
-
-plugins {
- // Apply the application plugin to add support for building a CLI application in Java.
- id 'eclipse'
- id 'application'
- id 'org.graalvm.buildtools.native' version '0.10.4'
- id 'org.jetbrains.kotlin.jvm'
- id 'com.diffplug.spotless' version '6.25.0'
-}
-
-// Get the version from the property file first
-version = new Properties().with {property ->
- file("gradle.properties").withInputStream {property.load(it)}
- property.getProperty("version")
-}
-
-repositories {
- mavenCentral()
- mavenLocal()
-}
-
-java {
-}
-
-if (project.hasProperty('mainClass')) {
- mainClassName = project.getProperty('mainClass')
-} else {
- // use a default
- mainClassName =("com.ibm.cldk.CodeAnalyzer")
-}
-
-sourceSets {
- main {
- java {
- srcDirs = ["src/main/java"]
- }
- resources {
- srcDirs = ["src/main/resources"]
- }
- }
- test {
- java {
- srcDirs = ['src/test/java']
- }
- resources {
- srcDirs= ["src/test/resources"]
- }
- }
-}
-
-// Remove that nagging bin folder vscode seems to generate every single time
-clean.doFirst {
- delete "${rootDir}/bin"
-}
-
-dependencies {
- // PICOCLI for handling commandline interface
- implementation 'info.picocli:picocli:4.1.0'
- testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'
- testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'
- testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'
- testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'
- annotationProcessor 'info.picocli:picocli-codegen:4.1.0'
-
- implementation 'org.apache.commons:commons-lang3:3.14.0'
-
- implementation group: 'commons-cli', name: 'commons-cli', version: '1.4'
-
- implementation 'commons-io:commons-io:2.8.0'
-
- implementation 'org.apache.logging.log4j:log4j-api:2.18.0'
- implementation 'org.apache.logging.log4j:log4j-core:2.18.0'
- def walaVersion = '1.6.7'
-
- compileOnly 'org.projectlombok:lombok:1.18.30'
- annotationProcessor 'org.projectlombok:lombok:1.18.30'
-
- implementation "com.ibm.wala:com.ibm.wala.shrike:${walaVersion}"
- implementation "com.ibm.wala:com.ibm.wala.util:${walaVersion}"
- implementation "com.ibm.wala:com.ibm.wala.core:${walaVersion}"
- implementation "com.ibm.wala:com.ibm.wala.cast:${walaVersion}"
- implementation "com.ibm.wala:com.ibm.wala.cast.java:${walaVersion}"
- implementation "com.ibm.wala:com.ibm.wala.cast.java.ecj:${walaVersion}"
-
- compileOnly 'org.projectlombok:lombok:1.18.30'
- annotationProcessor 'org.projectlombok:lombok:1.18.30'
-
- implementation 'com.google.guava:guava:33.0.0-jre'
-
- implementation("commons-io:commons-io:2.15.1")
- implementation("org.ow2.asm:asm:9.6")
- implementation("org.eclipse.jdt:org.eclipse.jdt.core:3.21.0")
- implementation("org.eclipse.platform:org.eclipse.core.commands:3.9.700")
- implementation("org.eclipse.platform:org.eclipse.core.contenttype:3.7.1000")
- implementation("org.eclipse.platform:org.eclipse.core.expressions:3.7.100")
- implementation("org.eclipse.platform:org.eclipse.core.filesystem:1.9.0")
- implementation("org.eclipse.platform:org.eclipse.core.jobs:3.11.0")
- implementation('org.eclipse.platform:org.eclipse.core.resources:3.20.0')
- implementation("org.eclipse.platform:org.eclipse.core.runtime:3.17.100")
- implementation("org.eclipse.platform:org.eclipse.equinox.app:1.5.100")
- implementation("org.eclipse.platform:org.eclipse.equinox.common:3.14.100")
- implementation("org.eclipse.platform:org.eclipse.equinox.preferences:3.8.200")
- implementation("org.eclipse.platform:org.eclipse.equinox.registry:3.10.200")
- implementation("org.eclipse.platform:org.eclipse.osgi:3.16.300")
- implementation("org.eclipse.platform:org.eclipse.text:3.11.0")
-
- implementation('org.json:json:20231013')
- implementation('com.google.code.gson:gson:2.10.1')
- implementation('org.jgrapht:jgrapht-core:1.5.2')
- implementation('org.jgrapht:jgrapht-io:1.5.2')
- implementation('org.jgrapht:jgrapht-ext:1.5.2')
- implementation('com.github.javaparser:javaparser-symbol-solver-core:3.26.3')
- implementation('com.github.javaparser:javaparser-core:3.26.3')
-
- // TestContainers
- testImplementation 'org.testcontainers:testcontainers:1.20.6'
- testImplementation 'org.testcontainers:junit-jupiter:1.20.6'
-
- // JUnit 5
- testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.1'
- testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.1' // for @ParameterizedTest
- testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.1'
-
- // SLF4J - for TestContainers logging
- testImplementation 'org.slf4j:slf4j-api:2.0.9'
- testImplementation 'org.slf4j:slf4j-simple:2.0.9'
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
-
-}
-
-test {
- useJUnitPlatform()
- // Optional: Enable TestContainers reuse to speed up tests
- systemProperty 'testcontainers.reuse.enable', 'true'
-}
-
-spotless {
- java {
- target 'src/**/*.java'
- trimTrailingWhitespace()
- endWithNewline()
- importOrder()
- }
-}
-
-compileJava.dependsOn spotlessApply
-
-// Optionally, automatically format before compilation
-// compileJava.dependsOn googleJavaFormat
-
-task fatJar(type: Jar) {
- archiveBaseName = 'codeanalyzer'
- duplicatesStrategy = DuplicatesStrategy.EXCLUDE
- manifest {
- attributes(
- 'Implementation-Title': 'codeanalyzer',
- 'Implementation-Version': project.version,
- 'Main-Class': 'com.ibm.cldk.CodeAnalyzer'
- )
- }
-
- // Collect and include runtime classpath dependencies, excluding signature files
- from {
- configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
- }
- exclude 'META-INF/*.RSA', 'META-INF/*.SF', 'META-INF/*.DSA'
- with jar
-}
-
-run {
- if (project.hasProperty('args')) {
- args = project.args.split(',')
- }
-}
-
-graalvmNative {
- binaries {
- main {
- imageName = "codeanalyzer"
- mainClass = "com.ibm.cldk.CodeAnalyzer"
- buildArgs.add("-Ob")
- buildArgs.add("-march=compatibility")
- buildArgs.add("--no-fallback")
- buildArgs.add("--no-server")
- buildArgs.add("-H:ReflectionConfigurationFiles=$projectDir/src/main/resources/META-INF/native-image-config/reflect-config.json")
- buildArgs.add("-H:ResourceConfigurationFiles=$projectDir/src/main/resources/META-INF/native-image-config/resource-config.json")
- buildArgs.add("-H:JNIConfigurationFiles=$projectDir/src/main/resources/META-INF/native-image-config/jni-config.json")
- buildArgs.add("-H:DynamicProxyConfigurationFiles=$projectDir/src/main/resources/META-INF/native-image-config/proxy-config.json")
- }
- test {
- buildArgs.add("-O0")
- }
- }
- binaries.configureEach {
- buildArgs.add("--verbose")
- }
-}
-
-// Define a property for the output directory
-def binDir = project.hasProperty('binDir') ? project.property('binDir') : "$projectDir/artifacts/bin"
-
-
-// Task to copy the native executable to the specified directory
-task copyNativeExecutable(type: Copy) {
- dependsOn nativeCompile // Ensure this runs after the native image is built
-
- from "${buildDir}/native/nativeCompile/codeanalyzer"
- into binDir
-
- fileMode = 0755
-}
-
-task createRelease {
- doLast {
- def releaseTitle = 'latest'
- def hostName = 'git@github.ibm.com'
- def repo = 'cldk/codeanalyzer'
-
- // Command to create release
- def delete = "gh release delete latest --cleanup-tag --yes -R ${hostName}:${repo}"//
- // Execute command
- def proc = delete.execute()
- proc.in.eachLine { line -> println line } // Print output
- proc.err.eachLine { line -> println "Error: $line" } // Print error
- proc.waitFor() // Wait for process to complete
- def command = "gh release create latest $projectDir/build/libs/codeanalyzer.jar -t ${releaseTitle} -R ${hostName}:${repo}"
- try {
- def release = command.execute()
- release.in.eachLine { line -> println line } // Print output
- release.err.eachLine { line -> println "Error: $line" } // Print error
- release.waitFor() // Wait for process to complete
- } catch (Exception e) {
- throw new GradleException("Error executing gh command: ${e.message}")
- }
- }
-
-}
-
-tasks.register('bumpVersion') {
- description = 'Bumps the version number (patch, minor, or major)'
- group = 'Versioning'
-
- doLast {
- def versionFile = file('gradle.properties')
- def versionFileText = versionFile.text
- def versionPattern = /version\s*=\s*(\d+)\.(\d+)\.(\d+)/
- def matcher = (versionFileText =~ versionPattern)
-
- if (matcher.find()) {
- def major = matcher.group(1) as int
- def minor = matcher.group(2) as int
- def patch = matcher.group(3) as int
-
- def bumpType = project.hasProperty('bumpType') ? project.bumpType : 'patch'
-
- switch (bumpType) {
- case 'major':
- major++
- minor = 0
- patch = 0
- break
- case 'minor':
- minor++
- patch = 0
- break
- case 'patch':
- default:
- patch++
- break
- }
-
- def newVersion = "${major}.${minor}.${patch}"
- def updatedContent = versionFileText.replaceFirst(versionPattern, "version=$newVersion")
- versionFile.text = updatedContent
-
- println "Version bumped to $newVersion"
- } else {
- throw new GradleException("Version not found in gradle.properties")
- }
- }
-}
-
-nativeCompile.finalizedBy copyNativeExecutable
- kotlin {
- jvmToolchain(11)
- }
diff --git a/docs/assets/Artboard 1.png b/docs/assets/Artboard 1.png
deleted file mode 100644
index 11244faa..00000000
Binary files a/docs/assets/Artboard 1.png and /dev/null differ
diff --git a/docs/assets/ns.png b/docs/assets/ns.png
deleted file mode 100644
index b998bf91..00000000
Binary files a/docs/assets/ns.png and /dev/null differ
diff --git a/gradle.properties b/gradle.properties
deleted file mode 100644
index 867fa98a..00000000
--- a/gradle.properties
+++ /dev/null
@@ -1 +0,0 @@
-version=2.3.7
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 2c352119..00000000
Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 09523c0e..00000000
--- a/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
-networkTimeout=10000
-validateDistributionUrl=true
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
deleted file mode 100755
index f5feea6d..00000000
--- a/gradlew
+++ /dev/null
@@ -1,252 +0,0 @@
-#!/bin/sh
-
-#
-# Copyright © 2015-2021 the original authors.
-#
-# Licensed 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
-#
-# https://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.
-#
-# SPDX-License-Identifier: Apache-2.0
-#
-
-##############################################################################
-#
-# Gradle start up script for POSIX generated by Gradle.
-#
-# Important for running:
-#
-# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
-# noncompliant, but you have some other compliant shell such as ksh or
-# bash, then to run this script, type that shell name before the whole
-# command line, like:
-#
-# ksh Gradle
-#
-# Busybox and similar reduced shells will NOT work, because this script
-# requires all of these POSIX shell features:
-# * functions;
-# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
-# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
-# * compound commands having a testable exit status, especially «case»;
-# * various built-in commands including «command», «set», and «ulimit».
-#
-# Important for patching:
-#
-# (2) This script targets any POSIX shell, so it avoids extensions provided
-# by Bash, Ksh, etc; in particular arrays are avoided.
-#
-# The "traditional" practice of packing multiple parameters into a
-# space-separated string is a well documented source of bugs and security
-# problems, so this is (mostly) avoided, by progressively accumulating
-# options in "$@", and eventually passing that to Java.
-#
-# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
-# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
-# see the in-line comments for details.
-#
-# There are tweaks for specific operating systems such as AIX, CygWin,
-# Darwin, MinGW, and NonStop.
-#
-# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
-# within the Gradle project.
-#
-# You can find Gradle at https://github.com/gradle/gradle/.
-#
-##############################################################################
-
-# Attempt to set APP_HOME
-
-# Resolve links: $0 may be a link
-app_path=$0
-
-# Need this for daisy-chained symlinks.
-while
- APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
- [ -h "$app_path" ]
-do
- ls=$( ls -ld "$app_path" )
- link=${ls#*' -> '}
- case $link in #(
- /*) app_path=$link ;; #(
- *) app_path=$APP_HOME$link ;;
- esac
-done
-
-# This is normally unused
-# shellcheck disable=SC2034
-APP_BASE_NAME=${0##*/}
-# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
-APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
-' "$PWD" ) || exit
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD=maximum
-
-warn () {
- echo "$*"
-} >&2
-
-die () {
- echo
- echo "$*"
- echo
- exit 1
-} >&2
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "$( uname )" in #(
- CYGWIN* ) cygwin=true ;; #(
- Darwin* ) darwin=true ;; #(
- MSYS* | MINGW* ) msys=true ;; #(
- NONSTOP* ) nonstop=true ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD=$JAVA_HOME/jre/sh/java
- else
- JAVACMD=$JAVA_HOME/bin/java
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD=java
- if ! command -v java >/dev/null 2>&1
- then
- die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-fi
-
-# Increase the maximum file descriptors if we can.
-if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
- case $MAX_FD in #(
- max*)
- # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC2039,SC3045
- MAX_FD=$( ulimit -H -n ) ||
- warn "Could not query maximum file descriptor limit"
- esac
- case $MAX_FD in #(
- '' | soft) :;; #(
- *)
- # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC2039,SC3045
- ulimit -n "$MAX_FD" ||
- warn "Could not set maximum file descriptor limit to $MAX_FD"
- esac
-fi
-
-# Collect all arguments for the java command, stacking in reverse order:
-# * args from the command line
-# * the main class name
-# * -classpath
-# * -D...appname settings
-# * --module-path (only if needed)
-# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
-
-# For Cygwin or MSYS, switch paths to Windows format before running java
-if "$cygwin" || "$msys" ; then
- APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
- CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
-
- JAVACMD=$( cygpath --unix "$JAVACMD" )
-
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- for arg do
- if
- case $arg in #(
- -*) false ;; # don't mess with options #(
- /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
- [ -e "$t" ] ;; #(
- *) false ;;
- esac
- then
- arg=$( cygpath --path --ignore --mixed "$arg" )
- fi
- # Roll the args list around exactly as many times as the number of
- # args, so each arg winds up back in the position where it started, but
- # possibly modified.
- #
- # NB: a `for` loop captures its iteration list before it begins, so
- # changing the positional parameters here affects neither the number of
- # iterations, nor the values presented in `arg`.
- shift # remove old arg
- set -- "$@" "$arg" # push replacement arg
- done
-fi
-
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-
-# Collect all arguments for the java command:
-# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
-# and any embedded shellness will be escaped.
-# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
-# treated as '${Hostname}' itself on the command line.
-
-set -- \
- "-Dorg.gradle.appname=$APP_BASE_NAME" \
- -classpath "$CLASSPATH" \
- org.gradle.wrapper.GradleWrapperMain \
- "$@"
-
-# Stop when "xargs" is not available.
-if ! command -v xargs >/dev/null 2>&1
-then
- die "xargs is not available"
-fi
-
-# Use "xargs" to parse quoted args.
-#
-# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
-#
-# In Bash we could simply go:
-#
-# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
-# set -- "${ARGS[@]}" "$@"
-#
-# but POSIX shell has neither arrays nor command substitution, so instead we
-# post-process each arg (as a line of input to sed) to backslash-escape any
-# character that might be a shell metacharacter, then use eval to reverse
-# that process (while maintaining the separation between arguments), and wrap
-# the whole thing up as a single "set" statement.
-#
-# This will of course break if any of these variables contains a newline or
-# an unmatched quote.
-#
-
-eval "set -- $(
- printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
- xargs -n1 |
- sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
- tr '\n' ' '
- )" '"$@"'
-
-exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
deleted file mode 100644
index 9b42019c..00000000
--- a/gradlew.bat
+++ /dev/null
@@ -1,94 +0,0 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-@rem SPDX-License-Identifier: Apache-2.0
-@rem
-
-@if "%DEBUG%"=="" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%"=="" set DIRNAME=.
-@rem This is normally unused
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if %ERRORLEVEL% equ 0 goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-goto fail
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if %ERRORLEVEL% equ 0 goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-set EXIT_CODE=%ERRORLEVEL%
-if %EXIT_CODE% equ 0 set EXIT_CODE=1
-if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
-exit /b %EXIT_CODE%
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..8ba56009
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,8065 @@
+{
+ "name": "codeanalyzer-java-docs",
+ "version": "0.0.1",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "codeanalyzer-java-docs",
+ "version": "0.0.1",
+ "dependencies": {
+ "@astrojs/starlight": "^0.37.6",
+ "@expressive-code/plugin-collapsible-sections": "^0.42.0",
+ "@expressive-code/plugin-line-numbers": "^0.42.0",
+ "astro": "^5.18.0",
+ "astro-mermaid": "^2.0.2",
+ "mermaid": "^11.15.0",
+ "sharp": "^0.34.2"
+ }
+ },
+ "node_modules/@antfu/install-pkg": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz",
+ "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==",
+ "license": "MIT",
+ "dependencies": {
+ "package-manager-detector": "^1.3.0",
+ "tinyexec": "^1.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/@astrojs/compiler": {
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.13.1.tgz",
+ "integrity": "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==",
+ "license": "MIT"
+ },
+ "node_modules/@astrojs/internal-helpers": {
+ "version": "0.7.6",
+ "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.6.tgz",
+ "integrity": "sha512-GOle7smBWKfMSP8osUIGOlB5kaHdQLV3foCsf+5Q9Wsuu+C6Fs3Ez/ttXmhjZ1HkSgsogcM1RXSjjOVieHq16Q==",
+ "license": "MIT"
+ },
+ "node_modules/@astrojs/markdown-remark": {
+ "version": "6.3.11",
+ "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.11.tgz",
+ "integrity": "sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/internal-helpers": "0.7.6",
+ "@astrojs/prism": "3.3.0",
+ "github-slugger": "^2.0.0",
+ "hast-util-from-html": "^2.0.3",
+ "hast-util-to-text": "^4.0.2",
+ "import-meta-resolve": "^4.2.0",
+ "js-yaml": "^4.1.1",
+ "mdast-util-definitions": "^6.0.0",
+ "rehype-raw": "^7.0.0",
+ "rehype-stringify": "^10.0.1",
+ "remark-gfm": "^4.0.1",
+ "remark-parse": "^11.0.0",
+ "remark-rehype": "^11.1.2",
+ "remark-smartypants": "^3.0.2",
+ "shiki": "^3.21.0",
+ "smol-toml": "^1.6.0",
+ "unified": "^11.0.5",
+ "unist-util-remove-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "unist-util-visit-parents": "^6.0.2",
+ "vfile": "^6.0.3"
+ }
+ },
+ "node_modules/@astrojs/mdx": {
+ "version": "4.3.14",
+ "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-4.3.14.tgz",
+ "integrity": "sha512-FBrqJQORVm+rkRa2TS5CjU9PBA6hkhrwLVBSS9A77gN2+iehvjq1w6yya/d0YKC7osiVorKkr3Qd9wNbl0ZkGA==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/markdown-remark": "6.3.11",
+ "@mdx-js/mdx": "^3.1.1",
+ "acorn": "^8.15.0",
+ "es-module-lexer": "^1.7.0",
+ "estree-util-visit": "^2.0.0",
+ "hast-util-to-html": "^9.0.5",
+ "piccolore": "^0.1.3",
+ "rehype-raw": "^7.0.0",
+ "remark-gfm": "^4.0.1",
+ "remark-smartypants": "^3.0.2",
+ "source-map": "^0.7.6",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.3"
+ },
+ "engines": {
+ "node": "18.20.8 || ^20.3.0 || >=22.0.0"
+ },
+ "peerDependencies": {
+ "astro": "^5.0.0"
+ }
+ },
+ "node_modules/@astrojs/prism": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.3.0.tgz",
+ "integrity": "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "prismjs": "^1.30.0"
+ },
+ "engines": {
+ "node": "18.20.8 || ^20.3.0 || >=22.0.0"
+ }
+ },
+ "node_modules/@astrojs/sitemap": {
+ "version": "3.7.3",
+ "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.3.tgz",
+ "integrity": "sha512-f8euLVsyeAmAkSm/1M2Kb8sL8byQmfgbvBNaHFItCheTj/IpiJYSEWVcqDHZ/yEHxiS7+w87mQkzwZaPHmk5GA==",
+ "license": "MIT",
+ "dependencies": {
+ "sitemap": "^9.0.0",
+ "stream-replace-string": "^2.0.0",
+ "zod": "^4.3.6"
+ }
+ },
+ "node_modules/@astrojs/starlight": {
+ "version": "0.37.7",
+ "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.37.7.tgz",
+ "integrity": "sha512-KyBnou8aKIlPJUSNx6a1SN7XyH22oj/VAvTGC+Edld4Bnei1A//pmCRTBvSrSeoGrdUjK0ErFUfaEhhO1bPfDg==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/markdown-remark": "^6.3.1",
+ "@astrojs/mdx": "^4.2.3",
+ "@astrojs/sitemap": "^3.3.0",
+ "@pagefind/default-ui": "^1.3.0",
+ "@types/hast": "^3.0.4",
+ "@types/js-yaml": "^4.0.9",
+ "@types/mdast": "^4.0.4",
+ "astro-expressive-code": "^0.41.1",
+ "bcp-47": "^2.1.0",
+ "hast-util-from-html": "^2.0.1",
+ "hast-util-select": "^6.0.2",
+ "hast-util-to-string": "^3.0.0",
+ "hastscript": "^9.0.0",
+ "i18next": "^23.11.5",
+ "js-yaml": "^4.1.0",
+ "klona": "^2.0.6",
+ "magic-string": "^0.30.17",
+ "mdast-util-directive": "^3.0.0",
+ "mdast-util-to-markdown": "^2.1.0",
+ "mdast-util-to-string": "^4.0.0",
+ "pagefind": "^1.3.0",
+ "rehype": "^13.0.1",
+ "rehype-format": "^5.0.0",
+ "remark-directive": "^3.0.0",
+ "ultrahtml": "^1.6.0",
+ "unified": "^11.0.5",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.2"
+ },
+ "peerDependencies": {
+ "astro": "^5.5.0"
+ }
+ },
+ "node_modules/@astrojs/telemetry": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.0.tgz",
+ "integrity": "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ci-info": "^4.2.0",
+ "debug": "^4.4.0",
+ "dlv": "^1.1.3",
+ "dset": "^3.1.4",
+ "is-docker": "^3.0.0",
+ "is-wsl": "^3.1.0",
+ "which-pm-runs": "^1.1.0"
+ },
+ "engines": {
+ "node": "18.20.8 || ^20.3.0 || >=22.0.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+ "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.7"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz",
+ "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+ "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@braintree/sanitize-url": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz",
+ "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==",
+ "license": "MIT"
+ },
+ "node_modules/@capsizecss/unpack": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.0.tgz",
+ "integrity": "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==",
+ "license": "MIT",
+ "dependencies": {
+ "fontkitten": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@chevrotain/types": {
+ "version": "11.1.2",
+ "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.1.2.tgz",
+ "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@ctrl/tinycolor": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.2.0.tgz",
+ "integrity": "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
+ "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
+ "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz",
+ "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz",
+ "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz",
+ "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz",
+ "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz",
+ "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz",
+ "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz",
+ "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz",
+ "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz",
+ "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz",
+ "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz",
+ "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz",
+ "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz",
+ "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz",
+ "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz",
+ "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz",
+ "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz",
+ "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz",
+ "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz",
+ "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz",
+ "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@expressive-code/core": {
+ "version": "0.42.0",
+ "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.42.0.tgz",
+ "integrity": "sha512-MN11+9nfmaC7sYu2BZJXAXqwkBRt8t1xTSqP+Ti1NfTEskgl6xUnzDxoaiQkg0BMzpglA0pys4dpDKquP/cyIw==",
+ "license": "MIT",
+ "dependencies": {
+ "@ctrl/tinycolor": "^4.0.4",
+ "hast-util-select": "^6.0.2",
+ "hast-util-to-html": "^9.0.1",
+ "hast-util-to-text": "^4.0.1",
+ "hastscript": "^9.0.0",
+ "postcss": "^8.4.38",
+ "postcss-nested": "^6.0.1",
+ "unist-util-visit": "^5.0.0",
+ "unist-util-visit-parents": "^6.0.1"
+ }
+ },
+ "node_modules/@expressive-code/plugin-collapsible-sections": {
+ "version": "0.42.0",
+ "resolved": "https://registry.npmjs.org/@expressive-code/plugin-collapsible-sections/-/plugin-collapsible-sections-0.42.0.tgz",
+ "integrity": "sha512-HAksURXFxFmN/tuqIHvt8N47WVFTG7kYWZswJ1LMuts6496l9c7nBBRZCaTpce1dOLRrfGjKytWixqkgGWno8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@expressive-code/core": "^0.42.0"
+ }
+ },
+ "node_modules/@expressive-code/plugin-frames": {
+ "version": "0.41.7",
+ "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.41.7.tgz",
+ "integrity": "sha512-diKtxjQw/979cTglRFaMCY/sR6hWF0kSMg8jsKLXaZBSfGS0I/Hoe7Qds3vVEgeoW+GHHQzMcwvgx/MOIXhrTA==",
+ "license": "MIT",
+ "dependencies": {
+ "@expressive-code/core": "^0.41.7"
+ }
+ },
+ "node_modules/@expressive-code/plugin-frames/node_modules/@expressive-code/core": {
+ "version": "0.41.7",
+ "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.41.7.tgz",
+ "integrity": "sha512-ck92uZYZ9Wba2zxkiZLsZGi9N54pMSAVdrI9uW3Oo9AtLglD5RmrdTwbYPCT2S/jC36JGB2i+pnQtBm/Ib2+dg==",
+ "license": "MIT",
+ "dependencies": {
+ "@ctrl/tinycolor": "^4.0.4",
+ "hast-util-select": "^6.0.2",
+ "hast-util-to-html": "^9.0.1",
+ "hast-util-to-text": "^4.0.1",
+ "hastscript": "^9.0.0",
+ "postcss": "^8.4.38",
+ "postcss-nested": "^6.0.1",
+ "unist-util-visit": "^5.0.0",
+ "unist-util-visit-parents": "^6.0.1"
+ }
+ },
+ "node_modules/@expressive-code/plugin-line-numbers": {
+ "version": "0.42.0",
+ "resolved": "https://registry.npmjs.org/@expressive-code/plugin-line-numbers/-/plugin-line-numbers-0.42.0.tgz",
+ "integrity": "sha512-xY0s/b8UTF8fGrHWbJ8uX95yCDU1NpepXOTtcLBJobQV08RA7G+hzxL0BtDRso7RK4bCrJHio+BgYzI/M21BBA==",
+ "license": "MIT",
+ "dependencies": {
+ "@expressive-code/core": "^0.42.0"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki": {
+ "version": "0.41.7",
+ "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.41.7.tgz",
+ "integrity": "sha512-DL605bLrUOgqTdZ0Ot5MlTaWzppRkzzqzeGEu7ODnHF39IkEBbFdsC7pbl3LbUQ1DFtnfx6rD54k/cdofbW6KQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@expressive-code/core": "^0.41.7",
+ "shiki": "^3.2.2"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki/node_modules/@expressive-code/core": {
+ "version": "0.41.7",
+ "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.41.7.tgz",
+ "integrity": "sha512-ck92uZYZ9Wba2zxkiZLsZGi9N54pMSAVdrI9uW3Oo9AtLglD5RmrdTwbYPCT2S/jC36JGB2i+pnQtBm/Ib2+dg==",
+ "license": "MIT",
+ "dependencies": {
+ "@ctrl/tinycolor": "^4.0.4",
+ "hast-util-select": "^6.0.2",
+ "hast-util-to-html": "^9.0.1",
+ "hast-util-to-text": "^4.0.1",
+ "hastscript": "^9.0.0",
+ "postcss": "^8.4.38",
+ "postcss-nested": "^6.0.1",
+ "unist-util-visit": "^5.0.0",
+ "unist-util-visit-parents": "^6.0.1"
+ }
+ },
+ "node_modules/@expressive-code/plugin-text-markers": {
+ "version": "0.41.7",
+ "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.41.7.tgz",
+ "integrity": "sha512-Ewpwuc5t6eFdZmWlFyeuy3e1PTQC0jFvw2Q+2bpcWXbOZhPLsT7+h8lsSIJxb5mS7wZko7cKyQ2RLYDyK6Fpmw==",
+ "license": "MIT",
+ "dependencies": {
+ "@expressive-code/core": "^0.41.7"
+ }
+ },
+ "node_modules/@expressive-code/plugin-text-markers/node_modules/@expressive-code/core": {
+ "version": "0.41.7",
+ "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.41.7.tgz",
+ "integrity": "sha512-ck92uZYZ9Wba2zxkiZLsZGi9N54pMSAVdrI9uW3Oo9AtLglD5RmrdTwbYPCT2S/jC36JGB2i+pnQtBm/Ib2+dg==",
+ "license": "MIT",
+ "dependencies": {
+ "@ctrl/tinycolor": "^4.0.4",
+ "hast-util-select": "^6.0.2",
+ "hast-util-to-html": "^9.0.1",
+ "hast-util-to-text": "^4.0.1",
+ "hastscript": "^9.0.0",
+ "postcss": "^8.4.38",
+ "postcss-nested": "^6.0.1",
+ "unist-util-visit": "^5.0.0",
+ "unist-util-visit-parents": "^6.0.1"
+ }
+ },
+ "node_modules/@iconify/types": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
+ "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
+ "license": "MIT"
+ },
+ "node_modules/@iconify/utils": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.3.tgz",
+ "integrity": "sha512-LPKOXPn/zV+zis1oOfGWogaXVpqUybF3ZS6SCZIsz8vg0ivVp9+fVqyYB7xq0aiST/VhUQYGO1qo6uoYSiEJqw==",
+ "license": "MIT",
+ "dependencies": {
+ "@antfu/install-pkg": "^1.1.0",
+ "@iconify/types": "^2.0.0",
+ "import-meta-resolve": "^4.2.0"
+ }
+ },
+ "node_modules/@img/colour": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
+ "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
+ "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
+ "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
+ "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
+ "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
+ "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
+ "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
+ "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
+ "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
+ "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
+ "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
+ "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
+ "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
+ "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
+ "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
+ "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
+ "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
+ "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
+ "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
+ "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
+ "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
+ "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.7.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
+ "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
+ "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
+ "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "license": "MIT"
+ },
+ "node_modules/@mdx-js/mdx": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz",
+ "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdx": "^2.0.0",
+ "acorn": "^8.0.0",
+ "collapse-white-space": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "estree-util-scope": "^1.0.0",
+ "estree-walker": "^3.0.0",
+ "hast-util-to-jsx-runtime": "^2.0.0",
+ "markdown-extensions": "^2.0.0",
+ "recma-build-jsx": "^1.0.0",
+ "recma-jsx": "^1.0.0",
+ "recma-stringify": "^1.0.0",
+ "rehype-recma": "^1.0.0",
+ "remark-mdx": "^3.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-rehype": "^11.0.0",
+ "source-map": "^0.7.0",
+ "unified": "^11.0.0",
+ "unist-util-position-from-estree": "^2.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/@mermaid-js/parser": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.1.1.tgz",
+ "integrity": "sha512-VuHdsYMK1bT6X2JbcAaWAhugTRvRBRyuZgd+c22swUeI9g/ntaxF7CY7dYarhZovofCbUNO0G7JesfmNtjYOCw==",
+ "license": "MIT",
+ "dependencies": {
+ "@chevrotain/types": "~11.1.1"
+ }
+ },
+ "node_modules/@oslojs/encoding": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz",
+ "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==",
+ "license": "MIT"
+ },
+ "node_modules/@pagefind/darwin-arm64": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.5.2.tgz",
+ "integrity": "sha512-MXpI+7HsAdPkvJ0gk9xj9g541BCqBZOBbdwj9g6lB5LCj6kSV6nqDSjzcAJwvOsfu0fjwvC8hQU+ecfhp+MpiQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@pagefind/darwin-x64": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.5.2.tgz",
+ "integrity": "sha512-IojxFWMEJe0RQ7PQ3KXQsPIImNsbpPYpoZ+QUDrL8fAl/O27IX+LVLs74/UzEZy5uA2LD8Nz1AiwKr72vrkZQw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@pagefind/default-ui": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@pagefind/default-ui/-/default-ui-1.5.2.tgz",
+ "integrity": "sha512-pm1LMnQg8N2B3n2TnjKlhaFihpz6zTiA4HiGQ6/slKO/+8K9CAU5kcjdSSPgpuk1PMuuN4hxLipUIifnrkl3Sg==",
+ "license": "MIT"
+ },
+ "node_modules/@pagefind/freebsd-x64": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.5.2.tgz",
+ "integrity": "sha512-7EVzo9+0w+2cbe671BtMj10UlNo83I+HrLVLfRxO731svHRJKUfJ/mo05gU14pe9PCfpKNQT8FS3Xc/oDN6pOA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@pagefind/linux-arm64": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.5.2.tgz",
+ "integrity": "sha512-Ovt9+K35sqzn8H3ZMXGwls4TD/wMJuvRtShHIsmUQREmaxjrDEX7gHckRCrwYJ4XE1H1p6HkLz3wukrAnsfXQw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@pagefind/linux-x64": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.5.2.tgz",
+ "integrity": "sha512-V+tFqHKXhQKq/WqPBD67AFy7scn1/aZID00ws4fSDd+1daSi5UHR9VVlRrOUYKxn3VuFQYRD7lYXdZK1WED1YA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@pagefind/windows-arm64": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@pagefind/windows-arm64/-/windows-arm64-1.5.2.tgz",
+ "integrity": "sha512-hN9Nh90fNW61nNRCW9ZyQrAj/mD0eRvmJ8NlTUzkbuW8kIzGJUi3cxjFkEcMZ5h/8FsKWD/VcouZl4yo1F7B6g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@pagefind/windows-x64": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.5.2.tgz",
+ "integrity": "sha512-Fa2Iyw7kaDRzGMfNYNUXNW2zbL5FQVDgSOcbDHdzBrDEdpqOqg8TcZ68F22ol6NJ9IGzvUdmeyZypLW5dyhqsg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/pluginutils": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz",
+ "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/pluginutils/node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz",
+ "integrity": "sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.61.1.tgz",
+ "integrity": "sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.61.1.tgz",
+ "integrity": "sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.61.1.tgz",
+ "integrity": "sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.61.1.tgz",
+ "integrity": "sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.61.1.tgz",
+ "integrity": "sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.61.1.tgz",
+ "integrity": "sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.61.1.tgz",
+ "integrity": "sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.61.1.tgz",
+ "integrity": "sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.61.1.tgz",
+ "integrity": "sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.61.1.tgz",
+ "integrity": "sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.61.1.tgz",
+ "integrity": "sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.61.1.tgz",
+ "integrity": "sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.61.1.tgz",
+ "integrity": "sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.61.1.tgz",
+ "integrity": "sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.61.1.tgz",
+ "integrity": "sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.61.1.tgz",
+ "integrity": "sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.1.tgz",
+ "integrity": "sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.61.1.tgz",
+ "integrity": "sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.61.1.tgz",
+ "integrity": "sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.61.1.tgz",
+ "integrity": "sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.61.1.tgz",
+ "integrity": "sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.61.1.tgz",
+ "integrity": "sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.61.1.tgz",
+ "integrity": "sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.61.1.tgz",
+ "integrity": "sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@shikijs/core": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.23.0.tgz",
+ "integrity": "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.23.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4",
+ "hast-util-to-html": "^9.0.5"
+ }
+ },
+ "node_modules/@shikijs/engine-javascript": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.23.0.tgz",
+ "integrity": "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.23.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "oniguruma-to-es": "^4.3.4"
+ }
+ },
+ "node_modules/@shikijs/engine-oniguruma": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz",
+ "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.23.0",
+ "@shikijs/vscode-textmate": "^10.0.2"
+ }
+ },
+ "node_modules/@shikijs/langs": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz",
+ "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.23.0"
+ }
+ },
+ "node_modules/@shikijs/themes": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz",
+ "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.23.0"
+ }
+ },
+ "node_modules/@shikijs/types": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz",
+ "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4"
+ }
+ },
+ "node_modules/@shikijs/vscode-textmate": {
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz",
+ "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",
+ "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-array": "*",
+ "@types/d3-axis": "*",
+ "@types/d3-brush": "*",
+ "@types/d3-chord": "*",
+ "@types/d3-color": "*",
+ "@types/d3-contour": "*",
+ "@types/d3-delaunay": "*",
+ "@types/d3-dispatch": "*",
+ "@types/d3-drag": "*",
+ "@types/d3-dsv": "*",
+ "@types/d3-ease": "*",
+ "@types/d3-fetch": "*",
+ "@types/d3-force": "*",
+ "@types/d3-format": "*",
+ "@types/d3-geo": "*",
+ "@types/d3-hierarchy": "*",
+ "@types/d3-interpolate": "*",
+ "@types/d3-path": "*",
+ "@types/d3-polygon": "*",
+ "@types/d3-quadtree": "*",
+ "@types/d3-random": "*",
+ "@types/d3-scale": "*",
+ "@types/d3-scale-chromatic": "*",
+ "@types/d3-selection": "*",
+ "@types/d3-shape": "*",
+ "@types/d3-time": "*",
+ "@types/d3-time-format": "*",
+ "@types/d3-timer": "*",
+ "@types/d3-transition": "*",
+ "@types/d3-zoom": "*"
+ }
+ },
+ "node_modules/@types/d3-array": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",
+ "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-axis": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz",
+ "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-brush": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz",
+ "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-chord": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz",
+ "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-color": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+ "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-contour": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz",
+ "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-array": "*",
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-dispatch": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz",
+ "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-drag": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
+ "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-dsv": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz",
+ "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-ease": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
+ "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-fetch": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz",
+ "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-dsv": "*"
+ }
+ },
+ "node_modules/@types/d3-force": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz",
+ "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-format": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz",
+ "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-geo": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz",
+ "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/d3-hierarchy": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz",
+ "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-interpolate": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+ "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-color": "*"
+ }
+ },
+ "node_modules/@types/d3-path": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
+ "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-polygon": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz",
+ "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-quadtree": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz",
+ "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-random": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz",
+ "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-scale": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
+ "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-time": "*"
+ }
+ },
+ "node_modules/@types/d3-scale-chromatic": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+ "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-selection": {
+ "version": "3.0.11",
+ "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz",
+ "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-shape": {
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz",
+ "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-path": "*"
+ }
+ },
+ "node_modules/@types/d3-time": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
+ "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-time-format": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",
+ "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-timer": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
+ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-transition": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz",
+ "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-zoom": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
+ "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-interpolate": "*",
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/debug": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz",
+ "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/estree-jsx": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz",
+ "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@types/geojson": {
+ "version": "7946.0.16",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
+ "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/hast": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/js-yaml": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz",
+ "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/mdast": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
+ "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/mdx": {
+ "version": "2.0.13",
+ "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz",
+ "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/nlcst": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz",
+ "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "24.13.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.0.tgz",
+ "integrity": "sha512-5vtOqGQr4NJKeEzV441FcOi2MeG9UTWq9LqVLGneDdu4vlX17H8kQ2PA2UmNwCUGPVDj4oBjNhS7ReVEIWJJrg==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.18.0"
+ }
+ },
+ "node_modules/@types/sax": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz",
+ "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/trusted-types": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@types/unist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+ "license": "MIT"
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz",
+ "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==",
+ "license": "ISC"
+ },
+ "node_modules/@upsetjs/venn.js": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz",
+ "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==",
+ "license": "MIT",
+ "optionalDependencies": {
+ "d3-selection": "^3.0.0",
+ "d3-transition": "^3.0.1"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ansi-align": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
+ "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.1.0"
+ }
+ },
+ "node_modules/ansi-align/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-align/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/ansi-align/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-align/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/anymatch/node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/arg": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+ "license": "MIT"
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "license": "Python-2.0"
+ },
+ "node_modules/aria-query": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
+ "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/array-iterate": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz",
+ "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/astring": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz",
+ "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==",
+ "license": "MIT",
+ "bin": {
+ "astring": "bin/astring"
+ }
+ },
+ "node_modules/astro": {
+ "version": "5.18.2",
+ "resolved": "https://registry.npmjs.org/astro/-/astro-5.18.2.tgz",
+ "integrity": "sha512-TnFwLnAXty5MXKPDGuKXqK4AMBXG+FH6RUdK7Oyc3gyfNoFIthT+4eRbzOK43bdRlLaZuxgciDSjgtggZ3OtGQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@astrojs/compiler": "^2.13.0",
+ "@astrojs/internal-helpers": "0.7.6",
+ "@astrojs/markdown-remark": "6.3.11",
+ "@astrojs/telemetry": "3.3.0",
+ "@capsizecss/unpack": "^4.0.0",
+ "@oslojs/encoding": "^1.1.0",
+ "@rollup/pluginutils": "^5.3.0",
+ "acorn": "^8.15.0",
+ "aria-query": "^5.3.2",
+ "axobject-query": "^4.1.0",
+ "boxen": "8.0.1",
+ "ci-info": "^4.3.1",
+ "clsx": "^2.1.1",
+ "common-ancestor-path": "^1.0.1",
+ "cookie": "^1.1.1",
+ "cssesc": "^3.0.0",
+ "debug": "^4.4.3",
+ "deterministic-object-hash": "^2.0.2",
+ "devalue": "^5.6.2",
+ "diff": "^8.0.3",
+ "dlv": "^1.1.3",
+ "dset": "^3.1.4",
+ "es-module-lexer": "^1.7.0",
+ "esbuild": "^0.27.3",
+ "estree-walker": "^3.0.3",
+ "flattie": "^1.1.1",
+ "fontace": "~0.4.0",
+ "github-slugger": "^2.0.0",
+ "html-escaper": "3.0.3",
+ "http-cache-semantics": "^4.2.0",
+ "import-meta-resolve": "^4.2.0",
+ "js-yaml": "^4.1.1",
+ "magic-string": "^0.30.21",
+ "magicast": "^0.5.1",
+ "mrmime": "^2.0.1",
+ "neotraverse": "^0.6.18",
+ "p-limit": "^6.2.0",
+ "p-queue": "^8.1.1",
+ "package-manager-detector": "^1.6.0",
+ "piccolore": "^0.1.3",
+ "picomatch": "^4.0.3",
+ "prompts": "^2.4.2",
+ "rehype": "^13.0.2",
+ "semver": "^7.7.3",
+ "shiki": "^3.21.0",
+ "smol-toml": "^1.6.0",
+ "svgo": "^4.0.0",
+ "tinyexec": "^1.0.2",
+ "tinyglobby": "^0.2.15",
+ "tsconfck": "^3.1.6",
+ "ultrahtml": "^1.6.0",
+ "unifont": "~0.7.3",
+ "unist-util-visit": "^5.0.0",
+ "unstorage": "^1.17.4",
+ "vfile": "^6.0.3",
+ "vite": "^6.4.1",
+ "vitefu": "^1.1.1",
+ "xxhash-wasm": "^1.1.0",
+ "yargs-parser": "^21.1.1",
+ "yocto-spinner": "^0.2.3",
+ "zod": "^3.25.76",
+ "zod-to-json-schema": "^3.25.1",
+ "zod-to-ts": "^1.2.0"
+ },
+ "bin": {
+ "astro": "astro.js"
+ },
+ "engines": {
+ "node": "18.20.8 || ^20.3.0 || >=22.0.0",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/astrodotbuild"
+ },
+ "optionalDependencies": {
+ "sharp": "^0.34.0"
+ }
+ },
+ "node_modules/astro-expressive-code": {
+ "version": "0.41.7",
+ "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.41.7.tgz",
+ "integrity": "sha512-hUpogGc6DdAd+I7pPXsctyYPRBJDK7Q7d06s4cyP0Vz3OcbziP3FNzN0jZci1BpCvLn9675DvS7B9ctKKX64JQ==",
+ "license": "MIT",
+ "dependencies": {
+ "rehype-expressive-code": "^0.41.7"
+ },
+ "peerDependencies": {
+ "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta"
+ }
+ },
+ "node_modules/astro-mermaid": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/astro-mermaid/-/astro-mermaid-2.0.2.tgz",
+ "integrity": "sha512-ihx63qwZ0hlu9mDjs6auQEXyo13s9h5HFHFIHovjTJH6ot97u0VqyGk3P1kkjPYZeOMrZ1Y7QAOevUfLs9cDfA==",
+ "license": "MIT",
+ "dependencies": {
+ "import-meta-resolve": "^4.2.0",
+ "mdast-util-to-string": "^4.0.0",
+ "unist-util-visit": "^5.0.0"
+ },
+ "peerDependencies": {
+ "@mermaid-js/layout-elk": "^0.2.0",
+ "astro": ">=4",
+ "mermaid": "^10.0.0 || ^11.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@mermaid-js/layout-elk": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/astro/node_modules/zod": {
+ "version": "3.25.76",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
+ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/astro/node_modules/zod-to-ts": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/zod-to-ts/-/zod-to-ts-1.2.0.tgz",
+ "integrity": "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==",
+ "peerDependencies": {
+ "typescript": "^4.9.4 || ^5.0.2",
+ "zod": "^3"
+ }
+ },
+ "node_modules/axobject-query": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
+ "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/bail": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
+ "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/base-64": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz",
+ "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==",
+ "license": "MIT"
+ },
+ "node_modules/bcp-47": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.0.tgz",
+ "integrity": "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w==",
+ "license": "MIT",
+ "dependencies": {
+ "is-alphabetical": "^2.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/bcp-47-match": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz",
+ "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+ "license": "ISC"
+ },
+ "node_modules/boxen": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz",
+ "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-align": "^3.0.1",
+ "camelcase": "^8.0.0",
+ "chalk": "^5.3.0",
+ "cli-boxes": "^3.0.0",
+ "string-width": "^7.2.0",
+ "type-fest": "^4.21.0",
+ "widest-line": "^5.0.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
+ "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ccount": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+ "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/character-entities": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
+ "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-html4": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
+ "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-reference-invalid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
+ "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
+ "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 20.19.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz",
+ "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cli-boxes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
+ "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/collapse-white-space": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz",
+ "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/comma-separated-tokens": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
+ "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/common-ancestor-path": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz",
+ "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==",
+ "license": "ISC"
+ },
+ "node_modules/cookie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+ "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/cookie-es": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz",
+ "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==",
+ "license": "MIT"
+ },
+ "node_modules/cose-base": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz",
+ "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==",
+ "license": "MIT",
+ "dependencies": {
+ "layout-base": "^1.0.0"
+ }
+ },
+ "node_modules/crossws": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz",
+ "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==",
+ "license": "MIT",
+ "dependencies": {
+ "uncrypto": "^0.1.3"
+ }
+ },
+ "node_modules/css-select": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
+ "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.1.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css-selector-parser": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.3.0.tgz",
+ "integrity": "sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/mdevils"
+ },
+ {
+ "type": "patreon",
+ "url": "https://patreon.com/mdevils"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/css-tree": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
+ "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==",
+ "license": "MIT",
+ "dependencies": {
+ "mdn-data": "2.27.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
+ "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "license": "MIT",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/csso": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
+ "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
+ "license": "MIT",
+ "dependencies": {
+ "css-tree": "~2.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/csso/node_modules/css-tree": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
+ "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
+ "license": "MIT",
+ "dependencies": {
+ "mdn-data": "2.0.28",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/csso/node_modules/mdn-data": {
+ "version": "2.0.28",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
+ "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
+ "license": "CC0-1.0"
+ },
+ "node_modules/cytoscape": {
+ "version": "3.34.0",
+ "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.34.0.tgz",
+ "integrity": "sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/cytoscape-cose-bilkent": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz",
+ "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==",
+ "license": "MIT",
+ "dependencies": {
+ "cose-base": "^1.0.0"
+ },
+ "peerDependencies": {
+ "cytoscape": "^3.2.0"
+ }
+ },
+ "node_modules/cytoscape-fcose": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz",
+ "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "cose-base": "^2.2.0"
+ },
+ "peerDependencies": {
+ "cytoscape": "^3.2.0"
+ }
+ },
+ "node_modules/cytoscape-fcose/node_modules/cose-base": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz",
+ "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==",
+ "license": "MIT",
+ "dependencies": {
+ "layout-base": "^2.0.0"
+ }
+ },
+ "node_modules/cytoscape-fcose/node_modules/layout-base": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz",
+ "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==",
+ "license": "MIT"
+ },
+ "node_modules/d3": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",
+ "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "3",
+ "d3-axis": "3",
+ "d3-brush": "3",
+ "d3-chord": "3",
+ "d3-color": "3",
+ "d3-contour": "4",
+ "d3-delaunay": "6",
+ "d3-dispatch": "3",
+ "d3-drag": "3",
+ "d3-dsv": "3",
+ "d3-ease": "3",
+ "d3-fetch": "3",
+ "d3-force": "3",
+ "d3-format": "3",
+ "d3-geo": "3",
+ "d3-hierarchy": "3",
+ "d3-interpolate": "3",
+ "d3-path": "3",
+ "d3-polygon": "3",
+ "d3-quadtree": "3",
+ "d3-random": "3",
+ "d3-scale": "4",
+ "d3-scale-chromatic": "3",
+ "d3-selection": "3",
+ "d3-shape": "3",
+ "d3-time": "3",
+ "d3-time-format": "4",
+ "d3-timer": "3",
+ "d3-transition": "3",
+ "d3-zoom": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-array": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+ "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+ "license": "ISC",
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-axis": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
+ "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-brush": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
+ "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "3",
+ "d3-transition": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-chord": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
+ "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-path": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-contour": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
+ "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
+ "license": "ISC",
+ "dependencies": {
+ "delaunator": "5"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-drag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
+ "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-selection": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dsv": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+ "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+ "license": "ISC",
+ "dependencies": {
+ "commander": "7",
+ "iconv-lite": "0.6",
+ "rw": "1"
+ },
+ "bin": {
+ "csv2json": "bin/dsv2json.js",
+ "csv2tsv": "bin/dsv2dsv.js",
+ "dsv2dsv": "bin/dsv2dsv.js",
+ "dsv2json": "bin/dsv2json.js",
+ "json2csv": "bin/json2dsv.js",
+ "json2dsv": "bin/json2dsv.js",
+ "json2tsv": "bin/json2dsv.js",
+ "tsv2csv": "bin/dsv2dsv.js",
+ "tsv2json": "bin/dsv2json.js"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-ease": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-fetch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
+ "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dsv": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-force": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+ "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-quadtree": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz",
+ "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-geo": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
+ "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2.5.0 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-hierarchy": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
+ "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-polygon": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
+ "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-quadtree": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+ "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-random": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
+ "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-sankey": {
+ "version": "0.12.3",
+ "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz",
+ "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-array": "1 - 2",
+ "d3-shape": "^1.2.0"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/d3-array": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+ "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "internmap": "^1.0.0"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/d3-path": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
+ "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-sankey/node_modules/d3-shape": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",
+ "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-path": "1"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/internmap": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
+ "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==",
+ "license": "ISC"
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale-chromatic": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+ "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-interpolate": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-selection": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
+ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
+ "license": "ISC",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-transition": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
+ "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-dispatch": "1 - 3",
+ "d3-ease": "1 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "d3-selection": "2 - 3"
+ }
+ },
+ "node_modules/d3-zoom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
+ "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "2 - 3",
+ "d3-transition": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/dagre-d3-es": {
+ "version": "7.0.14",
+ "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz",
+ "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==",
+ "license": "MIT",
+ "dependencies": {
+ "d3": "^7.9.0",
+ "lodash-es": "^4.17.21"
+ }
+ },
+ "node_modules/dayjs": {
+ "version": "1.11.21",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz",
+ "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==",
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decode-named-character-reference": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz",
+ "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/defu": {
+ "version": "6.1.7",
+ "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz",
+ "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==",
+ "license": "MIT"
+ },
+ "node_modules/delaunator": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz",
+ "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==",
+ "license": "ISC",
+ "dependencies": {
+ "robust-predicates": "^3.0.2"
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/destr": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz",
+ "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==",
+ "license": "MIT"
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/deterministic-object-hash": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz",
+ "integrity": "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "base-64": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/devalue": {
+ "version": "5.8.1",
+ "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz",
+ "integrity": "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==",
+ "license": "MIT"
+ },
+ "node_modules/devlop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/diff": {
+ "version": "8.0.4",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz",
+ "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/direction": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz",
+ "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==",
+ "license": "MIT",
+ "bin": {
+ "direction": "cli.js"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/dlv": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+ "license": "MIT"
+ },
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/dom-serializer/node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/dompurify": {
+ "version": "3.4.8",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.8.tgz",
+ "integrity": "sha512-yb1cEmaOum7wFvOCSQxyfgVlv5D47Rc30iZWoMpbDIWTnJ6grDDQyu2KFJzB2k7u0pMuJcQ1zphH//fFnw2tjQ==",
+ "license": "(MPL-2.0 OR Apache-2.0)",
+ "optionalDependencies": {
+ "@types/trusted-types": "^2.0.7"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
+ "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/dset": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz",
+ "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "license": "MIT"
+ },
+ "node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+ "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+ "license": "MIT"
+ },
+ "node_modules/es-toolkit": {
+ "version": "1.47.0",
+ "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.47.0.tgz",
+ "integrity": "sha512-n1GuoD0WEQZMBk5tttoZSqwgyLx01oqa5XsBmCHwPyNe1S9jPBEmtR2pSgp2kJuWE3ciFZ6yRHmY4pM4C3OOkw==",
+ "license": "MIT",
+ "workspaces": [
+ "docs",
+ "benchmarks"
+ ]
+ },
+ "node_modules/esast-util-from-estree": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz",
+ "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-visit": "^2.0.0",
+ "unist-util-position-from-estree": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/esast-util-from-js": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz",
+ "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "acorn": "^8.0.0",
+ "esast-util-from-estree": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz",
+ "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.7",
+ "@esbuild/android-arm": "0.27.7",
+ "@esbuild/android-arm64": "0.27.7",
+ "@esbuild/android-x64": "0.27.7",
+ "@esbuild/darwin-arm64": "0.27.7",
+ "@esbuild/darwin-x64": "0.27.7",
+ "@esbuild/freebsd-arm64": "0.27.7",
+ "@esbuild/freebsd-x64": "0.27.7",
+ "@esbuild/linux-arm": "0.27.7",
+ "@esbuild/linux-arm64": "0.27.7",
+ "@esbuild/linux-ia32": "0.27.7",
+ "@esbuild/linux-loong64": "0.27.7",
+ "@esbuild/linux-mips64el": "0.27.7",
+ "@esbuild/linux-ppc64": "0.27.7",
+ "@esbuild/linux-riscv64": "0.27.7",
+ "@esbuild/linux-s390x": "0.27.7",
+ "@esbuild/linux-x64": "0.27.7",
+ "@esbuild/netbsd-arm64": "0.27.7",
+ "@esbuild/netbsd-x64": "0.27.7",
+ "@esbuild/openbsd-arm64": "0.27.7",
+ "@esbuild/openbsd-x64": "0.27.7",
+ "@esbuild/openharmony-arm64": "0.27.7",
+ "@esbuild/sunos-x64": "0.27.7",
+ "@esbuild/win32-arm64": "0.27.7",
+ "@esbuild/win32-ia32": "0.27.7",
+ "@esbuild/win32-x64": "0.27.7"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/estree-util-attach-comments": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz",
+ "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-build-jsx": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz",
+ "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "estree-walker": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-is-identifier-name": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
+ "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-scope": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz",
+ "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-to-js": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz",
+ "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "astring": "^1.8.0",
+ "source-map": "^0.7.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-visit": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz",
+ "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
+ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
+ "license": "MIT"
+ },
+ "node_modules/expressive-code": {
+ "version": "0.41.7",
+ "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.41.7.tgz",
+ "integrity": "sha512-2wZjC8OQ3TaVEMcBtYY4Va3lo6J+Ai9jf3d4dbhURMJcU4Pbqe6EcHe424MIZI0VHUA1bR6xdpoHYi3yxokWqA==",
+ "license": "MIT",
+ "dependencies": {
+ "@expressive-code/core": "^0.41.7",
+ "@expressive-code/plugin-frames": "^0.41.7",
+ "@expressive-code/plugin-shiki": "^0.41.7",
+ "@expressive-code/plugin-text-markers": "^0.41.7"
+ }
+ },
+ "node_modules/expressive-code/node_modules/@expressive-code/core": {
+ "version": "0.41.7",
+ "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.41.7.tgz",
+ "integrity": "sha512-ck92uZYZ9Wba2zxkiZLsZGi9N54pMSAVdrI9uW3Oo9AtLglD5RmrdTwbYPCT2S/jC36JGB2i+pnQtBm/Ib2+dg==",
+ "license": "MIT",
+ "dependencies": {
+ "@ctrl/tinycolor": "^4.0.4",
+ "hast-util-select": "^6.0.2",
+ "hast-util-to-html": "^9.0.1",
+ "hast-util-to-text": "^4.0.1",
+ "hastscript": "^9.0.0",
+ "postcss": "^8.4.38",
+ "postcss-nested": "^6.0.1",
+ "unist-util-visit": "^5.0.0",
+ "unist-util-visit-parents": "^6.0.1"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "license": "MIT"
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/flattie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz",
+ "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/fontace": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz",
+ "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==",
+ "license": "MIT",
+ "dependencies": {
+ "fontkitten": "^1.0.2"
+ }
+ },
+ "node_modules/fontkitten": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz",
+ "integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==",
+ "license": "MIT",
+ "dependencies": {
+ "tiny-inflate": "^1.0.3"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/get-east-asian-width": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
+ "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/github-slugger": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz",
+ "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==",
+ "license": "ISC"
+ },
+ "node_modules/h3": {
+ "version": "1.15.11",
+ "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz",
+ "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie-es": "^1.2.3",
+ "crossws": "^0.3.5",
+ "defu": "^6.1.6",
+ "destr": "^2.0.5",
+ "iron-webcrypto": "^1.2.1",
+ "node-mock-http": "^1.0.4",
+ "radix3": "^1.1.2",
+ "ufo": "^1.6.3",
+ "uncrypto": "^0.1.3"
+ }
+ },
+ "node_modules/hachure-fill": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz",
+ "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==",
+ "license": "MIT"
+ },
+ "node_modules/hast-util-embedded": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz",
+ "integrity": "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-is-element": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-format": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/hast-util-format/-/hast-util-format-1.1.0.tgz",
+ "integrity": "sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-embedded": "^3.0.0",
+ "hast-util-minify-whitespace": "^1.0.0",
+ "hast-util-phrasing": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "html-whitespace-sensitive-tag-names": "^3.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-from-html": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz",
+ "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "devlop": "^1.1.0",
+ "hast-util-from-parse5": "^8.0.0",
+ "parse5": "^7.0.0",
+ "vfile": "^6.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-from-parse5": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz",
+ "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "devlop": "^1.0.0",
+ "hastscript": "^9.0.0",
+ "property-information": "^7.0.0",
+ "vfile": "^6.0.0",
+ "vfile-location": "^5.0.0",
+ "web-namespaces": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-has-property": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz",
+ "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-is-body-ok-link": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.1.tgz",
+ "integrity": "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-is-element": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz",
+ "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-minify-whitespace": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-minify-whitespace/-/hast-util-minify-whitespace-1.0.1.tgz",
+ "integrity": "sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-embedded": "^3.0.0",
+ "hast-util-is-element": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-parse-selector": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz",
+ "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-phrasing": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-3.0.1.tgz",
+ "integrity": "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-embedded": "^3.0.0",
+ "hast-util-has-property": "^3.0.0",
+ "hast-util-is-body-ok-link": "^3.0.0",
+ "hast-util-is-element": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-raw": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz",
+ "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "hast-util-from-parse5": "^8.0.0",
+ "hast-util-to-parse5": "^8.0.0",
+ "html-void-elements": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "parse5": "^7.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0",
+ "web-namespaces": "^2.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-select": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.4.tgz",
+ "integrity": "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "bcp-47-match": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "css-selector-parser": "^3.0.0",
+ "devlop": "^1.0.0",
+ "direction": "^2.0.0",
+ "hast-util-has-property": "^3.0.0",
+ "hast-util-to-string": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "nth-check": "^2.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "unist-util-visit": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-estree": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz",
+ "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-attach-comments": "^3.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "style-to-js": "^1.0.0",
+ "unist-util-position": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-html": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz",
+ "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "html-void-elements": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "stringify-entities": "^4.0.0",
+ "zwitch": "^2.0.4"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-jsx-runtime": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz",
+ "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "style-to-js": "^1.0.0",
+ "unist-util-position": "^5.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-parse5": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz",
+ "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "web-namespaces": "^2.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-string": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz",
+ "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-text": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz",
+ "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "hast-util-is-element": "^3.0.0",
+ "unist-util-find-after": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-whitespace": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
+ "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hastscript": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz",
+ "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-parse-selector": "^4.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/html-escaper": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
+ "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==",
+ "license": "MIT"
+ },
+ "node_modules/html-void-elements": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
+ "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/html-whitespace-sensitive-tag-names": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/html-whitespace-sensitive-tag-names/-/html-whitespace-sensitive-tag-names-3.0.1.tgz",
+ "integrity": "sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/http-cache-semantics": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+ "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/i18next": {
+ "version": "23.16.8",
+ "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.8.tgz",
+ "integrity": "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://locize.com"
+ },
+ {
+ "type": "individual",
+ "url": "https://locize.com/i18next.html"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.2"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/import-meta-resolve": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz",
+ "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/inline-style-parser": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
+ "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==",
+ "license": "MIT"
+ },
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/iron-webcrypto": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz",
+ "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/brc-dd"
+ }
+ },
+ "node_modules/is-alphabetical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
+ "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-alphanumerical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
+ "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-alphabetical": "^2.0.0",
+ "is-decimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-decimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
+ "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-docker": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
+ "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
+ "license": "MIT",
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-hexadecimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
+ "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-inside-container": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
+ "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
+ "license": "MIT",
+ "dependencies": {
+ "is-docker": "^3.0.0"
+ },
+ "bin": {
+ "is-inside-container": "cli.js"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-wsl": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz",
+ "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-inside-container": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/js-yaml": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
+ "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/katex": {
+ "version": "0.16.47",
+ "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz",
+ "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==",
+ "funding": [
+ "https://opencollective.com/katex",
+ "https://github.com/sponsors/katex"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "commander": "^8.3.0"
+ },
+ "bin": {
+ "katex": "cli.js"
+ }
+ },
+ "node_modules/katex/node_modules/commander": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/khroma": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz",
+ "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="
+ },
+ "node_modules/kleur": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/klona": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz",
+ "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/layout-base": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz",
+ "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash-es": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz",
+ "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
+ "license": "MIT"
+ },
+ "node_modules/longest-streak": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
+ "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "11.5.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz",
+ "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/magicast": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz",
+ "integrity": "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.3",
+ "@babel/types": "^7.29.0",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/markdown-extensions": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz",
+ "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/markdown-table": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
+ "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/marked": {
+ "version": "16.4.2",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz",
+ "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==",
+ "license": "MIT",
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/mdast-util-definitions": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz",
+ "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "unist-util-visit": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-directive": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.1.0.tgz",
+ "integrity": "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "parse-entities": "^4.0.0",
+ "stringify-entities": "^4.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-find-and-replace": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
+ "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "escape-string-regexp": "^5.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-from-markdown": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz",
+ "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark": "^4.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
+ "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-gfm-autolink-literal": "^2.0.0",
+ "mdast-util-gfm-footnote": "^2.0.0",
+ "mdast-util-gfm-strikethrough": "^2.0.0",
+ "mdast-util-gfm-table": "^2.0.0",
+ "mdast-util-gfm-task-list-item": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-autolink-literal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
+ "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-find-and-replace": "^3.0.0",
+ "micromark-util-character": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-strikethrough": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
+ "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-table": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
+ "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "markdown-table": "^3.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-task-list-item": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
+ "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz",
+ "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==",
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-expression": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
+ "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-jsx": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz",
+ "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "parse-entities": "^4.0.0",
+ "stringify-entities": "^4.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdxjs-esm": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz",
+ "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-phrasing": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
+ "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-hast": {
+ "version": "13.2.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
+ "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "trim-lines": "^3.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-markdown": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz",
+ "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "longest-streak": "^3.0.0",
+ "mdast-util-phrasing": "^4.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "unist-util-visit": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
+ "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdn-data": {
+ "version": "2.27.1",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz",
+ "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
+ "license": "CC0-1.0"
+ },
+ "node_modules/mermaid": {
+ "version": "11.15.0",
+ "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.15.0.tgz",
+ "integrity": "sha512-pTMbcf3rWdtLiYGpmoTjHEpeY8seiy6sR+9nD7LOs8KfUbHE4lOUAprTRqRAcWSQ6MQpdX+YEsxShtGsINtPtw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@braintree/sanitize-url": "^7.1.1",
+ "@iconify/utils": "^3.0.2",
+ "@mermaid-js/parser": "^1.1.1",
+ "@types/d3": "^7.4.3",
+ "@upsetjs/venn.js": "^2.0.0",
+ "cytoscape": "^3.33.1",
+ "cytoscape-cose-bilkent": "^4.1.0",
+ "cytoscape-fcose": "^2.2.0",
+ "d3": "^7.9.0",
+ "d3-sankey": "^0.12.3",
+ "dagre-d3-es": "7.0.14",
+ "dayjs": "^1.11.19",
+ "dompurify": "^3.3.1",
+ "es-toolkit": "^1.45.1",
+ "katex": "^0.16.25",
+ "khroma": "^2.1.0",
+ "marked": "^16.3.0",
+ "roughjs": "^4.6.6",
+ "stylis": "^4.3.6",
+ "ts-dedent": "^2.2.0",
+ "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0"
+ }
+ },
+ "node_modules/micromark": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
+ "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/debug": "^4.0.0",
+ "debug": "^4.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-core-commonmark": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz",
+ "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-destination": "^2.0.0",
+ "micromark-factory-label": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-factory-title": "^2.0.0",
+ "micromark-factory-whitespace": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-html-tag-name": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-directive": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz",
+ "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-factory-whitespace": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "parse-entities": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
+ "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-extension-gfm-autolink-literal": "^2.0.0",
+ "micromark-extension-gfm-footnote": "^2.0.0",
+ "micromark-extension-gfm-strikethrough": "^2.0.0",
+ "micromark-extension-gfm-table": "^2.0.0",
+ "micromark-extension-gfm-tagfilter": "^2.0.0",
+ "micromark-extension-gfm-task-list-item": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-autolink-literal": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
+ "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-strikethrough": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
+ "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-table": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
+ "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-tagfilter": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
+ "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-task-list-item": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
+ "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdx-expression": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz",
+ "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-mdx-expression": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-mdx-jsx": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz",
+ "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "micromark-factory-mdx-expression": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdx-md": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz",
+ "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdxjs": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz",
+ "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==",
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.0.0",
+ "acorn-jsx": "^5.0.0",
+ "micromark-extension-mdx-expression": "^3.0.0",
+ "micromark-extension-mdx-jsx": "^3.0.0",
+ "micromark-extension-mdx-md": "^2.0.0",
+ "micromark-extension-mdxjs-esm": "^3.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdxjs-esm": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz",
+ "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-position-from-estree": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-factory-destination": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
+ "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-label": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
+ "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-mdx-expression": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz",
+ "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-position-from-estree": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ }
+ },
+ "node_modules/micromark-factory-space": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
+ "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-title": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
+ "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-whitespace": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
+ "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-chunked": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
+ "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-classify-character": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
+ "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-combine-extensions": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
+ "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-numeric-character-reference": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
+ "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-string": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz",
+ "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-encode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
+ "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-events-to-acorn": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz",
+ "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/unist": "^3.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-visit": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ }
+ },
+ "node_modules/micromark-util-html-tag-name": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
+ "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-normalize-identifier": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
+ "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-resolve-all": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
+ "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-sanitize-uri": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
+ "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-subtokenize": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
+ "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-types": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
+ "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/mrmime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
+ "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.12",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
+ "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/neotraverse": {
+ "version": "0.6.18",
+ "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz",
+ "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/nlcst-to-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz",
+ "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/nlcst": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/node-fetch-native": {
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz",
+ "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==",
+ "license": "MIT"
+ },
+ "node_modules/node-mock-http": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz",
+ "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==",
+ "license": "MIT"
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
+ "node_modules/ofetch": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz",
+ "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==",
+ "license": "MIT",
+ "dependencies": {
+ "destr": "^2.0.5",
+ "node-fetch-native": "^1.6.7",
+ "ufo": "^1.6.1"
+ }
+ },
+ "node_modules/ohash": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz",
+ "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
+ "license": "MIT"
+ },
+ "node_modules/oniguruma-parser": {
+ "version": "0.12.2",
+ "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz",
+ "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==",
+ "license": "MIT"
+ },
+ "node_modules/oniguruma-to-es": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz",
+ "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==",
+ "license": "MIT",
+ "dependencies": {
+ "oniguruma-parser": "^0.12.2",
+ "regex": "^6.1.0",
+ "regex-recursion": "^6.0.2"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz",
+ "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==",
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-queue": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.1.tgz",
+ "integrity": "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==",
+ "license": "MIT",
+ "dependencies": {
+ "eventemitter3": "^5.0.1",
+ "p-timeout": "^6.1.2"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-timeout": {
+ "version": "6.1.4",
+ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz",
+ "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/package-manager-detector": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz",
+ "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==",
+ "license": "MIT"
+ },
+ "node_modules/pagefind": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.5.2.tgz",
+ "integrity": "sha512-XTUaK0hXMCu2jszWE584JGQT7y284TmMV9l/HX3rnG5uo3rHI/uHU56XTyyyPFjeWEBxECbAi0CaFDJOONtG0Q==",
+ "license": "MIT",
+ "bin": {
+ "pagefind": "lib/runner/bin.cjs"
+ },
+ "optionalDependencies": {
+ "@pagefind/darwin-arm64": "1.5.2",
+ "@pagefind/darwin-x64": "1.5.2",
+ "@pagefind/freebsd-x64": "1.5.2",
+ "@pagefind/linux-arm64": "1.5.2",
+ "@pagefind/linux-x64": "1.5.2",
+ "@pagefind/windows-arm64": "1.5.2",
+ "@pagefind/windows-x64": "1.5.2"
+ }
+ },
+ "node_modules/parse-entities": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
+ "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "character-entities-legacy": "^3.0.0",
+ "character-reference-invalid": "^2.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0",
+ "is-hexadecimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/parse-entities/node_modules/@types/unist": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+ "license": "MIT"
+ },
+ "node_modules/parse-latin": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz",
+ "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/nlcst": "^2.0.0",
+ "@types/unist": "^3.0.0",
+ "nlcst-to-string": "^4.0.0",
+ "unist-util-modify-children": "^4.0.0",
+ "unist-util-visit-children": "^3.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/parse5": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
+ "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^6.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/path-data-parser": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz",
+ "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==",
+ "license": "MIT"
+ },
+ "node_modules/piccolore": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz",
+ "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==",
+ "license": "ISC"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/points-on-curve": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz",
+ "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==",
+ "license": "MIT"
+ },
+ "node_modules/points-on-path": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz",
+ "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==",
+ "license": "MIT",
+ "dependencies": {
+ "path-data-parser": "0.1.0",
+ "points-on-curve": "0.2.0"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.15",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
+ "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "nanoid": "^3.3.12",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-nested": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
+ "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.1.1"
+ },
+ "engines": {
+ "node": ">=12.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.14"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/prismjs": {
+ "version": "1.30.0",
+ "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
+ "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/prompts": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
+ "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "kleur": "^3.0.3",
+ "sisteransi": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/property-information": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz",
+ "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/radix3": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz",
+ "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==",
+ "license": "MIT"
+ },
+ "node_modules/readdirp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
+ "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20.19.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/recma-build-jsx": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz",
+ "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-util-build-jsx": "^3.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/recma-jsx": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz",
+ "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==",
+ "license": "MIT",
+ "dependencies": {
+ "acorn-jsx": "^5.0.0",
+ "estree-util-to-js": "^2.0.0",
+ "recma-parse": "^1.0.0",
+ "recma-stringify": "^1.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ },
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/recma-parse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz",
+ "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "esast-util-from-js": "^2.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/recma-stringify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz",
+ "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-util-to-js": "^2.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz",
+ "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==",
+ "license": "MIT",
+ "dependencies": {
+ "regex-utilities": "^2.3.0"
+ }
+ },
+ "node_modules/regex-recursion": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz",
+ "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==",
+ "license": "MIT",
+ "dependencies": {
+ "regex-utilities": "^2.3.0"
+ }
+ },
+ "node_modules/regex-utilities": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz",
+ "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==",
+ "license": "MIT"
+ },
+ "node_modules/rehype": {
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz",
+ "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "rehype-parse": "^9.0.0",
+ "rehype-stringify": "^10.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-expressive-code": {
+ "version": "0.41.7",
+ "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.41.7.tgz",
+ "integrity": "sha512-25f8ZMSF1d9CMscX7Cft0TSQIqdwjce2gDOvQ+d/w0FovsMwrSt3ODP4P3Z7wO1jsIJ4eYyaDRnIR/27bd/EMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "expressive-code": "^0.41.7"
+ }
+ },
+ "node_modules/rehype-format": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/rehype-format/-/rehype-format-5.0.1.tgz",
+ "integrity": "sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-format": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-parse": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz",
+ "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-from-html": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-raw": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz",
+ "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-raw": "^9.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-recma": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz",
+ "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "hast-util-to-estree": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-stringify": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz",
+ "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-to-html": "^9.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-directive": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.1.tgz",
+ "integrity": "sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-directive": "^3.0.0",
+ "micromark-extension-directive": "^3.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-gfm": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
+ "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-gfm": "^3.0.0",
+ "micromark-extension-gfm": "^3.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-stringify": "^11.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-mdx": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz",
+ "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==",
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-mdx": "^3.0.0",
+ "micromark-extension-mdxjs": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-parse": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
+ "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-rehype": {
+ "version": "11.1.2",
+ "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz",
+ "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-smartypants": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.2.tgz",
+ "integrity": "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==",
+ "license": "MIT",
+ "dependencies": {
+ "retext": "^9.0.0",
+ "retext-smartypants": "^6.0.0",
+ "unified": "^11.0.4",
+ "unist-util-visit": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/remark-stringify": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
+ "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/retext": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz",
+ "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/nlcst": "^2.0.0",
+ "retext-latin": "^4.0.0",
+ "retext-stringify": "^4.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/retext-latin": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz",
+ "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/nlcst": "^2.0.0",
+ "parse-latin": "^7.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/retext-smartypants": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz",
+ "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/nlcst": "^2.0.0",
+ "nlcst-to-string": "^4.0.0",
+ "unist-util-visit": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/retext-stringify": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz",
+ "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/nlcst": "^2.0.0",
+ "nlcst-to-string": "^4.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/robust-predicates": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz",
+ "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==",
+ "license": "Unlicense"
+ },
+ "node_modules/rollup": {
+ "version": "4.61.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz",
+ "integrity": "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.9"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.61.1",
+ "@rollup/rollup-android-arm64": "4.61.1",
+ "@rollup/rollup-darwin-arm64": "4.61.1",
+ "@rollup/rollup-darwin-x64": "4.61.1",
+ "@rollup/rollup-freebsd-arm64": "4.61.1",
+ "@rollup/rollup-freebsd-x64": "4.61.1",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.61.1",
+ "@rollup/rollup-linux-arm-musleabihf": "4.61.1",
+ "@rollup/rollup-linux-arm64-gnu": "4.61.1",
+ "@rollup/rollup-linux-arm64-musl": "4.61.1",
+ "@rollup/rollup-linux-loong64-gnu": "4.61.1",
+ "@rollup/rollup-linux-loong64-musl": "4.61.1",
+ "@rollup/rollup-linux-ppc64-gnu": "4.61.1",
+ "@rollup/rollup-linux-ppc64-musl": "4.61.1",
+ "@rollup/rollup-linux-riscv64-gnu": "4.61.1",
+ "@rollup/rollup-linux-riscv64-musl": "4.61.1",
+ "@rollup/rollup-linux-s390x-gnu": "4.61.1",
+ "@rollup/rollup-linux-x64-gnu": "4.61.1",
+ "@rollup/rollup-linux-x64-musl": "4.61.1",
+ "@rollup/rollup-openbsd-x64": "4.61.1",
+ "@rollup/rollup-openharmony-arm64": "4.61.1",
+ "@rollup/rollup-win32-arm64-msvc": "4.61.1",
+ "@rollup/rollup-win32-ia32-msvc": "4.61.1",
+ "@rollup/rollup-win32-x64-gnu": "4.61.1",
+ "@rollup/rollup-win32-x64-msvc": "4.61.1",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/roughjs": {
+ "version": "4.6.6",
+ "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz",
+ "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==",
+ "license": "MIT",
+ "dependencies": {
+ "hachure-fill": "^0.5.2",
+ "path-data-parser": "^0.1.0",
+ "points-on-curve": "^0.2.0",
+ "points-on-path": "^0.2.1"
+ }
+ },
+ "node_modules/rw": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+ "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/sax": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
+ "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=11.0.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.8.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
+ "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
+ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@img/colour": "^1.0.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.7.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.34.5",
+ "@img/sharp-darwin-x64": "0.34.5",
+ "@img/sharp-libvips-darwin-arm64": "1.2.4",
+ "@img/sharp-libvips-darwin-x64": "1.2.4",
+ "@img/sharp-libvips-linux-arm": "1.2.4",
+ "@img/sharp-libvips-linux-arm64": "1.2.4",
+ "@img/sharp-libvips-linux-ppc64": "1.2.4",
+ "@img/sharp-libvips-linux-riscv64": "1.2.4",
+ "@img/sharp-libvips-linux-s390x": "1.2.4",
+ "@img/sharp-libvips-linux-x64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4",
+ "@img/sharp-linux-arm": "0.34.5",
+ "@img/sharp-linux-arm64": "0.34.5",
+ "@img/sharp-linux-ppc64": "0.34.5",
+ "@img/sharp-linux-riscv64": "0.34.5",
+ "@img/sharp-linux-s390x": "0.34.5",
+ "@img/sharp-linux-x64": "0.34.5",
+ "@img/sharp-linuxmusl-arm64": "0.34.5",
+ "@img/sharp-linuxmusl-x64": "0.34.5",
+ "@img/sharp-wasm32": "0.34.5",
+ "@img/sharp-win32-arm64": "0.34.5",
+ "@img/sharp-win32-ia32": "0.34.5",
+ "@img/sharp-win32-x64": "0.34.5"
+ }
+ },
+ "node_modules/shiki": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.23.0.tgz",
+ "integrity": "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/core": "3.23.0",
+ "@shikijs/engine-javascript": "3.23.0",
+ "@shikijs/engine-oniguruma": "3.23.0",
+ "@shikijs/langs": "3.23.0",
+ "@shikijs/themes": "3.23.0",
+ "@shikijs/types": "3.23.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4"
+ }
+ },
+ "node_modules/sisteransi": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+ "license": "MIT"
+ },
+ "node_modules/sitemap": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.1.tgz",
+ "integrity": "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "^24.9.2",
+ "@types/sax": "^1.2.1",
+ "arg": "^5.0.0",
+ "sax": "^1.4.1"
+ },
+ "bin": {
+ "sitemap": "dist/esm/cli.js"
+ },
+ "engines": {
+ "node": ">=20.19.5",
+ "npm": ">=10.8.2"
+ }
+ },
+ "node_modules/smol-toml": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz",
+ "integrity": "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/cyyynthia"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.7.6",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
+ "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/space-separated-tokens": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
+ "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/stream-replace-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz",
+ "integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==",
+ "license": "MIT"
+ },
+ "node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/stringify-entities": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
+ "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities-html4": "^2.0.0",
+ "character-entities-legacy": "^3.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/style-to-js": {
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz",
+ "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "style-to-object": "1.0.14"
+ }
+ },
+ "node_modules/style-to-object": {
+ "version": "1.0.14",
+ "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz",
+ "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==",
+ "license": "MIT",
+ "dependencies": {
+ "inline-style-parser": "0.2.7"
+ }
+ },
+ "node_modules/stylis": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz",
+ "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==",
+ "license": "MIT"
+ },
+ "node_modules/svgo": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz",
+ "integrity": "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==",
+ "license": "MIT",
+ "dependencies": {
+ "commander": "^11.1.0",
+ "css-select": "^5.1.0",
+ "css-tree": "^3.0.1",
+ "css-what": "^6.1.0",
+ "csso": "^5.0.5",
+ "picocolors": "^1.1.1",
+ "sax": "^1.5.0"
+ },
+ "bin": {
+ "svgo": "bin/svgo.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/svgo"
+ }
+ },
+ "node_modules/svgo/node_modules/commander": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
+ "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/tiny-inflate": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
+ "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz",
+ "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/trim-lines": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
+ "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/trough": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
+ "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/ts-dedent": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz",
+ "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.10"
+ }
+ },
+ "node_modules/tsconfck": {
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz",
+ "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==",
+ "license": "MIT",
+ "bin": {
+ "tsconfck": "bin/tsconfck.js"
+ },
+ "engines": {
+ "node": "^18 || >=20"
+ },
+ "peerDependencies": {
+ "typescript": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD",
+ "optional": true
+ },
+ "node_modules/type-fest": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+ "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/ufo": {
+ "version": "1.6.4",
+ "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz",
+ "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==",
+ "license": "MIT"
+ },
+ "node_modules/ultrahtml": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.6.0.tgz",
+ "integrity": "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==",
+ "license": "MIT"
+ },
+ "node_modules/uncrypto": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz",
+ "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==",
+ "license": "MIT"
+ },
+ "node_modules/undici-types": {
+ "version": "7.18.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
+ "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
+ "license": "MIT"
+ },
+ "node_modules/unified": {
+ "version": "11.0.5",
+ "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
+ "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "bail": "^2.0.0",
+ "devlop": "^1.0.0",
+ "extend": "^3.0.0",
+ "is-plain-obj": "^4.0.0",
+ "trough": "^2.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unifont": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.4.tgz",
+ "integrity": "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==",
+ "license": "MIT",
+ "dependencies": {
+ "css-tree": "^3.1.0",
+ "ofetch": "^1.5.1",
+ "ohash": "^2.0.11"
+ }
+ },
+ "node_modules/unist-util-find-after": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz",
+ "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-is": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz",
+ "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-modify-children": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz",
+ "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "array-iterate": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-position": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
+ "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-position-from-estree": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz",
+ "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-remove-position": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz",
+ "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-visit": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-stringify-position": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz",
+ "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-children": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz",
+ "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-parents": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz",
+ "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unstorage": {
+ "version": "1.17.5",
+ "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz",
+ "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==",
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "^3.1.3",
+ "chokidar": "^5.0.0",
+ "destr": "^2.0.5",
+ "h3": "^1.15.10",
+ "lru-cache": "^11.2.7",
+ "node-fetch-native": "^1.6.7",
+ "ofetch": "^1.5.1",
+ "ufo": "^1.6.3"
+ },
+ "peerDependencies": {
+ "@azure/app-configuration": "^1.8.0",
+ "@azure/cosmos": "^4.2.0",
+ "@azure/data-tables": "^13.3.0",
+ "@azure/identity": "^4.6.0",
+ "@azure/keyvault-secrets": "^4.9.0",
+ "@azure/storage-blob": "^12.26.0",
+ "@capacitor/preferences": "^6 || ^7 || ^8",
+ "@deno/kv": ">=0.9.0",
+ "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0",
+ "@planetscale/database": "^1.19.0",
+ "@upstash/redis": "^1.34.3",
+ "@vercel/blob": ">=0.27.1",
+ "@vercel/functions": "^2.2.12 || ^3.0.0",
+ "@vercel/kv": "^1 || ^2 || ^3",
+ "aws4fetch": "^1.0.20",
+ "db0": ">=0.2.1",
+ "idb-keyval": "^6.2.1",
+ "ioredis": "^5.4.2",
+ "uploadthing": "^7.4.4"
+ },
+ "peerDependenciesMeta": {
+ "@azure/app-configuration": {
+ "optional": true
+ },
+ "@azure/cosmos": {
+ "optional": true
+ },
+ "@azure/data-tables": {
+ "optional": true
+ },
+ "@azure/identity": {
+ "optional": true
+ },
+ "@azure/keyvault-secrets": {
+ "optional": true
+ },
+ "@azure/storage-blob": {
+ "optional": true
+ },
+ "@capacitor/preferences": {
+ "optional": true
+ },
+ "@deno/kv": {
+ "optional": true
+ },
+ "@netlify/blobs": {
+ "optional": true
+ },
+ "@planetscale/database": {
+ "optional": true
+ },
+ "@upstash/redis": {
+ "optional": true
+ },
+ "@vercel/blob": {
+ "optional": true
+ },
+ "@vercel/functions": {
+ "optional": true
+ },
+ "@vercel/kv": {
+ "optional": true
+ },
+ "aws4fetch": {
+ "optional": true
+ },
+ "db0": {
+ "optional": true
+ },
+ "idb-keyval": {
+ "optional": true
+ },
+ "ioredis": {
+ "optional": true
+ },
+ "uploadthing": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "license": "MIT"
+ },
+ "node_modules/uuid": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz",
+ "integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist-node/bin/uuid"
+ }
+ },
+ "node_modules/vfile": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
+ "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-location": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz",
+ "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-message": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
+ "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vite": {
+ "version": "6.4.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz",
+ "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==",
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
+ "node_modules/vitefu": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz",
+ "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==",
+ "license": "MIT",
+ "workspaces": [
+ "tests/deps/*",
+ "tests/projects/*",
+ "tests/projects/workspace/packages/*"
+ ],
+ "peerDependencies": {
+ "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/web-namespaces": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
+ "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/which-pm-runs": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz",
+ "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/widest-line": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz",
+ "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==",
+ "license": "MIT",
+ "dependencies": {
+ "string-width": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/xxhash-wasm": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz",
+ "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==",
+ "license": "MIT"
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz",
+ "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/yocto-spinner": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/yocto-spinner/-/yocto-spinner-0.2.3.tgz",
+ "integrity": "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==",
+ "license": "MIT",
+ "dependencies": {
+ "yoctocolors": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=18.19"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/yoctocolors": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz",
+ "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
+ "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-to-json-schema": {
+ "version": "3.25.2",
+ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz",
+ "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==",
+ "license": "ISC",
+ "peerDependencies": {
+ "zod": "^3.25.28 || ^4"
+ }
+ },
+ "node_modules/zwitch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..8dfd03a8
--- /dev/null
+++ b/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "codeanalyzer-java-docs",
+ "type": "module",
+ "version": "0.0.1",
+ "scripts": {
+ "dev": "astro dev",
+ "start": "astro dev",
+ "build": "astro build",
+ "preview": "astro preview",
+ "astro": "astro"
+ },
+ "dependencies": {
+ "@astrojs/starlight": "^0.37.6",
+ "@expressive-code/plugin-collapsible-sections": "^0.42.0",
+ "@expressive-code/plugin-line-numbers": "^0.42.0",
+ "astro": "^5.18.0",
+ "astro-mermaid": "^2.0.2",
+ "mermaid": "^11.15.0",
+ "sharp": "^0.34.2"
+ }
+}
diff --git a/docs/assets/logo.png b/public/assets/images/codeanalyzer.png
similarity index 100%
rename from docs/assets/logo.png
rename to public/assets/images/codeanalyzer.png
diff --git a/public/favicon.png b/public/favicon.png
new file mode 100644
index 00000000..4c32c517
Binary files /dev/null and b/public/favicon.png differ
diff --git a/settings.gradle b/settings.gradle
deleted file mode 100644
index a6b36d6a..00000000
--- a/settings.gradle
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-Copyright IBM Corporation 2023, 2024
-
-Licensed under the Apache Public License 2.0, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-
-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.
-*/
-pluginManagement {
- plugins {
- id 'org.jetbrains.kotlin.jvm' version '2.1.10'
- }
-}
-plugins {
- id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
-}
diff --git a/src/assets/logo.png b/src/assets/logo.png
new file mode 100644
index 00000000..93cab6b3
Binary files /dev/null and b/src/assets/logo.png differ
diff --git a/src/components/Neo4jPropertyGraph.astro b/src/components/Neo4jPropertyGraph.astro
new file mode 100644
index 00000000..7252cdbf
--- /dev/null
+++ b/src/components/Neo4jPropertyGraph.astro
@@ -0,0 +1,121 @@
+---
+// AUTO-GENERATED property-graph hero for java. Encodes the real Neo4j schema
+// (node labels, typed relationships, key properties). Theme-aware via Starlight
+// CSS custom properties; renders as static SVG (no client JS).
+---
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+J_HAS_UNIT
+
+J_DECLARES_TYPE
+
+J_HAS_FIELD
+
+J_HAS_CALLABLE
+
+J_HAS_PARAMETER
+
+J_HAS_CALLSITE
+
+J_RESOLVES_TO
+
+J_CALLS
+
+
+:JApplication
+name
+schema_version
+
+
+:JCompilationUnit
+file_path
+package_name
+
+
+:JType
+:JSymbol
+fqn
+is_interface
+
+
+:JField
+name
+type
+
+
+
+:JCallable
+:JSymbol
+★ :JEntrypoint
+signature
+cyclomatic_complexity
+
+
+:JParameter
+name
+type
+
+
+:JCallSite
+method_name
+receiver_type
+
+
+:JCallable
+name
+is_entrypoint
+
+
+ The analysis is a Neo4j property graph : every node carries a
+ label (its color) and properties ; every relationship carries a
+ type . The dashed ring marks an :JEntrypoint;
+ the J_CALLS edge is the resolved call graph.
+
+
+
+
diff --git a/src/content.config.ts b/src/content.config.ts
new file mode 100644
index 00000000..d9ee8c9d
--- /dev/null
+++ b/src/content.config.ts
@@ -0,0 +1,7 @@
+import { defineCollection } from 'astro:content';
+import { docsLoader } from '@astrojs/starlight/loaders';
+import { docsSchema } from '@astrojs/starlight/schema';
+
+export const collections = {
+ docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
+};
diff --git a/src/content/docs/contributing.mdx b/src/content/docs/contributing.mdx
new file mode 100644
index 00000000..2374a763
--- /dev/null
+++ b/src/content/docs/contributing.mdx
@@ -0,0 +1,110 @@
+---
+title: Contributing
+description: How to build, test, and extend codeanalyzer-java — adding framework finders, evolving the schema, and keeping native-image config current.
+---
+
+import { Steps, Aside, LinkCard, CardGrid } from "@astrojs/starlight/components";
+
+codeanalyzer-java is open source under the Apache 2.0 license. Contributions — bug fixes, new framework finders, schema enrichments — are welcome.
+
+## Build and test locally
+
+
+
+1. **Install a JDK (11+)** and clone:
+
+ ```bash
+ sdk install java 17.0.10-sem
+ git clone https://github.com/codellm-devkit/codeanalyzer-java
+ cd codeanalyzer-java
+ ```
+
+2. **Build the fat JAR:**
+
+ ```bash
+ ./gradlew fatJar
+ # build/libs/codeanalyzer-2.3.7.jar
+ ```
+
+3. **Run the test suite:**
+
+ ```bash
+ ./gradlew test
+ ```
+
+4. **Try it against a sample project:**
+
+ ```bash
+ java -jar build/libs/codeanalyzer-2.3.7.jar \
+ -i src/test/resources/test-applications/ \
+ -a 2 -v
+ ```
+
+
+
+
+The build uses Spotless. Run `./gradlew spotlessApply` before committing to fix formatting, or `./gradlew spotlessCheck` to verify.
+
+
+## Where things live
+
+The codebase is organized under `com.ibm.cldk` (see [Architecture](/codeanalyzer-java/guides/architecture/) for the full map):
+
+- **`CodeAnalyzer.java`** — the CLI orchestrator; add or change flags here.
+- **`SymbolTable.java`** — Javaparser symbol extraction.
+- **`SystemDependencyGraph.java`** — WALA call-graph construction.
+- **`entities/`** — the output data model. Changing these changes the [JSON schema](/codeanalyzer-java/schema/).
+- **`javaee/`** — framework finders, dispatched by `EntrypointsFinderFactory` and `CRUDFinderFactory`.
+- **`utils/BuildProject.java`** — Maven/Gradle build and dependency download.
+
+## Adding a framework finder
+
+Entry-point and CRUD detection are pluggable per framework. To add support for a new framework:
+
+
+
+1. **Pick the dimension** — entry points, CRUD, or both — and find the matching factory (`EntrypointsFinderFactory` / `CRUDFinderFactory`).
+
+2. **Add a finder** under `javaee//` implementing the finder interface, with the annotations / superclasses / interfaces / method conventions that identify the construct (mirror an existing finder like the Spring or JAX-RS ones).
+
+3. **Register it** in the factory so it's selected when that framework is present.
+
+4. **Set the schema flags** — entry-point finders mark `is_entrypoint` / `is_entrypoint_class`; CRUD finders populate `crud_operations` / `crud_queries`.
+
+5. **Add a test application** under `src/test/resources/test-applications/` and assert the expected output.
+
+
+
+See the existing [entry-point](/codeanalyzer-java/frameworks/entry-points/) and [CRUD](/codeanalyzer-java/frameworks/crud/) coverage for the patterns to follow. The Camel finder is a good example of a stub awaiting completion.
+
+## Evolving the schema
+
+The output JSON is a versioned contract that the [CLDK Python SDK](/codeanalyzer-java/integration/python-sdk/) and other consumers depend on.
+
+- Changing an `entities/` class changes the wire format. Field names serialize as `snake_case`.
+- For incompatible changes, bump the version (in `gradle.properties`) and, where feasible, add a guard for the old shape — as was done for the legacy string-import format.
+- Update the [schema docs](/codeanalyzer-java/schema/) alongside the code.
+
+## Keeping native-image config current
+
+If you add reflection-dependent code, regenerate the native-image config so the [native binary](/codeanalyzer-java/installing/#option-2-native-binary-graalvm) keeps working:
+
+```bash
+./gradlew fatJar
+java -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image-config \
+ -jar build/libs/codeanalyzer-2.3.7.jar -i -a 2 -v
+./gradlew nativeCompile
+```
+
+## Opening a PR
+
+- Branch from `main`, keep changes focused, and include a test.
+- Run `./gradlew spotlessApply test` before pushing.
+- Reference any related issue in the PR description.
+
+
+
+
+
+
+Found a limitation? Open an [issue](https://github.com/codellm-devkit/codeanalyzer-java/issues) with details.
diff --git a/src/content/docs/frameworks/crud.mdx b/src/content/docs/frameworks/crud.mdx
new file mode 100644
index 00000000..2e76a22e
--- /dev/null
+++ b/src/content/docs/frameworks/crud.mdx
@@ -0,0 +1,120 @@
+---
+title: CRUD detection
+description: How codeanalyzer-java detects database operations — JPA persistence calls and queries classified as CREATE / READ / UPDATE / DELETE — and where they appear in the schema.
+---
+
+import { Aside } from "@astrojs/starlight/components";
+
+codeanalyzer-java surfaces data-access patterns by detecting **CRUD operations** in method bodies and attaching them to the relevant [callable](/codeanalyzer-java/schema/symbol-table/#callable-jcallable). This lets you audit where an application reads from and writes to persistent storage without reading every method by hand.
+
+Detection is dispatched per framework by a `CRUDFinderFactory`. Today **JPA / Jakarta Persistence** detection is fully implemented; **Spring Data** and **JDBC** finders exist but are currently stubs.
+
+## Where it appears
+
+Each callable carries two arrays:
+
+```typescript
+{
+ crud_operations: JCRUDOperation[] // persistence operations (persist/find/merge/remove, ...)
+ crud_queries: JCRUDQuery[] // query definitions (createQuery / createNamedQuery)
+}
+```
+
+## CRUD operations (`JCRUDOperation`)
+
+```typescript
+{
+ line_number: number
+ operation_type: "CREATE" | "READ" | "UPDATE" | "DELETE"
+ target_table: string // reserved — not yet populated
+ involved_columns: string[] // reserved — not yet populated
+ condition: string // reserved — not yet populated
+ joined_tables: string[] // reserved — not yet populated
+}
+```
+
+### JPA operation mapping
+
+For JPA, calls on the `EntityManager` and on query objects map to operation types:
+
+| Operation type | Detected from |
+|----------------|---------------|
+| **CREATE** | `EntityManager.persist(...)` |
+| **READ** | `EntityManager.find(...)`; query execution `getResultList()`, `getSingleResult()`, `getFirstResult()`, `getMaxResults()` |
+| **UPDATE** | `EntityManager.merge(...)`; query `executeUpdate()` |
+| **DELETE** | `EntityManager.remove(...)` |
+
+
+`executeUpdate()` is classified as **UPDATE**. Without dataflow analysis over the query string, an UPDATE and a DELETE issued through the same API call can't be distinguished, so it is reported as UPDATE.
+
+
+## CRUD queries (`JCRUDQuery`)
+
+Query *definitions* (as opposed to executions) are captured separately:
+
+```typescript
+{
+ line_number: number
+ query_arguments: string[] // the query string and any parameters
+ query_type: "READ" | "WRITE" | "NAMED"
+}
+```
+
+These come from `EntityManager.createQuery(String)` and `EntityManager.createNamedQuery(String)`. The `query_type` is inferred from the query text:
+
+| `query_type` | Inferred when |
+|--------------|---------------|
+| **READ** | the query string begins with `select` |
+| **WRITE** | the query string begins with `update`, `delete`, or `insert` |
+| **NAMED** | the query was created via `createNamedQuery(...)` |
+
+## Coverage and limits
+
+- **JPA / Jakarta Persistence** — implemented as described above.
+- **Spring Data** — finder present but stubbed; repository-derived queries are not yet classified.
+- **JDBC** — finder present but stubbed; raw `Statement` / `PreparedStatement` calls are not yet classified.
+- The `target_table`, `involved_columns`, `condition`, and `joined_tables` fields on `JCRUDOperation` are reserved for future enrichment and are not populated yet.
+
+
+CRUD data is part of the symbol table, so it's available at [analysis level 1](/codeanalyzer-java/guides/analysis-levels/) — you don't need a call graph to enumerate persistence operations across the app.
+
+
+## In the Neo4j graph
+
+When you project the analysis with `--emit neo4j`, CRUD detection is not flattened into the callable — it becomes **first-class graph structure**. Each detected operation is a `:JCrudOperation` node and each query a `:JCrudQuery` node, hung off its owning `:JCallable` or `:JCallSite`:
+
+```cypher
+(:JCallable | :JCallSite)-[:J_HAS_CRUD_OPERATION]->(:JCrudOperation)
+(:JCallable | :JCallSite)-[:J_HAS_CRUD_QUERY]->(:JCrudQuery)
+```
+
+That turns "where does this application write to persistent storage?" into a Cypher traversal across the whole graph — and, once many applications share one database, across the entire portfolio. For example, every method that issues a write:
+
+```cypher
+MATCH (c:JCallable)-[:J_HAS_CRUD_OPERATION]->(op:JCrudOperation)
+WHERE op.operation_type IN ['CREATE', 'UPDATE', 'DELETE']
+RETURN c.signature, op.operation_type
+```
+
+`JCrudOperation` exposes `operation_type` along with the `target_table`, `involved_columns`, `condition`, and `joined_tables` properties — keep in mind those last four are reserved (see above), so today you filter on `operation_type`. See the [Neo4j graph-schema reference](/codeanalyzer-java/schema/neo4j-graph/) for the full node and relationship inventory.
+
+## Using it downstream
+
+```python
+from cldk import CLDK
+from cldk.analysis import AnalysisLevel
+
+analysis = CLDK.java(
+ project_path="my-app",
+ analysis_level=AnalysisLevel.symbol_table,
+)
+
+for cls in analysis.get_classes():
+ for sig, m in analysis.get_methods_in_class(cls).items():
+ for op in m.crud_operations:
+ print(f"{op.operation_type} at {cls}:{op.line_number}")
+```
+
+The same query works unchanged against a graph that was produced out of band: pass a `Neo4jConnectionConfig` instead of `project_path` and the read-only Neo4j backend reconstructs the identical models — no JDK, native binary, or project source required. Set `application_name` to the `--app-name` the graph was loaded with. See the [Neo4j graph output guide](/codeanalyzer-java/guides/neo4j-output/) for the full read-back flow.
+
+Combine with [entry-point](/codeanalyzer-java/frameworks/entry-points/) and call-graph data to answer questions like "which externally-reachable methods perform writes?"
diff --git a/src/content/docs/frameworks/entry-points.mdx b/src/content/docs/frameworks/entry-points.mdx
new file mode 100644
index 00000000..9413ddc5
--- /dev/null
+++ b/src/content/docs/frameworks/entry-points.mdx
@@ -0,0 +1,114 @@
+---
+title: Entry points
+description: How codeanalyzer-java identifies program entry points — main methods plus Spring, JAX-RS, Struts, and Jakarta/Servlet endpoints — and how that surfaces in the schema.
+---
+
+import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
+
+An **entry point** is a method the runtime (or a framework) can invoke without an in-program caller: a `main`, a REST handler, a servlet, a scheduled job. They matter because they anchor the WALA [call graph](/codeanalyzer-java/schema/call-graph/) and seed reachability — a taint path has to start *somewhere*.
+
+codeanalyzer-java detects entry points through an `EntrypointsFinderFactory` that dispatches to per-framework finders. Detected entry points surface in the schema as:
+
+- `type.is_entrypoint_class` — `true` on the [type](/codeanalyzer-java/schema/symbol-table/#type-jtype) when it's a recognized entry-point class (including any class with a `main(String[])`).
+- `callable.is_entrypoint` — `true` on the [callable](/codeanalyzer-java/schema/symbol-table/#callable-jcallable) for the specific entry-point method.
+
+## Supported frameworks
+
+
+
+**Class-level annotations:** `@RestController`, `@Controller`, `@HandlerInterceptor`, `@SpringBootApplication`, `@Configuration`, `@Component`, `@Service`, `@Repository`.
+
+**Interfaces:** classes implementing `CommandLineRunner` or `ApplicationRunner`.
+
+**Method-level annotations:** `@GetMapping`, `@PostMapping`, `@PutMapping`, `@DeleteMapping`, `@PatchMapping`, `@RequestMapping`, `@EventListener`, `@Scheduled`, `@KafkaListener`, `@RabbitListener`, `@JmsListener`, `@PreAuthorize`, `@PostAuthorize`, `@PostConstruct`, `@PreDestroy`, plus AOP advice (`@Around`, `@Before`, `@After`) and batch scopes (`@JobScope`, `@StepScope`).
+
+
+**HTTP-method annotations** (at class or method level) mark REST resource methods: `@GET`, `@POST`, `@PUT`, `@DELETE`, `@HEAD`.
+
+
+**Class-level annotations:** `@Action`, `@Namespace`, `@InterceptorRef`.
+
+**Superclass / interface:** classes extending `ActionSupport` or implementing `Interceptor`.
+
+**Method-level annotations:** `@Action`, `@Actions`, `@ValidationMethod`, `@InputConfig`, `@BeforeResult`, `@Before`, `@After`, `@Result`, `@Results`.
+
+**Convention:** a method named `execute()`.
+
+
+**Class-level annotations:** `@WebServlet`, `@WebFilter`, `@WebListener`, `@ServerEndpoint`, `@MessageDriven`, `@WebService`.
+
+**Superclasses:** classes extending `HttpServlet` or `GenericServlet`.
+
+**Interfaces:** classes implementing `ServletContextListener`, `HttpSessionListener`, `ServletRequestListener`, or `MessageListener`.
+
+**Method signature:** methods taking `HttpServletRequest` or `HttpServletResponse` parameters.
+
+
+
+## Plain `main` methods
+
+Independent of any framework, a class declaring `public static void main(String[] args)` is an entry point. Its type gets `is_entrypoint_class: true` and the `main` callable gets `is_entrypoint: true`. This is the baseline anchor for ordinary applications.
+
+
+A Camel finder exists (`RouteBuilder` subclasses, `Processor`/`Producer`/`Consumer` implementers, `@Component`) but is currently a stub marked not-implemented — Camel routes are not yet detected as entry points.
+
+
+## Why entry points matter for the call graph
+
+WALA builds the call graph by traversing outward from entry points. If a project has no `main` and none of the framework patterns above match, WALA may have nothing to anchor on and the `call_graph` can come back empty. When that happens, confirm the project actually has a recognized entry point — see [Troubleshooting](/codeanalyzer-java/troubleshooting/).
+
+## Using entry points downstream
+
+Once analyzed, you can filter on the flags to drive reachability. Via the Python SDK, against an in-process analysis:
+
+```python
+analysis = CLDK(language="java").analysis(
+ project_path="my-web-app",
+ analysis_level=AnalysisLevel.call_graph,
+)
+
+# Every method flagged as an entry point
+entrypoints = [
+ (cls, sig)
+ for cls in analysis.get_classes()
+ for sig, m in analysis.get_methods_in_class(cls).items()
+ if m.is_entrypoint
+]
+```
+
+Seed a `networkx` reachability query from these to ask whether a sink is reachable from any externally-invocable method.
+
+## Entry points in the Neo4j projection
+
+When you project to a Neo4j property graph with [`--emit neo4j`](/codeanalyzer-java/guides/neo4j-output/), the `is_entrypoint` / `is_entrypoint_class` properties are still carried on the `:JCallable` and `:JType` nodes — but the projection *also* layers a marker label, `:JEntrypoint`, onto the owning callable (and entry-point class). That turns reachability seeding into a one-line `MATCH` instead of a property filter:
+
+```cypher
+// Every entry-point method in one application — the reachability seeds
+MATCH (a:JApplication {name: 'daytrader8'})-[:J_HAS_UNIT]->(:JCompilationUnit)
+ -[:J_DECLARES_TYPE]->(:JType)-[:J_HAS_CALLABLE]->(c:JCallable:JEntrypoint)
+RETURN c.signature
+```
+
+From there you traverse `J_CALLS` edges (present once you analyze at [level 2](/codeanalyzer-java/guides/analysis-levels/)) to ask the same reachability question entirely in Cypher, across every application in the database. Reading the seeds back through the SDK is identical to the in-process path above — point the facade at the graph and the same typed `JCallable` objects come back, with no JDK, native binary, or project source on the consumer:
+
+```python
+from cldk import CLDK
+from cldk.analysis import AnalysisLevel
+from cldk.analysis.commons.backend_config import Neo4jConnectionConfig
+
+analysis = CLDK.java(
+ analysis_level=AnalysisLevel.call_graph,
+ backend=Neo4jConnectionConfig(
+ uri="bolt://localhost:7687",
+ username="neo4j",
+ password="neo4j", # or set NEO4J_PASSWORD
+ application_name="daytrader8", # must match the --app-name the graph was loaded with
+ ),
+)
+
+entrypoints = analysis.get_entry_point_methods()
+```
+
+
+`:JEntrypoint` and the `is_entrypoint` / `is_entrypoint_class` properties are part of the versioned graph contract. For the full node-label and relationship inventory, see the [Neo4j graph schema](/codeanalyzer-java/schema/neo4j-graph/).
+
diff --git a/src/content/docs/guides/analysis-levels.mdx b/src/content/docs/guides/analysis-levels.mdx
new file mode 100644
index 00000000..17e4a245
--- /dev/null
+++ b/src/content/docs/guides/analysis-levels.mdx
@@ -0,0 +1,67 @@
+---
+title: Analysis levels
+description: codeanalyzer-java has two analysis levels — level 1 builds the symbol table, level 2 adds the WALA call graph. What each computes, what it costs, and when to use which.
+---
+
+import { Aside, CardGrid, LinkCard } from "@astrojs/starlight/components";
+
+The `-a` / `--analysis-level` flag selects how much work codeanalyzer-java does. There are two levels.
+
+| Level | Produces | Builds the project? | Relative cost |
+|-------|----------|---------------------|---------------|
+| **1** (default) | `symbol_table` only | No (source parse only) | Fast |
+| **2** | `symbol_table` + `call_graph` | Yes, by default | Slower |
+
+## Level 1 — Symbol table
+
+```bash
+java -jar codeanalyzer-2.3.7.jar -i /path/to/project -a 1 -o ./output
+```
+
+Level 1 runs only the Javaparser pipeline. It parses every `.java` file and produces the [symbol table](/codeanalyzer-java/schema/symbol-table/): all types, their fields, methods and constructors, comments, and imports — with source locations, method bodies, and cyclomatic complexity.
+
+It does **not** build the project. Library dependencies are still downloaded for type resolution (so qualified names resolve), but no compilation of *your* code is required. This makes level 1 the fast path, suitable when you only need program structure — not who-calls-whom.
+
+
+Level 1 is also the only level that supports [incremental target-file analysis](/codeanalyzer-java/guides/incremental-analysis/). If you pass `-t` with level 2, the analyzer downgrades to level 1 and warns.
+
+
+## Level 2 — Symbol table + call graph
+
+```bash
+java -jar codeanalyzer-2.3.7.jar -i /path/to/project -a 2 -o ./output -v
+```
+
+Level 2 does everything level 1 does, then runs WALA to build an interprocedural [call graph](/codeanalyzer-java/schema/call-graph/). The result is a `call_graph` array of caller→callee edges added alongside the symbol table.
+
+Because WALA analyzes the *compiled* program, level 2 builds the project by default (auto-detecting Maven or Gradle). You can control this:
+
+- **`-b ""`** — supply a custom build command instead of auto-build.
+- **`--no-build`** — skip building entirely; use this when the project is already compiled.
+
+See [Build integration](/codeanalyzer-java/guides/build-integration/) for how the build is invoked.
+
+
+WALA needs entry points to anchor the graph. If a project has no `main(String[])` and no recognized framework entry points, the call graph can come back empty. See [Entry points](/codeanalyzer-java/frameworks/entry-points/) and [Troubleshooting](/codeanalyzer-java/troubleshooting/).
+
+
+## The level also shapes the Neo4j graph
+
+The analysis level governs the [Neo4j projection](/codeanalyzer-java/guides/neo4j-output/) the same way it governs `analysis.json`. Level 1 emits the lossless symbol-table subgraph — compilation units, types, callables, fields, and the rest — with **no `J_CALLS` edges**. Level 2 adds the call graph as `(:JCallable)-[:J_CALLS]->(:JCallable)` relationships on top of it.
+
+This carries through the `-t` downgrade: because passing `-t` with `-a 2` forces level 1, a targeted incremental Bolt push (`--emit neo4j -t ...`) replaces only the changed units' symbol-table subgraphs and **carries no call edges**. Refresh `J_CALLS` with a full level-2 run.
+
+## Choosing a level
+
+- **Use level 1** when you need the program's *structure*: listing classes and methods, reading method bodies, finding fields, extracting Javadoc, surveying imports, or doing incremental per-file updates.
+- **Use level 2** when you need *reachability* or *call relationships*: who calls a method, what a method transitively reaches, or seeding a taint/reachability query from an entry point.
+
+## Next steps
+
+
+
+
+
+
+
+
diff --git a/src/content/docs/guides/architecture.mdx b/src/content/docs/guides/architecture.mdx
new file mode 100644
index 00000000..0ff9ef55
--- /dev/null
+++ b/src/content/docs/guides/architecture.mdx
@@ -0,0 +1,148 @@
+---
+title: Architecture
+description: How codeanalyzer-java is built — the Javaparser symbol-extraction pipeline, WALA call-graph construction, the package layout, and the core dependencies.
+---
+
+import { Aside, FileTree } from "@astrojs/starlight/components";
+
+codeanalyzer-java combines two complementary static-analysis technologies behind a single CLI. The same intermediate representation (IR) — a symbol table plus an optional call graph — is emitted either as the canonical `analysis.json` or as a Neo4j property graph. This page describes how the pieces fit together.
+
+## The analysis pipeline
+
+```mermaid
+graph LR
+ A["Java source files"] --> B["Javaparser + Symbol Solver"]
+ B --> C["Symbol Table"]
+ C --> D["Type & Method extraction"]
+ D --> IR["In-memory IR (symbol table + call graph)"]
+
+ G["Built project + dependencies"] --> H["WALA ClassHierarchy"]
+ H --> I["Call graph construction"]
+ I --> J["call_graph edges"]
+ J --> IR
+
+ IR -->|"default (--emit json)"| E["Gson serialization"]
+ E --> F["analysis.json"]
+
+ IR -->|"--emit neo4j"| GP["GraphProjector .project()"]
+ GP --> GR["GraphRows (nodes + relationships)"]
+ GR -->|"no Bolt URI"| CW["CypherWriter"]
+ CW --> SNAP["graph.cypher snapshot"]
+ GR -->|"URI present (fat jar)"| BW["BoltWriter"]
+ BW --> N4J["Neo4j property graph (live, incremental)"]
+```
+
+There are two analysis tracks that converge into one IR, which then fans out to one of two emitters:
+
+1. **Symbol extraction (Javaparser).** Always runs. Parses each `.java` file into an AST, resolves types against downloaded library dependencies (or, for single-source mode, against the JDK only), and walks the AST to collect types, callables, fields, comments, and imports.
+
+2. **Call-graph construction (WALA).** Runs only at [analysis level 2](/codeanalyzer-java/guides/analysis-levels/). Builds a class hierarchy from the compiled project and computes an interprocedural call graph, emitted as caller→callee edges.
+
+By default both tracks are serialized by Gson into a single `analysis.json` whose field names use `lower_case_with_underscores`, with nulls preserved (`serializeNulls`) so consumers see a stable shape. With `--emit neo4j`, the *same* IR is instead projected into a Neo4j property graph — a **lossless** projection where every IR entity becomes a first-class node or relationship.
+
+### Stage by stage
+
+The CLI orchestrator (`CodeAnalyzer`) runs roughly this sequence:
+
+1. **Resolve inputs** — determine project root, output path, analysis level, emit target, and whether to build.
+2. **Download dependencies** — `BuildProject` invokes Maven/Gradle to fetch library JARs into a temporary `_library_dependencies` directory, used by the symbol solver for type resolution. See [Build integration](/codeanalyzer-java/guides/build-integration/).
+3. **Extract symbol table** — `SymbolTable` parses sources. There are three entry points: `extractAll` (whole project), `extract` (specific [target files](/codeanalyzer-java/guides/incremental-analysis/)), and `extractSingle` (a source string).
+4. **Construct call graph** (level 2 only) — `SystemDependencyGraph` runs WALA over the built project and produces a list of edges.
+5. **Clean up** — the temporary dependency directory is removed (unless `--no-clean-dependencies` is set).
+6. **Emit** — selected by `--emit` (default `json`):
+ - **`json`** — Gson serializes `{ symbol_table, call_graph?, version }` to `analysis.json`, or to stdout if no output directory was given.
+ - **`neo4j`** — projects the IR to a Neo4j property graph instead of writing `analysis.json` (see below). This is an *alternative*, not an addition: when `emit == neo4j` the analyzer returns without writing `analysis.json`.
+ - **`schema`** — prints the machine-readable schema contract (`schema.neo4j.json`) and returns before any project analysis. Useful for publishing the graph's versioned shape to consumers.
+
+
+Type resolution quality depends on dependencies being available. That's why level-2 analysis builds the project by default — WALA needs compiled classes, and the symbol solver benefits from the resolved classpath.
+
+
+### The Neo4j emit path
+
+When `--emit neo4j` is given, the orchestrator hands the in-memory IR to `GraphProjector.project()`, which turns it into `GraphRows` — a flat, batched set of node and relationship rows that mirrors the [graph schema](/codeanalyzer-java/schema/). At level 2 the projection also includes `J_CALLS` edges from the WALA call graph; at level 1 it emits the lossless symbol-table subgraph with no `J_CALLS`. Every emitted graph is anchored at a single `:JApplication` node (keyed on `--app-name`) and stamped with `schema_version` `1.0.0`.
+
+`GraphRows` is then handed to one of two writers, chosen purely by whether a Bolt URI resolved (the `--neo4j-uri` flag or the `NEO4J_URI` environment variable):
+
+- **No URI → `CypherWriter`.** Renders a self-contained, re-runnable `graph.cypher` snapshot: the constraints and indexes, a *scoped wipe* of this application's prior subgraph, then batched `UNWIND … MERGE` statements (batch size 500) for nodes and edges. The snapshot is **not** incremental — it expresses the full truth of one analysis run. Load it with `cypher-shell < graph.cypher`.
+
+- **URI present → `BoltWriter`.** Pushes incrementally over the official Neo4j Java driver: it ensures constraints/indexes, diffs each compilation unit's `content_hash` (a SHA-256 over the unit) against the live database, and replaces *only* changed units' subgraphs via idempotent `MERGE` upserts (batch size 1000). Shared `:JPackage`/`:JAnnotation` nodes are `MERGE`-only so they coexist across applications. On a full run it prunes units whose source file has vanished; on a [targeted run](/codeanalyzer-java/guides/incremental-analysis/) (`-t`) that orphan pruning is skipped.
+
+This is the producer side of a producer/consumer split: the analyzer runs out-of-band (a CI job or Kubernetes `CronJob`) and pushes app-scoped subgraphs into a shared cluster, while lightweight read-only consumers — agents, dashboards, and the [CLDK Python SDK](/codeanalyzer-java/integration/python-sdk/) — fan out reads from it. See the [Neo4j graph guide](/codeanalyzer-java/guides/neo4j-output/) for the full producer/consumer story.
+
+
+The Bolt push is wired through a reflective seam (`Neo4jEmitter.loadBoltSink` → `BoltSink`), so the Neo4j driver and Netty are **not** bundled into the GraalVM native image. In the prebuilt native binary, `--neo4j-uri` therefore degrades gracefully to writing a `graph.cypher` snapshot with a warning. The live, incremental Bolt push happens from the fat jar (`java -jar`).
+
+
+## Package structure
+
+The analyzer lives under the `com.ibm.cldk` package:
+
+
+- com.ibm.cldk
+ - CodeAnalyzer.java CLI entry point; orchestrates the pipeline
+ - SymbolTable.java Javaparser-based symbol extraction
+ - SystemDependencyGraph.java WALA-based call-graph construction
+ - entities/ Output data model (serialized to JSON)
+ - JavaCompilationUnit.java one .java file: types + imports + comments
+ - Type.java class / interface / enum / record
+ - Callable.java method or constructor
+ - Field.java member field
+ - Comment.java Javadoc / inline comment
+ - Import.java import declaration (path, static, wildcard)
+ - CallableVertex.java call-graph node
+ - CallEdge.java call-graph edge
+ - CallSite.java an individual call within a method body
+ - CRUDOperation.java a detected DB operation
+ - CRUDQuery.java a detected query definition
+ - ...
+ - neo4j/ Neo4j property-graph projection (`--emit neo4j`)
+ - Neo4jEmitter.java emit entry point; picks snapshot vs. live Bolt
+ - GraphProjector.java IR → graph nodes + relationships
+ - RowBuilder.java builds per-label/relationship row sets
+ - GraphRows.java the batched node + relationship payload
+ - CypherWriter.java renders the re-runnable graph.cypher snapshot
+ - BoltWriter.java incremental, content_hash-diffed live push
+ - BoltSink.java reflective seam over the Neo4j driver
+ - BoltConfig.java driver-free Bolt connection config
+ - SchemaCatalog.java builds schema.neo4j.json (`--emit schema`)
+ - Schema.java constraints + indexes DDL (labels, fulltext)
+ - javaee/ framework-specific finders
+ - EntrypointsFinderFactory.java selects entry-point detectors
+ - CRUDFinderFactory.java selects CRUD detectors
+ - spring/ Spring detectors
+ - struts/ Struts detectors
+ - jax/ JAX-RS detectors
+ - jakarta/ Servlet / JPA detectors
+ - camel/ Camel (stub)
+ - utils/
+ - BuildProject.java Maven/Gradle build + dependency download
+ - Log.java verbosity-aware logging
+
+
+## Core dependencies
+
+| Library | Version | Role |
+|---------|---------|------|
+| **WALA** | 1.6.7 | Class hierarchy, interprocedural call graph (`shrike`, `util`, `core`, `cast`, `cast.java`, `cast.java.ecj`) |
+| **Javaparser** | — | Source parsing and symbol/type resolution |
+| **Eclipse JDT** | 3.21.0 | Backing compiler/AST infrastructure used during analysis |
+| **Picocli** | 4.1.0 | Command-line interface |
+| **Gson** | 2.10.1 | JSON serialization of the output schema |
+| **Neo4j Java driver** | 4.4.12 | Live, incremental Bolt push for `--emit neo4j --neo4j-uri …` |
+| **JGraphT** | 1.5.2 | Graph data structures |
+| **Guava** | 33.0.0 | General utilities |
+| **Log4j** | 2.18.0 | Logging |
+
+The whole set is shaded into one fat JAR by `./gradlew fatJar`, so a single `java -jar` invocation has everything it needs — including the Neo4j driver for the live Bolt push.
+
+
+The Neo4j driver is loaded reflectively rather than referenced directly, so the GraalVM native image (`./gradlew nativeCompile`) prunes the driver and Netty entirely. That keeps the native binary small and Netty-metadata-free at the cost of the live Bolt push, which is why `--neo4j-uri` falls back to a `graph.cypher` snapshot there.
+
+
+## Where to go next
+
+- [Analysis levels](/codeanalyzer-java/guides/analysis-levels/) — what level 1 vs. level 2 actually compute.
+- [Build integration](/codeanalyzer-java/guides/build-integration/) — how dependency download and project builds work.
+- [Neo4j property graph](/codeanalyzer-java/guides/neo4j-output/) — the producer/consumer architecture, snapshot vs. live Bolt, and multi-tenant graph design.
+- [Output schema](/codeanalyzer-java/schema/) — the JSON and the Neo4j graph these stages produce.
diff --git a/src/content/docs/guides/build-integration.mdx b/src/content/docs/guides/build-integration.mdx
new file mode 100644
index 00000000..8ee167e3
--- /dev/null
+++ b/src/content/docs/guides/build-integration.mdx
@@ -0,0 +1,82 @@
+---
+title: Build integration
+description: How codeanalyzer-java auto-detects Maven and Gradle, downloads library dependencies for type resolution, and builds the project for WALA call-graph construction.
+---
+
+import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
+
+Accurate analysis needs two things from the build system: **resolved dependencies** (so the symbol solver can resolve qualified type names) and, at [level 2](/codeanalyzer-java/guides/analysis-levels/), **compiled classes** (so WALA can build the call graph). The `BuildProject` utility handles both, auto-detecting the build tool.
+
+## Build-tool auto-detection
+
+codeanalyzer-java inspects the project root and picks a build system:
+
+| Marker file | Build system | Invoked as |
+|-------------|--------------|------------|
+| `pom.xml` | Maven | `mvnw`/`mvnw.cmd` wrapper if present, otherwise system `mvn` |
+| `build.gradle` / `build.gradle.kts` | Gradle | `gradlew`/`gradlew.bat` wrapper if present, otherwise system `gradle` |
+
+Wrappers are preferred when present so the project's pinned tool version is used. The tool is validated by running its `--version` before any real work.
+
+## Dependency download (type resolution)
+
+Before parsing, codeanalyzer downloads the project's library dependencies so Javaparser's symbol solver can resolve types from third-party libraries. The JARs land in a temporary directory inside the project:
+
+
+
+Runs the equivalent of `dependency:copy-dependencies`, copying dependency JARs into:
+
+```
+target/_library_dependencies/
+```
+
+
+Applies an init script defining a `downloadDependencies` task that copies resolved dependencies into:
+
+```
+build/_library_dependencies/
+```
+
+
+
+After analysis, this `_library_dependencies` directory is removed automatically. Pass **`--no-clean-dependencies`** to keep it (useful for debugging resolution problems).
+
+
+If dependency download fails, analysis still proceeds — the analyzer logs a warning and continues with whatever it can resolve (the JDK and any classes it does find). Types that can't be resolved appear as simple, rather than fully-qualified, names.
+
+
+## Project build (call graph)
+
+At analysis level 2, WALA needs compiled classes. The `-b` / `--build-cmd` and `--no-build` flags control how that happens:
+
+- **Default (auto):** codeanalyzer compiles the project itself.
+ - Maven: `mvn compile` (tests skipped; common verification plugins — RAT, Findbugs, Checkstyle, PMD, Spotbugs, Enforcer, Javadoc, Spotless — are disabled to keep the build fast and resilient).
+ - Gradle: `gradle compileJava`.
+- **`-b "mvn clean install"`** — run your own build command instead of the auto-build.
+- **`--no-build`** — skip building; point the analyzer at an already-compiled project.
+
+### Including test classes
+
+By default only main sources are compiled. The hidden `--include-test-classes` flag additionally compiles test sources (`mvn test-compile` / `gradle compileTestJava`) so tests are included in analysis.
+
+## Pointing at a non-root build file
+
+For multi-module projects, the build descriptor may not sit at the input directory. Use **`-f` / `--project-root-path`** to point at the root `pom.xml` / `build.gradle` while `-i` still names the source directory you want analyzed. When omitted, the project root defaults to the input path.
+
+## Common build scenarios
+
+```bash
+# Pre-built project — skip the build, just analyze
+java -jar codeanalyzer-2.3.7.jar -i ./project -a 2 --no-build -o ./out
+
+# Custom build command
+java -jar codeanalyzer-2.3.7.jar -i ./project -a 2 -b "mvn -q clean package -DskipTests" -o ./out
+
+# Multi-module: analyze a submodule, build from the reactor root
+java -jar codeanalyzer-2.3.7.jar -i ./project/web -f ./project/pom.xml -a 2 -o ./out
+
+# Keep downloaded dependencies for inspection
+java -jar codeanalyzer-2.3.7.jar -i ./project -a 1 --no-clean-dependencies -o ./out
+```
+
+See the full flag list in the [CLI reference](/codeanalyzer-java/reference/cli/).
diff --git a/src/content/docs/guides/incremental-analysis.mdx b/src/content/docs/guides/incremental-analysis.mdx
new file mode 100644
index 00000000..8526b997
--- /dev/null
+++ b/src/content/docs/guides/incremental-analysis.mdx
@@ -0,0 +1,111 @@
+---
+title: Incremental analysis
+description: Use target files to re-analyze only the files that changed and patch them into an existing analysis.json, instead of re-running the whole project.
+---
+
+import { Aside, Steps } from "@astrojs/starlight/components";
+
+When only a handful of files change, re-analyzing the whole project is wasteful. The **`-t` / `--target-files`** flag re-runs symbol extraction for just the named files and merges the result into an existing `analysis.json` — or, when you emit to Neo4j, replaces just those files' subgraphs in a live property graph.
+
+## How it works
+
+```bash
+java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/project \
+ -t src/main/java/com/example/Service.java \
+ -t src/main/java/com/example/Repository.java \
+ -o ./output
+```
+
+
+
+1. codeanalyzer extracts the symbol table for **only** the target files.
+2. If `./output/analysis.json` already exists, the analyzer reads its existing `symbol_table`.
+3. Each re-analyzed compilation unit is tagged `is_modified: true` and replaces the corresponding entry in the existing table.
+4. The merged symbol table is written back to `analysis.json`.
+
+
+
+This is much faster than a full run because only the named files are parsed, while the rest of the symbol table is preserved as-is.
+
+
+Incremental analysis is a **symbol-table** operation. If you pass `-t` together with `-a 2`, the analyzer downgrades to level 1 and logs a warning — it will not recompute the call graph for individual files. To refresh the call graph, run a full level-2 analysis.
+
+
+## The `is_modified` flag
+
+Files updated through a target-file run carry `is_modified: true` on their compilation unit in the output. Consumers can use this to detect which entries changed since the last full run — for example, to invalidate caches or re-render only the affected parts of a UI.
+
+## Schema-compatibility guard
+
+When merging into an existing `analysis.json`, codeanalyzer checks that the file uses the current import schema (imports as structured objects, not bare strings). If it detects the **legacy** string-based import format, it refuses to merge and raises:
+
+> Existing analysis.json uses legacy import schema (imports as strings). Regenerate analysis with codeanalyzer 2.3.7 or newer.
+
+The fix is to regenerate the base `analysis.json` with a current JAR (2.3.7+) before applying incremental updates. See [Troubleshooting](/codeanalyzer-java/troubleshooting/).
+
+## Incremental updates to a Neo4j graph
+
+`-t` patches an `analysis.json` in place. The Neo4j Bolt writer is the graph analogue of the same idea: instead of merging compilation units back into a JSON file, it replaces only the changed units' subgraphs in a live, persistent property graph that many applications and many consumers share. Where the JSON form lives in one file you load whole, the graph is a queryable system of record you push deltas into over time.
+
+To target a live database, add `--emit neo4j` and a Bolt URI:
+
+```bash
+NEO4J_PASSWORD=secret java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/project \
+ --emit neo4j \
+ --app-name daytrader8 \
+ --neo4j-uri bolt://localhost:7687 \
+ --neo4j-user neo4j \
+ --neo4j-database neo4j \
+ -t src/main/java/com/example/Service.java \
+ -t src/main/java/com/example/Repository.java
+```
+
+The push is scoped to one application by `--app-name` (the `:JApplication` anchor node, here `daytrader8`), so independent apps coexist in one database without clobbering each other.
+
+
+The examples set `NEO4J_PASSWORD` rather than `--neo4j-password` so the secret never lands in shell history or process listings. The same precedence applies to the other connection settings: an explicit flag wins, otherwise `NEO4J_URI`, `NEO4J_USERNAME`, `NEO4J_PASSWORD`, and `NEO4J_DATABASE` are read from the environment.
+
+
+### How the Bolt writer decides what changed
+
+The Bolt writer is incremental by construction — it does not rewrite the whole graph on every run. It reads the database's current state and updates only what actually moved:
+
+
+
+1. It ensures the schema's constraints and indexes exist (idempotent — a no-op after the first run).
+2. For each compilation unit, it computes a `content_hash` (SHA-256 over the unit) and diffs it against the `content_hash` already stored on the unit in the live database.
+3. Units whose hash is **unchanged** are skipped entirely.
+4. Units whose hash **changed** have their subgraph replaced via idempotent `MERGE` upserts — types, callables, fields, call sites, and the rest are re-projected in batches.
+5. Shared `:JPackage` and `:JAnnotation` nodes are `MERGE`-only, so re-pushing one app never disturbs nodes another app depends on.
+
+
+
+Because every write is a `MERGE`, re-running the same analysis is a no-op against the graph — the push is fully idempotent. This makes it safe to run on every commit from CI, where most units hash identically and only the touched files cost anything.
+
+### `-t` skips orphan pruning, just as it forces level 1
+
+`-t` changes the Bolt push the same way it changes a JSON run: it narrows the scope and disables a whole-project step.
+
+On a **full run** (no `-t`), the writer knows it is seeing every compilation unit, so after upserting it **prunes orphans** — compilation units whose source file has vanished are detached and deleted, keeping the graph honest about what still exists on disk.
+
+On a **targeted run** (`-t` present), the writer only sees the files you named. It cannot tell a deleted file apart from one you simply didn't pass this run, so it marks the run as targeted and **skips orphan pruning** — only the named units' subgraphs are replaced; nothing is deleted.
+
+
+A `-t` Bolt push replaces only the subgraphs of the files you list. If a source file was **deleted**, its nodes remain in the graph until a full run (no `-t`) prunes them. After deletions, run a full analysis to garbage-collect vanished units — mirroring how a `-t` run also forgoes the level-2 call graph.
+
+
+
+The live Bolt push runs from the fat jar (`java -jar`), which bundles the Neo4j driver. The prebuilt GraalVM **native binary** does not bundle the driver, so passing `--neo4j-uri` there degrades to writing a `graph.cypher` snapshot with a warning instead of pushing over Bolt. Use the fat jar when you need the incremental live push.
+
+
+## When to use it
+
+- **CI / editor integrations** that re-analyze on save and want sub-second turnaround, patching either `analysis.json` or a shared graph.
+- **Large codebases** where a full parse is expensive but most files are unchanged — content-hash diffing means an unchanged unit costs nothing to re-push.
+- **A long-lived Neo4j graph** kept current commit-by-commit, where each run pushes only the delta rather than reloading the whole project.
+
+For call-graph-dependent work, or after large structural changes (including file deletions you want pruned from the graph), prefer a full [level-2 analysis](/codeanalyzer-java/guides/analysis-levels/).
+
+For the full Neo4j story — snapshot vs. live Bolt modes, the scoped wipe, multi-tenancy, and reading the graph back from the [Python SDK](/codeanalyzer-java/integration/python-sdk/) — see [Neo4j graph output](/codeanalyzer-java/guides/neo4j-output/).
diff --git a/src/content/docs/guides/neo4j-output.mdx b/src/content/docs/guides/neo4j-output.mdx
new file mode 100644
index 00000000..5036aa19
--- /dev/null
+++ b/src/content/docs/guides/neo4j-output.mdx
@@ -0,0 +1,258 @@
+---
+title: Neo4j graph output
+description: Project the analysis into a Neo4j property graph with --emit neo4j — a re-runnable graph.cypher snapshot or a live, incremental Bolt push — and read it back from the CLDK Python SDK.
+---
+
+import { Aside, Steps, Tabs, TabItem, Card, CardGrid } from "@astrojs/starlight/components";
+import Neo4jPropertyGraph from '../../../components/Neo4jPropertyGraph.astro';
+
+By default codeanalyzer-java writes one `analysis.json` per project. That file is self-contained, but it doesn't compose: to ask a question across a portfolio you load every blob into memory and stitch them together yourself. `--emit neo4j` projects the same symbol table and call graph into a **Neo4j property graph** instead — a queryable, persistent system of record that many applications can share, and that downstream tools read with Cypher rather than by parsing giant JSON files.
+
+
+
+The projection is **lossless**: every entity the analyzer extracts — compilation units, types, callables, fields, parameters, call sites, variables, enum constants, record components, initialization blocks, CRUD operations and queries, comments, annotations, and packages — becomes a first-class node or relationship. All node labels are `J`-prefixed and all relationship types are `J_`-prefixed, so a Java graph can share one Neo4j database with the Python (`Py*` / `PY_*`) and TypeScript (`TS*` / `TS_*`) backends without colliding. For the full label and relationship inventory, see the [graph-schema reference](/codeanalyzer-java/schema/).
+
+
+`--emit` selects *one* output target. With `--emit neo4j` the analyzer projects the IR and returns **without** writing `analysis.json`. The default (`--emit json`) is unchanged. There is also `--emit schema`, which prints the schema contract and runs no analysis at all (see [The schema contract](#the-schema-contract)).
+
+
+## Two emit modes
+
+`--emit neo4j` has two sub-modes, chosen purely by **whether a Bolt URI resolved** — from `--neo4j-uri` or the `NEO4J_URI` environment variable:
+
+```mermaid
+flowchart TD
+ A["--emit neo4j"] --> B{"Bolt URI resolved? (--neo4j-uri or NEO4J_URI)"}
+ B -->|"no"| C["CypherWriter writes graph.cypher"]
+ C --> C1["constraints + indexes"]
+ C --> C2["scoped wipe of this app's prior subgraph"]
+ C --> C3["batched UNWIND ... MERGE (BATCH = 500)"]
+ C --> C4["load later: cypher-shell < graph.cypher"]
+ B -->|"yes"| D["BoltWriter pushes over Bolt"]
+ D --> D1["ensure constraints + indexes"]
+ D --> D2["diff each unit's content_hash vs the live DB"]
+ D --> D3["replace only changed units' subgraphs (MERGE, BATCH = 1000)"]
+ D --> D4{"full run?"}
+ D4 -->|"yes"| D5["prune units whose source file vanished"]
+ D4 -->|"no (-t targeted)"| D6["skip orphan pruning"]
+```
+
+- **No URI → a `graph.cypher` snapshot.** A self-contained, re-runnable Cypher file expressing the *full* truth of this run. Good for review, version control, air-gapped loads, and CI artifacts.
+- **URI present → a live, incremental Bolt push.** The analyzer connects to a running Neo4j and updates only what changed since the last run. This is the mode you deploy against a shared cluster.
+
+
+The Neo4j driver is deliberately **not** bundled into the GraalVM native binary — it's loaded reflectively, so the native image can prune the driver and Netty. As a result, the prebuilt `codeanalyzer` native binary cannot open a Bolt connection: when you pass `--neo4j-uri` it **degrades gracefully to writing `graph.cypher`** and logs a warning. The live push happens from the fat JAR (`java -jar codeanalyzer-*.jar`). Use the JAR wherever you push over Bolt.
+
+
+## The `graph.cypher` snapshot
+
+With no Bolt URI, codeanalyzer renders a `CypherWriter` snapshot to `/graph.cypher` (defaulting to the current working directory if `-o` is omitted):
+
+```bash
+java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/project -a 2 \
+ --emit neo4j \
+ --app-name daytrader8 \
+ -o ./out
+# -> ./out/graph.cypher
+```
+
+The file is a single, ordered, re-runnable script that:
+
+
+
+1. Declares the **constraints and indexes** (uniqueness constraints plus a fulltext index for code search — see [The schema contract](#the-schema-contract)).
+2. Runs a **scoped wipe** of *this application's* prior subgraph — `MATCH (a:JApplication {name: 'daytrader8'})` then `DETACH DELETE` its units and their descendants. Shared `:JPackage` and `:JAnnotation` nodes are left intact so other applications keep theirs.
+3. Loads nodes and edges with **batched `UNWIND ... MERGE`** (`BATCH = 500`).
+
+
+
+Because the snapshot expresses full truth and wipes-then-reloads, **it is not incremental** — re-running it replaces this app's subgraph wholesale. Load it whenever you're ready:
+
+```bash
+cypher-shell -a bolt://localhost:7687 -u neo4j < ./out/graph.cypher
+```
+
+The scoped wipe means the snapshot is safe to load into a database that already hosts *other* applications: only the matching `:JApplication` anchor and its descendants are touched.
+
+## The live Bolt push
+
+When a Bolt URI resolves, the `BoltWriter` connects over the official `neo4j-java-driver` and updates the graph in place. Prefer the `NEO4J_PASSWORD` environment variable over `--neo4j-password` so the secret never lands in shell history or process listings:
+
+```bash
+export NEO4J_URI=bolt://localhost:7687
+export NEO4J_USERNAME=neo4j
+export NEO4J_PASSWORD=secret # keep credentials out of argv
+
+java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/project -a 2 \
+ --emit neo4j \
+ --app-name daytrader8
+```
+
+Everything resolves with a consistent precedence — **flag > environment variable > default**:
+
+| Setting | Flag | Environment | Default |
+|---------|------|-------------|---------|
+| Bolt URI | `--neo4j-uri` | `NEO4J_URI` | *(none → snapshot mode)* |
+| Username | `--neo4j-user` | `NEO4J_USERNAME` | `neo4j` |
+| Password | `--neo4j-password` | `NEO4J_PASSWORD` | `neo4j` |
+| Database | `--neo4j-database` | `NEO4J_DATABASE` | *(server default)* |
+
+The database is only pinned (`SessionConfig.forDatabase`) when you set it non-null; otherwise the driver uses the server's default database.
+
+### What "incremental" means here
+
+The push is genuinely incremental, not a wipe-and-reload. For each compilation unit, `BoltWriter`:
+
+
+
+1. **Diffs the `content_hash`.** Each unit carries a SHA-256 over its source. The writer reads the live DB's current state and compares — units whose hash is unchanged are skipped entirely.
+2. **Replaces only changed units' subgraphs** via idempotent `MERGE` upserts (`BATCH = 1000`), so re-running the same analysis is a no-op against an up-to-date graph.
+3. **Upserts shared nodes `MERGE`-only.** `:JPackage` and `:JAnnotation` are shared across applications; they are merged, never deleted, so concurrent app writers don't clobber each other.
+4. **Prunes orphans on a full run.** When you analyze the whole project (no `-t`), units whose source file has vanished are removed. On a **targeted** run (`-t` / `--target-files`), pruning is **skipped** — a targeted run only knows about the files you named, so it replaces just those subgraphs and leaves everything else alone.
+
+
+
+This is what makes the graph cheap to keep current: a CI job that re-analyzes on every push touches only the units that actually changed.
+
+
+Combine `--emit neo4j` over Bolt with `-t` to patch just the files a commit changed:
+
+```bash
+java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/project \
+ -t src/main/java/com/example/Service.java \
+ --emit neo4j --app-name daytrader8
+```
+
+Note that `-t` forces [level 1](/codeanalyzer-java/guides/incremental-analysis/), so a targeted run refreshes the symbol-table subgraph but not `J_CALLS` edges. Run a full `-a 2` analysis to recompute the call graph.
+
+
+## Scoping and multi-tenancy
+
+`--app-name` is the tenancy key. It sets the `name` of the single `:JApplication` anchor node (a uniqueness constraint guarantees one per name), and **every** analyzed compilation unit hangs off it via `(:JApplication {name})-[:J_HAS_UNIT]->(:JCompilationUnit)`. Both emit modes scope all of their writes — the snapshot's wipe and the Bolt push's upserts and pruning — to that one anchor.
+
+If you omit `--app-name`, it defaults to the base name of the `-i` input directory (or the literal `application` when there is no input). Because the wipe and the push are app-scoped, **many applications coexist in one database**, each rooted at its own anchor:
+
+```cypher
+// list every application living in this database
+MATCH (a:JApplication)
+RETURN a.name AS application, a.schema_version AS schema
+ORDER BY application;
+```
+
+Cross-service questions become a graph traversal instead of a memory problem. A whole-portfolio query never loads anything it doesn't need:
+
+```cypher
+// which applications declare a type that implements javax.servlet.Filter?
+MATCH (a:JApplication)-[:J_HAS_UNIT]->(:JCompilationUnit)
+ -[:J_DECLARES_TYPE]->(t:JType)
+WHERE 'javax.servlet.Filter' IN t.implements_list
+RETURN DISTINCT a.name AS application, t.fqn AS filter
+ORDER BY application;
+```
+
+### Schema version stamping
+
+Every emitted graph stamps `schema_version` on its `:JApplication` node. The current schema version is **`1.0.0`**. Read it straight off the anchor to confirm what contract a given application was loaded under:
+
+```cypher
+MATCH (a:JApplication {name: 'daytrader8'})
+RETURN a.schema_version; // -> "1.0.0"
+```
+
+## The schema contract
+
+`--emit schema` publishes the machine-readable schema contract — the catalog of every label, relationship, and property the projector is allowed to emit — and runs **no project analysis** (it short-circuits before any source is parsed, so it needs no `-i`):
+
+```bash
+# print the contract to stdout
+java -jar codeanalyzer-2.3.7.jar --emit schema
+
+# or write it to a file
+java -jar codeanalyzer-2.3.7.jar --emit schema -o ./out
+# -> ./out/schema.neo4j.json
+```
+
+The contract ships as DDL inside the analyzer: uniqueness constraints (including a global `:JSymbol.id` identity), plus indexes — among them a **fulltext index** (`j_code_fts`) over `JCallable.code` and `JCallable.docstring`, so you can full-text search source from Cypher:
+
+```cypher
+CALL db.index.fulltext.queryNodes('j_code_fts', 'executeQuery')
+YIELD node, score
+RETURN node.signature, score
+ORDER BY score DESC
+LIMIT 10;
+```
+
+A conformance test (`Neo4jSchemaConformanceTest`, no container required) asserts that the projector never emits an undeclared label, relationship, or property, and that `schema.neo4j.json` is current — so the contract you read is the contract the graph honors. For the full topology, see the [graph-schema reference](/codeanalyzer-java/schema/).
+
+## Deploying the producer/consumer split
+
+The Neo4j output naturally divides into a **producer** and **consumers**:
+
+
+
+Runs out-of-band as a CI / Kubernetes **Job** or **CronJob** from the fat JAR, pushing app-scoped subgraphs into a managed or clustered Neo4j over Bolt. These are the heavy pods — they build the project and run WALA.
+
+
+Agents, the CLDK Python SDK, and dashboards are lightweight, **read-only** Bolt clients. They never build or analyze anything; they query the graph and scale independently of the analysis pods.
+
+
+
+Many analyzer jobs write into one shared cluster — each anchored at its own `:JApplication` — and reads fan out from it. Give consumers **read-only credentials**; the SDK and Cypher dashboards need nothing more. For high availability, point producers at Neo4j Aura or an Enterprise cluster. Because the push is incremental and idempotent, a CronJob can re-run safely on a schedule without ever rebuilding the whole graph.
+
+
+The Bolt path is exercised by `Neo4jBoltWriterTest`, a Testcontainers-backed test that spins up a throwaway Neo4j. It is gated behind an environment flag so the default build stays container-free — enable it with `RUN_CONTAINER_TESTS=1`.
+
+
+## Reading the graph from the CLDK Python SDK
+
+The big payoff: analysis is **produced once, centrally, and read cheaply everywhere**. CLDK has a read-only Neo4j backend that reconstructs the **same typed model objects and the same `networkx` call graph** as the in-process analyzer — with **no JDK, no native binary, and no project source** on the consumer. It needs only the Bolt URI and read-only credentials.
+
+Install the driver extra:
+
+```bash
+pip install cldk[neo4j] # or: pip install neo4j
+```
+
+Select the backend by passing a `Neo4jConnectionConfig` to the `CLDK.java(...)` factory. The `application_name` here must match the `--app-name` the graph was loaded with — that's how the SDK scopes every query back to the right `:JApplication`:
+
+```python
+# Java project — read-only Neo4j backend
+from cldk import CLDK
+from cldk.analysis import AnalysisLevel
+from cldk.analysis.commons.backend_config import Neo4jConnectionConfig
+
+analysis = CLDK.java(
+ analysis_level=AnalysisLevel.call_graph,
+ backend=Neo4jConnectionConfig(
+ uri="bolt://localhost:7687",
+ username="neo4j",
+ password="neo4j", # read-only credentials are sufficient
+ application_name="daytrader8", # == the CLI --app-name
+ ),
+)
+
+symbol_table = analysis.get_symbol_table() # Dict[str, JCompilationUnit]
+cg = analysis.get_call_graph() # networkx.DiGraph
+klass = analysis.get_class("com.example.MyService")
+methods = analysis.get_methods_in_class("com.example.MyService")
+```
+
+The backend bulk-fetches nodes and relationships in a handful of Cypher queries and rebuilds the canonical `JApplication` — the same shape the in-process analyzer produces — so `get_*` returns identical `JType` / `JCallable` objects and the same call graph. Available methods include `get_symbol_table()`, `get_call_graph()`, `get_classes()`, `get_class()`, `get_methods()`, `get_methods_in_class()`, `get_callers()`, `get_callees()`, `get_entry_point_methods()`, and `get_all_crud_operations()`.
+
+
+The Neo4j backend is a pure read-only Cypher client. It never builds or writes the graph and has no dependency on the codeanalyzer engine — the graph is populated out of band by a separate `codeanalyzer --emit neo4j` job, and the SDK only reads it. Because the graph is external, `project_path` is **optional** for this backend. Backends are context managers (`with ...` / `.close()`). Parity with the in-memory backend holds modulo a few documented projection-lossy fields (e.g. comments collapse to a docstring; some call edges to external/library targets may be absent).
+
+
+
+The Neo4j read-back expects an emitter at **2.4.0 or newer**, with projection fixes landed in **2.4.1**. Keep the analyzer that populates the graph and the SDK that reads it on compatible versions.
+
+
+## Where to go next
+
+- [Graph-schema reference](/codeanalyzer-java/schema/) — every node label, relationship type, and property.
+- [Incremental analysis](/codeanalyzer-java/guides/incremental-analysis/) — how `-t` / `--target-files` works, and why it forces level 1.
+- [Analysis levels](/codeanalyzer-java/guides/analysis-levels/) — what `-a 1` vs. `-a 2` compute, and why only level 2 emits `J_CALLS`.
+- [Python SDK (CLDK)](/codeanalyzer-java/integration/python-sdk/) — the JSON-backed facade and how it relates to the Neo4j backend.
diff --git a/src/content/docs/index.mdx b/src/content/docs/index.mdx
new file mode 100644
index 00000000..76da1337
--- /dev/null
+++ b/src/content/docs/index.mdx
@@ -0,0 +1,45 @@
+---
+title: codeanalyzer-java
+description: The WALA + Javaparser static-analysis backend behind CodeLLM-DevKit's Java support — a standalone JAR that turns a Java project into a symbol table and call graph, emitted as a versioned JSON schema or a Neo4j property graph.
+template: doc
+hero:
+ tagline: One JAR turns an enterprise Java project into a symbol table and call graph — emitted as a single versioned JSON document, or projected losslessly into a Neo4j property graph your tools can query.
+ actions:
+ - text: Quickstart
+ link: /codeanalyzer-java/quickstart/
+ icon: rocket
+ variant: primary
+ - text: Output schema
+ link: /codeanalyzer-java/schema/
+ icon: right-arrow
+ variant: secondary
+ - text: GitHub
+ link: https://github.com/codellm-devkit/codeanalyzer-java
+ icon: github
+ variant: minimal
+---
+
+import { CardGrid, LinkCard } from "@astrojs/starlight/components";
+
+Point **codeanalyzer-java** at a Java project and get back `analysis.json` — a single document describing every class, method, field, and import in the codebase, plus an interprocedural call graph computed by WALA. The schema is the *same shape* the CLDK Python SDK deserializes into typed models, so whatever consumes it — an agent, a script, or the SDK — works against structured analysis instead of raw source.
+
+The same symbol table and call graph can also be projected, losslessly, into a **Neo4j property graph** with `--emit neo4j` — a re-runnable `graph.cypher` snapshot, or a live, incremental push over Bolt. One database can hold many applications side by side, each anchored at its own `:JApplication` node, so cross-service questions become a Cypher traversal instead of a memory problem. The SDK can then read straight from the graph — no JDK, native binary, or project source on the consumer.
+
+## Start here
+
+
+
+
+
+
+
+
+## Go deeper
+
+
+
+
+
+
+
+
diff --git a/src/content/docs/installing.mdx b/src/content/docs/installing.mdx
new file mode 100644
index 00000000..3979112c
--- /dev/null
+++ b/src/content/docs/installing.mdx
@@ -0,0 +1,191 @@
+---
+title: Installation
+description: Run codeanalyzer-java as a fat JAR, compile a native binary with GraalVM, or let the CLDK Python SDK bundle a compatible JAR for you.
+---
+
+import { Steps, Tabs, TabItem, Aside, CardGrid, LinkCard } from "@astrojs/starlight/components";
+
+There are three ways to get codeanalyzer-java, depending on what you're doing.
+
+
+
+
+
+
+
+
+If your goal is to *read* a graph that some other job already populated with `--emit neo4j`, you don't need any of this. The CLDK Python SDK's Neo4j backend is a pure read-only Cypher client — **no JDK, no analyzer binary, no project source** on the consumer. Jump to [reading a graph from Python](#reading-a-neo4j-graph-from-python), or see the [Neo4j output guide](/codeanalyzer-java/guides/neo4j-output/).
+
+
+## Prerequisites
+
+- A Linux, macOS, or WSL machine.
+- [SDKMan!](https://sdkman.io/) for managing JDK / GraalVM versions (recommended).
+
+Install SDKMan! if you don't have it:
+
+```bash
+curl -s "https://get.sdkman.io" | bash
+source "$HOME/.sdkman/bin/sdkman-init.sh"
+```
+
+## Option 1: Fat JAR (recommended)
+
+The fat JAR is the standard distribution — the CLDK SDK expects a JVM, and this is the simplest path. It is also the **only** build that can push live to Neo4j over Bolt (see the [native-image caveat](#native-image-and-the-neo4j-driver) below).
+
+
+
+1. **Install a JDK (Java 11 or above).** List available versions and install one:
+
+ ```bash
+ sdk list java | grep sem # IBM Semeru builds
+ sdk install java 17.0.10-sem
+ sdk use java 17.0.10-sem
+ ```
+
+2. **Build the JAR:**
+
+ ```bash
+ git clone https://github.com/codellm-devkit/codeanalyzer-java
+ cd codeanalyzer-java
+ ./gradlew fatJar
+ ```
+
+3. **Use it** — the JAR lands at `build/libs/codeanalyzer-2.3.7.jar`:
+
+ ```bash
+ java -jar build/libs/codeanalyzer-2.3.7.jar -i /path/to/project -a 2 -o ./output
+ ```
+
+
+
+The fat JAR is what you run as the **producer** in a Neo4j deployment — a CI or Kubernetes Job that projects analysis into the graph with `--emit neo4j` and pushes incrementally over Bolt. Prefer the `NEO4J_PASSWORD` environment variable over `--neo4j-password` so the secret never lands in shell history or process listings:
+
+```bash
+export NEO4J_URI=bolt://localhost:7687
+export NEO4J_USERNAME=neo4j
+export NEO4J_PASSWORD=secret # keep credentials out of argv
+
+java -jar build/libs/codeanalyzer-2.3.7.jar \
+ -i /path/to/project -a 2 \
+ --emit neo4j \
+ --app-name daytrader8
+```
+
+See the [Neo4j output guide](/codeanalyzer-java/guides/neo4j-output/) for the snapshot-vs-Bolt distinction, multi-tenancy, and the producer/consumer deployment model.
+
+## Option 2: Native binary (GraalVM)
+
+A native image needs no JVM at runtime. This is heavier to build but gives a standalone executable.
+
+
+
+1. **Install GraalVM** (17 or above):
+
+ ```bash
+ sdk list java | grep graal
+ sdk install java 21.0.2-graalce
+ sdk use java 21.0.2-graalce
+ ```
+
+2. **Compile the native binary.** `-PbinDir` is optional; without it the binary lands in `build/bin`:
+
+ ```bash
+ ./gradlew nativeCompile -PbinDir=$HOME/.local/bin
+ ```
+
+3. **Run it** (assuming the output dir is on your `$PATH`):
+
+ ```bash
+ codeanalyzer -i /path/to/project -a 2 -o ./output
+ ```
+
+
+
+
+The Neo4j driver is **deliberately not bundled** into the GraalVM native binary. `BoltWriter` and the official `neo4j-java-driver` are loaded reflectively (via a `Class.forName` name assembled at runtime), so native-image prunes the driver and Netty and keeps the binary small.
+
+The consequence: the prebuilt `codeanalyzer` native binary **cannot open a Bolt connection**. When you pass `--neo4j-uri`, it **degrades gracefully to writing a `graph.cypher` snapshot** and logs a warning — it does not error out. The live, incremental Bolt push happens only from the fat JAR (`java -jar`).
+
+What *does* work identically in both builds:
+
+- `--emit neo4j` **without** a URI → the re-runnable `graph.cypher` snapshot.
+- `--emit schema` → the `schema.neo4j.json` contract (it runs no analysis at all).
+- `--emit json` (the default) → `analysis.json`.
+
+So use the native binary freely for `analysis.json`, snapshots, and the schema contract; reach for the **fat JAR wherever you push live over Bolt**.
+
+
+
+If a native binary throws unexpected exceptions that the JAR doesn't, the bundled `reflect-config.json` is likely stale. Regenerate it with the native-image agent, then rebuild:
+
+```bash
+./gradlew fatJar
+java -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image-config \
+ -jar build/libs/codeanalyzer-2.3.7.jar -i /path/to/sample/project -a 2 -v
+./gradlew nativeCompile
+```
+
+
+## Option 3: Via the Python SDK
+
+If your goal is to use Java analysis from Python, you don't need to build anything. The CLDK SDK ships a compatible JAR and discovers it automatically:
+
+```bash
+pip install cldk
+```
+
+```python
+from cldk import CLDK
+from cldk.analysis import AnalysisLevel
+
+analysis = CLDK.java(
+ project_path="commons-cli",
+ analysis_level=AnalysisLevel.call_graph,
+)
+print(len(analysis.get_classes()), "classes")
+```
+
+This is the **in-process** backend: the SDK runs the bundled `codeanalyzer` over your project, parses the resulting `analysis.json`, and builds the model in process. To point it at a JAR you built yourself, pass `analysis_backend_path`. See [Python SDK integration](/codeanalyzer-java/integration/python-sdk/).
+
+### Reading a Neo4j graph from Python
+
+There's a second SDK path that builds nothing on the read side. When analysis has already been projected into a Neo4j property graph — by a separate `codeanalyzer --emit neo4j` job — the SDK can become a **read-only Cypher client** that reconstructs the **same typed model objects and the same `networkx` call graph** as the in-process analyzer, with **no JDK, no analyzer binary, and no project source** required. It needs only a Bolt URI and read-only credentials.
+
+Install the optional driver extra:
+
+```bash
+pip install "cldk[neo4j]" # or: pip install neo4j
+```
+
+Select this backend by passing a `Neo4jConnectionConfig` to the `CLDK.java(...)` factory. The `application_name` must match the `--app-name` the graph was loaded with — that's how the SDK scopes every query to the right `:JApplication`:
+
+```python
+from cldk import CLDK
+from cldk.analysis import AnalysisLevel
+from cldk.analysis.commons.backend_config import Neo4jConnectionConfig
+
+analysis = CLDK.java(
+ analysis_level=AnalysisLevel.call_graph,
+ backend=Neo4jConnectionConfig(
+ uri="bolt://localhost:7687",
+ username="neo4j",
+ password="neo4j", # read-only credentials are sufficient
+ application_name="daytrader8", # == the CLI --app-name
+ ),
+)
+
+symbol_table = analysis.get_symbol_table() # Dict[str, JCompilationUnit]
+cg = analysis.get_call_graph() # networkx.DiGraph
+```
+
+Because the graph is external, `project_path` is **optional** for this backend — there is no source tree to point at. The Neo4j read-back expects an emitter at **2.4.0 or newer** (with projection fixes landed in **2.4.1**). For the full deployment story, see the [Python SDK integration](/codeanalyzer-java/integration/python-sdk/) and the [Neo4j output guide](/codeanalyzer-java/guides/neo4j-output/).
+
+## Verify your install
+
+```bash
+java -jar build/libs/codeanalyzer-2.3.7.jar --version
+# 2.3.7
+```
+
+If you see the version string, you're ready — head to the [Quickstart](/codeanalyzer-java/quickstart/).
diff --git a/src/content/docs/integration/python-sdk.mdx b/src/content/docs/integration/python-sdk.mdx
new file mode 100644
index 00000000..fde8ea4d
--- /dev/null
+++ b/src/content/docs/integration/python-sdk.mdx
@@ -0,0 +1,241 @@
+---
+title: Python SDK (CLDK)
+description: How the CodeLLM-DevKit Python SDK drives codeanalyzer-java — running the analyzer to produce analysis.json, or reading a Neo4j property graph it populated out of band — behind the JavaAnalysis facade.
+---
+
+import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
+
+codeanalyzer-java is the JVM analysis engine behind [CodeLLM-DevKit (CLDK)](https://github.com/codellm-devkit/python-sdk)'s Java support. The SDK doesn't re-implement Java analysis — it gets the analysis from this engine and wraps it in a typed facade so Python callers never touch the backend directly.
+
+There are two ways it gets that analysis, and you pick between them by the **type** of object you pass to `backend=`:
+
+- **`CodeAnalyzerConfig`** (the default) — the SDK runs `codeanalyzer` on your project, parses the resulting `analysis.json`, and builds the model in process. This is the classic flow: source in, models out.
+- **`Neo4jConnectionConfig`** — the SDK becomes a **read-only Cypher client**. It reads a Neo4j property graph that some *other* job already populated with `codeanalyzer --emit neo4j`, and reconstructs the **same typed models** from the graph. No JDK, no analyzer binary, no project source on the consumer.
+
+Both produce an identical `JavaAnalysis` facade. The second is the one that scales across a portfolio — more on that below.
+
+## The flow
+
+```mermaid
+flowchart LR
+ A["CLDK.java(...)"] --> SEL{"backend type?"}
+
+ SEL -->|"CodeAnalyzerConfig (default)"| B["JCodeanalyzer backend"]
+ B --> C["codeanalyzer -i project -a level"]
+ C --> D["analysis.json (IR)"]
+
+ SEL -->|"Neo4jConnectionConfig"| N["JavaAnalysisBackend (read-only Cypher)"]
+ N --> G["Neo4j property graph (:JApplication {name})"]
+
+ C -. "out of band: codeanalyzer --emit neo4j" .-> EMIT{"Bolt URI set?"}
+ EMIT -->|"yes (fat jar)"| BOLT["live Bolt push (incremental, content_hash diff)"]
+ EMIT -->|"no"| SNAP["graph.cypher snapshot (scoped wipe + UNWIND MERGE)"]
+ BOLT --> G
+ SNAP -.->|"cypher-shell < graph.cypher"| G
+
+ D --> E["typed models JApplication / JType / JCallable"]
+ G --> E
+ E --> F["JavaAnalysis facade"]
+```
+
+The left half is the in-process backend; the right half is the Neo4j backend. The dotted edges show how the graph gets there in the first place: an analyzer run with `--emit neo4j` projects the very same IR (the thing that would otherwise become `analysis.json`) into the graph, either as a re-runnable `graph.cypher` snapshot or as a live incremental Bolt push. Whichever path filled the graph, the SDK reads it back into the same Pydantic models.
+
+## Default backend: running the analyzer
+
+This is the in-process flow. If you don't pass `backend=`, CLDK shells out to `codeanalyzer`, parses `analysis.json`, and builds the model.
+
+
+
+1. **Binary discovery** — if you don't point the SDK at a specific build, it locates a bundled `codeanalyzer` distribution from its package resources.
+2. **Invocation** — it runs the analyzer over your project at the requested level (`codeanalyzer -i -a -o `) and reads back the emitted `analysis.json`.
+3. **Parsing** — the JSON is deserialized into Pydantic models: `JApplication` (the whole document), `JType`, `JCallable`, and the rest — mirroring the [output schema](/codeanalyzer-java/schema/).
+4. **Facade** — the models are wrapped in `JavaAnalysis`, which exposes query methods like `get_classes()`, `get_methods_in_class()`, `get_call_graph()`, and `get_callers()`.
+
+
+
+```python
+from cldk import CLDK
+from cldk.analysis import AnalysisLevel
+
+# No backend= -> the default in-process JCodeanalyzer backend
+analysis = CLDK.java(
+ project_path="commons-cli",
+ analysis_level=AnalysisLevel.call_graph, # -> runs with -a 2
+)
+
+print(len(analysis.get_classes()), "classes")
+print(analysis.get_call_graph()) # -> networkx.DiGraph
+```
+
+The `analysis_level` maps directly onto the analyzer's [`-a` flag](/codeanalyzer-java/reference/cli/): `AnalysisLevel.symbol_table` → `-a 1`, `AnalysisLevel.call_graph` → `-a 2`.
+
+
+The SDK's Pydantic models are locked to a compatible schema [version](/codeanalyzer-java/schema/#versioning-and-stability). If you point it at a build whose `analysis.json` version differs incompatibly, deserialization can warn or fail. Keep the analyzer and SDK versions aligned.
+
+
+## Neo4j backend: reading from the graph
+
+The default backend re-analyzes the project on every run, in process. That's fine for one project on a developer's laptop. It does **not** compose across a portfolio: every `analysis.json` is a standalone document that has to be loaded whole into memory, and forty services means forty JSON blobs and forty re-runs.
+
+The Neo4j backend inverts this. Analysis is produced **once, centrally** — a CI or Kubernetes job runs `codeanalyzer --emit neo4j` and pushes an app-scoped subgraph into a shared Neo4j database (see the [Neo4j output guide](/codeanalyzer-java/guides/neo4j-output/)). Every consumer — agents, dashboards, and this SDK — is then a lightweight read-only client that just queries the graph. No analysis happens on the read side at all.
+
+Pass a `Neo4jConnectionConfig` to `backend=` and the facade swaps onto the read-only `JavaAnalysisBackend`:
+
+```python
+from cldk import CLDK
+from cldk.analysis import AnalysisLevel
+from cldk.analysis.commons.backend_config import Neo4jConnectionConfig
+
+analysis = CLDK.java(
+ analysis_level=AnalysisLevel.call_graph,
+ backend=Neo4jConnectionConfig(
+ uri="bolt://localhost:7687",
+ username="neo4j",
+ password="neo4j",
+ application_name="daytrader8",
+ ),
+)
+
+symbol_table = analysis.get_symbol_table() # Dict[str, JCompilationUnit]
+cg = analysis.get_call_graph() # networkx.DiGraph
+klass = analysis.get_class("com.example.MyService") # JType
+methods = analysis.get_methods_in_class("com.example.MyService")
+```
+
+The driver is an **optional dependency** — install it with the extra:
+
+```bash
+pip install "cldk[neo4j]" # or: pip install neo4j
+```
+
+If the `neo4j` driver isn't installed, constructing the backend raises `CodeanalyzerExecutionException` with that install hint.
+
+### Connection config
+
+`Neo4jConnectionConfig` is a thin wrapper over the official `neo4j` Python driver. The driver is created with `GraphDatabase.driver(uri, auth=(username, password))` and every query runs in `session(database=database)`.
+
+| Field | Default | Notes |
+| --- | --- | --- |
+| `uri` | *(required)* | Bolt URI of the Neo4j server, e.g. `bolt://localhost:7687`. |
+| `username` | `"neo4j"` | **Read-only credentials are sufficient** — the SDK never writes. |
+| `password` | `"neo4j"` | Read-only credentials are sufficient. |
+| `database` | `None` | Database name; `None` uses the server's default database. |
+| `application_name` | `None` | The `:JApplication` anchor to scope every query to. |
+
+
+`application_name` **must equal the `--app-name`** the graph was loaded with. That value is the unique key of the `:JApplication` anchor node — it's how a single Neo4j database hosts many applications side by side without them clobbering each other.
+
+If you omit it, the backend falls back to `Path(project_path).name` when a `project_path` was given; if it still can't resolve a name it raises `CodeanalyzerExecutionException("application_name is required to scope queries to an application.")`. In the example above the analyzer was run with `--app-name daytrader8`, so the SDK reads with `application_name="daytrader8"`.
+
+
+Because the graph is external, **`project_path` is optional** for the Neo4j backend — there is no source tree to point at. The backend is also a context manager, so you can scope the driver's lifetime:
+
+```python
+import os
+
+with CLDK.java(
+ backend=Neo4jConnectionConfig(
+ uri="bolt://neo4j.internal:7687",
+ username="reader", # read-only RBAC role
+ password=os.environ["NEO4J_PASSWORD"],
+ application_name="daytrader8",
+ ),
+) as analysis:
+ entrypoints = analysis.get_entry_point_methods()
+ cruds = analysis.get_all_crud_operations()
+```
+
+### What you get back
+
+The backend doesn't return raw graph rows. It bulk-fetches nodes and relationships in a handful of Cypher queries and **reconstructs the canonical `JApplication`** — handing an `analysis.json`-shaped payload to `JApplication(**payload)` — exactly the model the in-process analyzer would have built. So the `get_*` methods return the **identical typed objects** (`JType`, `JCallable`, a `networkx.DiGraph` call graph) regardless of which backend produced them:
+
+```python
+# Same methods, same return types, whichever backend you chose
+analysis.get_symbol_table() # Dict[str, JCompilationUnit]
+analysis.get_classes() # all JType nodes for this application
+analysis.get_class(fqn) # one JType
+analysis.get_methods_in_class(fqn) # callables in a class
+analysis.get_callers(...) # who calls this (level 2)
+analysis.get_callees(...) # what this calls (level 2)
+analysis.get_entry_point_methods()
+analysis.get_all_crud_operations()
+```
+
+A `get_call_graph()` over the Neo4j backend reads the projected `J_CALLS` edges directly out of the graph:
+
+```cypher
+MATCH (app:JApplication {name: $appName})-[:J_HAS_UNIT]->(:JCompilationUnit)
+ -[:J_DECLARES_TYPE]->(:JType)-[:J_HAS_CALLABLE]->(caller:JCallable)
+MATCH (caller)-[:J_CALLS]->(callee:JCallable)
+RETURN caller.id AS source, callee.id AS target
+```
+
+### Caveats and version requirements
+
+
+- **`J_CALLS` only exists at `-a 2`.** The graph carries call-graph edges only if the analyzer ran at analysis level 2. If the application was projected at level 1 (symbol table only), `get_call_graph()` has the types and methods but no call edges — the same limitation as a level-1 `analysis.json`.
+- **`J_CALLS` is gated to resolved application callables.** A call edge is kept only when *both* endpoints were emitted as `:JCallable` nodes, so calls into external libraries (JDK, third-party jars) don't appear as `J_CALLS` edges. This matches the in-memory call graph.
+- **Projection-lossy fields.** Parity with the in-memory backend holds *modulo* a few documented gaps — for instance comments collapse to a docstring on the owning node. The schema is a [lossless projection of the IR](/codeanalyzer-java/schema/neo4j-graph/) for structure; the small deltas are around comments and external call targets.
+- **Emitter version.** The graph must be produced by a `codeanalyzer-java` emitter **≥ 2.4.0**. Several projection fixes (issues #156 / #157 / #158 — e.g. multi-declarator fields kept as distinct nodes, single-type imports linked to a `:JType` rather than collapsed to a `:JPackage`) landed in **2.4.1**, so prefer **≥ 2.4.1** for full SDK parity.
+
+
+## Choosing a backend
+
+
+
+
+Use when you have the project source on hand and want a self-contained, one-shot analysis — local development, a single repo in CI, a notebook.
+
+```python
+analysis = CLDK.java(
+ project_path="my_project",
+ analysis_level=AnalysisLevel.call_graph,
+)
+```
+
+Every run re-analyzes the project; nothing is shared between runs or services.
+
+
+
+
+Use when analysis is produced centrally and read in many places — agents, dashboards, cross-service queries — without shipping the JDK, the analyzer binary, or the source to every consumer.
+
+```python
+analysis = CLDK.java(
+ analysis_level=AnalysisLevel.call_graph,
+ backend=Neo4jConnectionConfig(
+ uri="bolt://neo4j.internal:7687",
+ password=os.environ["NEO4J_PASSWORD"],
+ application_name="daytrader8",
+ ),
+)
+```
+
+Analysis is produced once by a separate `--emit neo4j` job; reads scale independently and cost a Cypher query.
+
+
+
+
+The whole point of the Neo4j backend is that **the read side carries no analysis dependency**. Forty services analyzed by forty Kubernetes jobs land in one cluster, each anchored at its own `:JApplication`, and a single SDK client queries across all of them by `application_name` — a graph traversal, not forty JSON parses.
+
+## Pointing at a custom build
+
+To use an analyzer you built yourself — say, a local development build — pass `analysis_backend_path` (a directory containing the analyzer distribution):
+
+```python
+analysis = CLDK.java(
+ project_path="my_project",
+ analysis_level=AnalysisLevel.call_graph,
+ analysis_backend_path="/path/containing/codeanalyzer",
+)
+```
+
+This is the bridge between this repo and the SDK: build it ([Installation](/codeanalyzer-java/installing/)), then point the SDK at your build output. This applies to the **in-process** backend only — the Neo4j backend has no analyzer to locate, since the graph was populated out of band.
+
+## See also
+
+- [Neo4j graph output](/codeanalyzer-java/guides/neo4j-output/) — how to populate the graph with `--emit neo4j`, snapshot vs. live Bolt, and the producer/consumer deployment model.
+- [Neo4j graph schema](/codeanalyzer-java/schema/neo4j-graph/) — the node labels, `J_*` relationships, constraints, and indexes the backend reads.
+- [CLI options](/codeanalyzer-java/reference/cli/) — `--emit`, `--app-name`, and the `--neo4j-*` connection flags.
+
+For the bigger picture — concepts, agent recipes, the cross-language API — see the main [CodeLLM-DevKit documentation](https://codellm-devkit.info).
diff --git a/src/content/docs/quickstart.mdx b/src/content/docs/quickstart.mdx
new file mode 100644
index 00000000..453ef90a
--- /dev/null
+++ b/src/content/docs/quickstart.mdx
@@ -0,0 +1,143 @@
+---
+title: Quickstart
+description: Build the codeanalyzer-java JAR and run your first analysis — symbol table and call graph, as analysis.json or a Neo4j graph — in a couple of minutes.
+---
+
+import { Steps, Tabs, TabItem, Aside } from "@astrojs/starlight/components";
+
+This guide gets you from a clone to a working `analysis.json` in a couple of minutes. For installation alternatives (native binary, pre-built JAR via the Python SDK), see [Installation](/codeanalyzer-java/installing/).
+
+## Prerequisites
+
+- A Linux, macOS, or WSL machine
+- A JDK, version **11 or above** (Java 17 recommended). We suggest installing it with [SDKMan!](https://sdkman.io/)
+
+
+If you only need the JAR to power the CLDK Python SDK, you don't have to build anything — `pip install cldk` bundles a compatible JAR. See [Python SDK integration](/codeanalyzer-java/integration/python-sdk/).
+
+
+## Build the JAR
+
+
+
+1. **Install a JDK** (Java 17 shown here, via SDKMan!):
+
+ ```bash
+ sdk install java 17.0.10-sem
+ sdk use java 17.0.10-sem
+ ```
+
+2. **Clone and build the fat JAR:**
+
+ ```bash
+ git clone https://github.com/codellm-devkit/codeanalyzer-java
+ cd codeanalyzer-java
+ ./gradlew fatJar
+ ```
+
+ The build produces a self-contained JAR at `build/libs/codeanalyzer-2.3.7.jar`.
+
+3. **Confirm it runs:**
+
+ ```bash
+ java -jar build/libs/codeanalyzer-2.3.7.jar --version
+ ```
+
+
+
+## Run your first analysis
+
+### Symbol table only (fast)
+
+Analysis level 1 parses source and builds the symbol table. It does not require building the target project, so it's quick:
+
+```bash
+java -jar build/libs/codeanalyzer-2.3.7.jar \
+ -i /path/to/your/project \
+ -a 1 \
+ -o ./output
+```
+
+This writes `./output/analysis.json` containing the `symbol_table` for every `.java` file.
+
+### Symbol table + call graph
+
+Analysis level 2 additionally builds the WALA call graph. By default codeanalyzer will build the target project (so WALA has compiled classes and resolved dependencies to work from):
+
+```bash
+java -jar build/libs/codeanalyzer-2.3.7.jar \
+ -i /path/to/your/project \
+ -a 2 \
+ -o ./output \
+ -v
+```
+
+The `-v` flag streams progress logs so you can watch the build and call-graph construction.
+
+### Analyze a single source string
+
+No project, no build — pass Java source directly and get a symbol table back on stdout:
+
+```bash
+java -jar build/libs/codeanalyzer-2.3.7.jar \
+ -s "public class Hello { public static void main(String[] a){} }" \
+ -a 1
+```
+
+
+When `-o` is omitted, the consolidated JSON is printed to stdout instead of being written to a file. This is how the Python SDK can capture output without a temp directory.
+
+
+## Read the output
+
+`analysis.json` has this top-level shape:
+
+```json
+{
+ "symbol_table": { "/abs/path/File.java": { /* compilation unit */ } },
+ "call_graph": [ /* caller→callee edges, present at level 2 */ ],
+ "version": "2.3.7"
+}
+```
+
+Continue to the [Output schema](/codeanalyzer-java/schema/) for the full structure, or the [CLI reference](/codeanalyzer-java/reference/cli/) for every flag.
+
+## Emit to Neo4j
+
+`analysis.json` is self-contained, but it doesn't compose: to ask a question across a portfolio you load every blob into memory and stitch it together yourself. `--emit neo4j` projects the *same* symbol table and call graph into a **Neo4j property graph** instead — a queryable system of record that many applications can share. `--emit` selects *one* output target, so `--emit neo4j` returns **without** writing `analysis.json`.
+
+The quickest path needs no running database. With no Bolt URI, codeanalyzer renders a self-contained, re-runnable `graph.cypher` snapshot:
+
+```bash
+java -jar build/libs/codeanalyzer-2.3.7.jar \
+ -i /path/to/your/project \
+ -a 2 \
+ --emit neo4j \
+ --app-name daytrader8 \
+ -o ./output
+# -> ./output/graph.cypher
+```
+
+`--app-name` is the tenancy key — it anchors this app's subgraph at a `:JApplication` node, so one database can host many apps side by side. Load the snapshot into any Neo4j whenever you're ready; the script declares its constraints and indexes, does a scoped wipe of just *this* app's prior subgraph, then `MERGE`-loads the graph:
+
+```bash
+cypher-shell -a bolt://localhost:7687 -u neo4j < ./output/graph.cypher
+```
+
+To push **live and incrementally** to a running cluster — only re-sending the compilation units whose `content_hash` changed — set a Bolt URI instead. Prefer the `NEO4J_PASSWORD` environment variable so the secret never lands in shell history:
+
+```bash
+export NEO4J_URI=bolt://localhost:7687
+export NEO4J_USERNAME=neo4j
+export NEO4J_PASSWORD=secret
+
+java -jar build/libs/codeanalyzer-2.3.7.jar \
+ -i /path/to/your/project -a 2 \
+ --emit neo4j --app-name daytrader8
+```
+
+
+The Neo4j driver isn't bundled into the GraalVM native binary, so the prebuilt `codeanalyzer` native binary can't open a Bolt connection — passing `--neo4j-uri` makes it fall back to writing `graph.cypher` with a warning. The live Bolt push happens from the fat JAR (`java -jar`).
+
+
+Once the graph is populated, the CLDK Python SDK reads it back with **no JDK, binary, or project source** — only read-only credentials. See the [Neo4j graph output guide](/codeanalyzer-java/guides/neo4j-output/) for the two emit modes, deployment as a Kubernetes Job, and the `--emit schema` contract, or [Python SDK integration](/codeanalyzer-java/integration/python-sdk/) to read the graph from Python.
diff --git a/src/content/docs/reference/cli.mdx b/src/content/docs/reference/cli.mdx
new file mode 100644
index 00000000..ab8e55e5
--- /dev/null
+++ b/src/content/docs/reference/cli.mdx
@@ -0,0 +1,160 @@
+---
+title: Command-line options
+description: The complete codeanalyzer-java CLI reference — every flag, what it does, and its default.
+---
+
+import { Aside } from "@astrojs/starlight/components";
+
+codeanalyzer-java is a Picocli command. Invoke it as a fat JAR (`java -jar codeanalyzer-2.3.7.jar ...`) or, if you built a native image, as `codeanalyzer ...`.
+
+By default the analyzer writes one `analysis.json` per project. Pass `--emit neo4j` to project that same model into a [Neo4j property graph](/codeanalyzer-java/guides/neo4j-output/) instead — either a self-contained `graph.cypher` snapshot or a live, incremental push over Bolt. The Neo4j-specific flags below all hang off that mode.
+
+## Usage
+
+```
+Usage: codeanalyzer [-hvV] [--no-build] [--no-clean-dependencies]
+ [--include-test-classes] [-a=] [-b=]
+ [-f=] [-i= ] [-o=]
+ [-s=] [-t=]...
+ [--emit=] [--app-name=]
+ [--neo4j-uri=] [--neo4j-user=]
+ [--neo4j-password=] [--neo4j-database=]
+
+Analyze java application.
+```
+
+## Options
+
+| Flag | Argument | Description | Default |
+|------|----------|-------------|---------|
+| `-i`, `--input` | path | Path to the project root directory to analyze. | — |
+| `-s`, `--source-analysis` | string | Analyze a single string of Java source instead of a project. No build required. | — |
+| `-o`, `--output` | path | Destination directory. Holds `analysis.json` for the default emit, `graph.cypher` for `--emit neo4j` without a URI, or `schema.neo4j.json` for `--emit schema`. If omitted, output goes to stdout. | stdout / cwd |
+| `-a`, `--analysis-level` | `1` \| `2` | `1` = symbol table only; `2` = symbol table + call graph. | `1` |
+| `-b`, `--build-cmd` | string | Custom build command. When omitted at level 2, an auto build is used. | auto |
+| `--no-build` | flag | Do not build the application; use already-compiled output. | off |
+| `--no-clean-dependencies` | flag | Do not delete the downloaded `_library_dependencies` directory after analysis. | off |
+| `-f`, `--project-root-path` | path | Path to the root `pom.xml` / `build.gradle` (for multi-module projects). | value of `-i` |
+| `-t`, `--target-files` | path | A file to (re)analyze incrementally; repeatable. Forces level 1. | — |
+| `--include-test-classes` | flag | Also compile/analyze test sources. (Hidden option.) | off |
+| `--emit` | `json` \| `neo4j` \| `schema` | Output target. `json` writes `analysis.json`; `neo4j` projects the model into a Neo4j property graph; `schema` prints the graph schema contract. Matched case-insensitively. | `json` |
+| `--app-name` | string | Logical application name used as the `:JApplication` anchor that scopes the graph. (Neo4j emit only.) | input dir base name |
+| `--neo4j-uri` | uri | Bolt URI of a live Neo4j, e.g. `bolt://localhost:7687`. Its presence is what switches `--emit neo4j` from a snapshot to a live push. Falls back to `NEO4J_URI`. | — |
+| `--neo4j-user` | string | Neo4j username for the Bolt push. Falls back to `NEO4J_USERNAME`. | `neo4j` |
+| `--neo4j-password` | string | Neo4j password for the Bolt push. Falls back to `NEO4J_PASSWORD`. Prefer the env var. | `neo4j` |
+| `--neo4j-database` | string | Target Neo4j database. Falls back to `NEO4J_DATABASE`; when unset, the server default database is used. | server default |
+| `-v`, `--verbose` | flag | Print logs to the console. | off |
+| `-h`, `--help` | flag | Show help and exit. | — |
+| `-V`, `--version` | flag | Print version information and exit. | — |
+
+
+Options are mutually informing, not all combinable. `-s` (single source) bypasses the project/build path entirely. `-t` (target files) forces level 1 even if `-a 2` is given. `--emit` is an *alternative* output target, not additive: `--emit neo4j` and `--emit schema` short-circuit the JSON write. See the linked guides for the semantics behind each flag.
+
+
+## Notes on key flags
+
+### `-i` vs. `-s`
+
+- **`-i`** points at a project directory on disk. This is the normal mode; dependencies are downloaded and (at level 2) the project is built.
+- **`-s`** passes Java source as a string. The symbol table is built directly from that snippet with JDK-only type resolution — no project, no build, no dependency download.
+
+Exactly one of these is the analysis subject. `-s` takes precedence when both are present.
+
+### `-o` (output)
+
+What lands in `-o` depends on `--emit`:
+
+- **`json`** (default) — writes `/analysis.json`, creating the directory if needed. Without `-o`, the consolidated JSON goes to stdout — convenient for piping, and how the [Python SDK](/codeanalyzer-java/integration/python-sdk/) can capture output without a temp file.
+- **`neo4j` without a URI** — writes `/graph.cypher` (defaults to the current working directory if `-o` is omitted).
+- **`schema`** — writes `/schema.neo4j.json`, or prints it to stdout when `-o` is omitted.
+
+### `-a` (analysis level)
+
+See [Analysis levels](/codeanalyzer-java/guides/analysis-levels/). Level 2 implies a build unless you pass `--no-build` or a custom `-b`. In the Neo4j projection, level 2 is also what adds `J_CALLS` edges (the WALA call graph) to the graph; level 1 emits the lossless symbol-table subgraph with no `J_CALLS`.
+
+### `-t` (target files)
+
+See [Incremental analysis](/codeanalyzer-java/guides/incremental-analysis/). Repeat the flag for multiple files. Forces level 1; merges into an existing `analysis.json` when one is present in the output directory.
+
+On a **live Bolt push** (`--emit neo4j` with a URI), `-t` marks the run as *targeted*: only the changed compilation units' subgraphs are replaced, and orphan pruning of vanished units is **skipped**. A full run (no `-t`) prunes units whose source file has disappeared. See [Neo4j output](#neo4j-output) below.
+
+## Neo4j output
+
+The four `--emit neo4j` modifiers (`--app-name`, `--neo4j-uri`, `--neo4j-user`, `--neo4j-password`, `--neo4j-database`) follow the same resolution order:
+
+> **flag > environment variable > default**
+
+A flag wins when set; otherwise the matching env var is used; otherwise the built-in default.
+
+| Flag | Environment variable | Default |
+|------|----------------------|---------|
+| `--neo4j-uri` | `NEO4J_URI` | — (no URI → `graph.cypher` snapshot) |
+| `--neo4j-user` | `NEO4J_USERNAME` | `neo4j` |
+| `--neo4j-password` | `NEO4J_PASSWORD` | `neo4j` |
+| `--neo4j-database` | `NEO4J_DATABASE` | server default |
+
+Keep credentials off the command line — prefer the `NEO4J_PASSWORD` environment variable to `--neo4j-password`, which would otherwise show up in shell history and process listings.
+
+### Two emit modes
+
+Whether `--emit neo4j` writes a file or talks to a server is decided **purely by whether a Bolt URI resolved** (from `--neo4j-uri` or `NEO4J_URI`):
+
+- **No URI → `graph.cypher` snapshot.** A self-contained, re-runnable Cypher script: constraints and indexes, a scoped wipe of *this* application's prior subgraph, then batched `UNWIND ... MERGE` for nodes and edges. It expresses the full truth (it is **not** incremental). Load it with `cypher-shell < graph.cypher`.
+- **URI present → live incremental Bolt push.** The Bolt writer ensures constraints and indexes, diffs each compilation unit's `content_hash` against the live database, and replaces **only changed units' subgraphs** via idempotent `MERGE` upserts. Shared `:JPackage` / `:JAnnotation` nodes are upserted once. On a full run it prunes units whose source file has vanished; a `-t` targeted run skips that pruning.
+
+`--app-name` is the tenancy key in both modes: it sets the `name` of the single `:JApplication` anchor, the wipe deletes only that application's subgraph, and many applications can share one Neo4j database side by side, each rooted at its own `:JApplication`. When omitted, it defaults to the base name of the `-i` input directory. The `:JApplication` node also carries `schema_version` (`1.0.0`), the versioned schema contract the graph conforms to.
+
+
+**Native-image caveat.** The Neo4j driver is deliberately *not* bundled in the GraalVM native binary. So in the prebuilt `codeanalyzer` native image, `--neo4j-uri` cannot open a Bolt connection — it degrades gracefully to writing `graph.cypher` and logs a warning. The **live Bolt push only happens from the fat JAR** (`java -jar codeanalyzer-2.3.7.jar ...`). Use the JAR wherever you actually push to a server.
+
+
+### `--emit schema`
+
+`--emit schema` prints the machine-readable schema contract — every node label, relationship type, and property the projection can emit — and **requires no project**: it short-circuits before any analysis, so `-i` / `-s` are unnecessary. Writes `schema.neo4j.json` to `-o` if given, otherwise stdout.
+
+```bash
+java -jar codeanalyzer-2.3.7.jar --emit schema -o ./out # ./out/schema.neo4j.json
+java -jar codeanalyzer-2.3.7.jar --emit schema # prints to stdout
+```
+
+### Examples
+
+Write a re-runnable snapshot for one application:
+
+```bash
+java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/daytrader8 -a 2 \
+ --emit neo4j --app-name daytrader8 \
+ -o ./out
+cypher-shell -u neo4j -p "$NEO4J_PASSWORD" < ./out/graph.cypher
+```
+
+Push live and incrementally over Bolt (credentials from the environment):
+
+```bash
+export NEO4J_PASSWORD=secret
+java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/daytrader8 -a 2 \
+ --emit neo4j --app-name daytrader8 \
+ --neo4j-uri bolt://localhost:7687 \
+ --neo4j-user neo4j \
+ --neo4j-database neo4j
+```
+
+Re-push only the files that changed (targeted — no orphan pruning):
+
+```bash
+java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/daytrader8 -a 2 \
+ --emit neo4j --app-name daytrader8 \
+ --neo4j-uri bolt://localhost:7687 \
+ -t src/main/java/com/example/AccountService.java
+```
+
+The `--app-name` you load with is the same value a reader scopes to. In the [Python SDK](/codeanalyzer-java/integration/python-sdk/), `Neo4jConnectionConfig(application_name="daytrader8")` reads back exactly the graph produced by `--app-name daytrader8` — no JDK, no JAR, no project sources, just the Bolt URI and read-only credentials. See the [Neo4j guide](/codeanalyzer-java/guides/neo4j-output/) for the producer/consumer split and the full schema.
+
+## Exit behavior
+
+The command exits non-zero on failure. Run with `-v` to see the underlying logs (build invocation, dependency download, parse problems, WALA progress, and — for a live push — the Bolt connection and per-unit upserts) when diagnosing a failed run.
+
+See worked invocations in [Examples](/codeanalyzer-java/reference/examples/).
diff --git a/src/content/docs/reference/examples.mdx b/src/content/docs/reference/examples.mdx
new file mode 100644
index 00000000..de050f2c
--- /dev/null
+++ b/src/content/docs/reference/examples.mdx
@@ -0,0 +1,255 @@
+---
+title: Examples
+description: Worked codeanalyzer-java invocations — symbol-table runs, full call-graph analysis, single-source mode, incremental updates, Neo4j property-graph output, and reading the graph back from the Python SDK.
+---
+
+import { Aside } from "@astrojs/starlight/components";
+
+A collection of complete, copy-pasteable invocations. Replace `codeanalyzer-2.3.7.jar` with your built JAR path (or `codeanalyzer` if you compiled a native binary).
+
+## Symbol table only (fast, no build)
+
+Parse a project and emit the symbol table. No project build required:
+
+```bash
+java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/commons-cli \
+ -a 1 \
+ -o ./output
+# -> ./output/analysis.json with symbol_table
+```
+
+## Full analysis with call graph
+
+Symbol table plus the WALA call graph. The project is built automatically:
+
+```bash
+java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/commons-cli \
+ -a 2 \
+ -o ./output \
+ -v
+# -> ./output/analysis.json with symbol_table + call_graph
+```
+
+## Single source string (no project, no build)
+
+Analyze a snippet directly; output goes to stdout:
+
+```bash
+java -jar codeanalyzer-2.3.7.jar \
+ -s "public class HelloWorld { public static void main(String[] args) {} }" \
+ -a 1
+```
+
+## Pre-built project (skip the build)
+
+If the project is already compiled, skip the build step for a faster level-2 run:
+
+```bash
+java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/project \
+ -a 2 \
+ --no-build \
+ -o ./output
+```
+
+## Custom build command
+
+Use your own build instead of the auto build:
+
+```bash
+java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/project \
+ -a 2 \
+ -b "mvn -q clean package -DskipTests" \
+ -o ./output
+```
+
+## Incremental analysis (target files)
+
+Re-analyze just two files and patch them into an existing `analysis.json`:
+
+```bash
+java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/project \
+ -t src/main/java/org/apache/commons/cli/Option.java \
+ -t src/main/java/org/apache/commons/cli/Options.java \
+ -o ./output
+```
+
+The named files are tagged `is_modified: true` in the merged output. See [Incremental analysis](/codeanalyzer-java/guides/incremental-analysis/).
+
+## Multi-module project
+
+Analyze a submodule while building from the reactor root:
+
+```bash
+java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/project/web-module \
+ -f /path/to/project/pom.xml \
+ -a 2 \
+ -o ./output
+```
+
+## Pipe stdout into a tool
+
+Omit `-o` to stream JSON to stdout and process it inline:
+
+```bash
+java -jar codeanalyzer-2.3.7.jar -i /path/to/project -a 1 \
+ | jq '.symbol_table | keys | length'
+# prints the number of analyzed source files
+```
+
+## Emit a Neo4j property graph (snapshot)
+
+`--emit neo4j` projects the same IR as `analysis.json` — losslessly — into a Neo4j property graph. With **no Bolt URI present**, the analyzer writes a self-contained, re-runnable `graph.cypher` snapshot to the output directory:
+
+```bash
+java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/daytrader8 \
+ -a 2 \
+ --emit neo4j \
+ --app-name daytrader8 \
+ -o ./out
+# -> ./out/graph.cypher
+```
+
+The snapshot is constraints + indexes, a **scoped wipe** of this application's prior subgraph, then batched `UNWIND ... MERGE` for every node and edge. It expresses the full truth of the application — it is *not* incremental. Load it into a running Neo4j with `cypher-shell`:
+
+```bash
+cypher-shell -u neo4j -p "$NEO4J_PASSWORD" < ./out/graph.cypher
+```
+
+
+`--emit` is an alternative to the default JSON output, not additive: when `--emit neo4j` is set the analyzer projects the graph and returns without writing `analysis.json`. At `-a 2` the projection includes `J_CALLS` call-graph edges; at `-a 1` you get the lossless symbol-table subgraph with no `J_CALLS`.
+
+
+## Push a live graph over Bolt (incremental)
+
+When a Bolt URI resolves — from `--neo4j-uri` or the `NEO4J_URI` env var — `--emit neo4j` pushes **incrementally** to a running Neo4j instead of writing a file. Prefer the `NEO4J_*` environment variables for credentials so secrets stay off the command line:
+
+```bash
+export NEO4J_URI=bolt://localhost:7687
+export NEO4J_USERNAME=neo4j
+export NEO4J_PASSWORD=secret
+
+java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/daytrader8 \
+ -a 2 \
+ --emit neo4j \
+ --app-name daytrader8
+# -> pushes the daytrader8 subgraph to bolt://localhost:7687
+```
+
+The Bolt writer ensures constraints/indexes, diffs each compilation unit's `content_hash` (SHA-256) against the live database, and replaces **only changed units' subgraphs** with idempotent `MERGE` upserts. Shared `:JPackage` / `:JAnnotation` nodes are `MERGE`d in place. On a **full run** (no `-t`), units whose source file vanished are pruned.
+
+
+The live Bolt push runs from the **fat JAR** (`java -jar`). In the prebuilt **GraalVM native binary**, the Neo4j driver is deliberately not bundled, so `--neo4j-uri` degrades gracefully — `codeanalyzer` falls back to writing `graph.cypher` and prints a warning. Use `java -jar` when you want the real Bolt push.
+
+
+You can also pass the flags explicitly instead of the env vars; a flag wins when both are set:
+
+```bash
+java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/daytrader8 \
+ -a 2 \
+ --emit neo4j \
+ --app-name daytrader8 \
+ --neo4j-uri bolt://localhost:7687 \
+ --neo4j-user neo4j \
+ --neo4j-database neo4j
+# password comes from NEO4J_PASSWORD; --neo4j-password also works
+```
+
+`--app-name` is the tenancy key: it sets the `name` of the single `:JApplication` anchor (`(:JApplication {name})-[:J_HAS_UNIT]->(:JCompilationUnit)`). Many applications coexist in one database, each rooted at its own anchor, so a wipe or re-push of `daytrader8` never touches another app's subgraph. If you omit `--app-name`, it defaults to the base name of the `-i` directory.
+
+## Targeted incremental re-push
+
+Combine `-t` with a live Bolt push to update only the units you changed. A targeted run replaces just those changed-unit subgraphs and **skips orphan pruning** — units for files that vanished are left alone, because a targeted run does not claim to know about the whole application:
+
+```bash
+export NEO4J_URI=bolt://localhost:7687
+export NEO4J_PASSWORD=secret
+
+java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/daytrader8 \
+ --emit neo4j \
+ --app-name daytrader8 \
+ -t src/main/java/com/ibm/websphere/samples/daytrader/TradeAction.java
+# replaces only TradeAction's subgraph; vanished units are NOT pruned
+```
+
+
+`-t` forces level 1, so a targeted run does not refresh `J_CALLS` edges. Run a full `-a 2` push when you need the call graph rebuilt across the application.
+
+
+## Emit the schema contract
+
+`--emit schema` publishes the versioned schema contract — every node label, relationship type, and property the projector can produce — with **no project analysis required**. It short-circuits before any analysis runs. Write it to a file:
+
+```bash
+java -jar codeanalyzer-2.3.7.jar --emit schema -o ./out
+# -> ./out/schema.neo4j.json
+```
+
+Or print it to stdout when `-o` is omitted — handy for diffing against a checked-in copy in CI:
+
+```bash
+java -jar codeanalyzer-2.3.7.jar --emit schema \
+ | jq '.schema_version'
+# "1.0.0"
+```
+
+The `schema_version` (`1.0.0`) printed here is the same value stamped on the `:JApplication` node of every graph you emit, so consumers can assert they are reading a contract they understand. See the [Neo4j graph schema](/codeanalyzer-java/schema/neo4j-graph/) for the full label and relationship catalog.
+
+## From Python via CLDK (local analysis)
+
+If you'd rather not manage the JAR yourself, the CLDK SDK invokes it for you:
+
+```python
+from cldk import CLDK
+from cldk.analysis import AnalysisLevel
+
+analysis = CLDK(language="java").analysis(
+ project_path="commons-cli",
+ analysis_level=AnalysisLevel.call_graph,
+)
+print(len(analysis.get_classes()), "classes")
+print(analysis.get_call_graph()) # -> networkx.DiGraph
+```
+
+See [Python SDK integration](/codeanalyzer-java/integration/python-sdk/) for details.
+
+## Read the graph back from Python (no JAR, no JDK)
+
+Once an application is in Neo4j, the SDK can read it directly — no JDK, no native binary, and no project source on the consumer. It only needs the Bolt URI and read-only credentials. Install the driver extra (`pip install cldk[neo4j]`), then point CLDK at the same `application_name` the graph was loaded with:
+
+```python
+# Java application — read-only Neo4j backend
+from cldk import CLDK
+from cldk.analysis import AnalysisLevel
+from cldk.analysis.commons.backend_config import Neo4jConnectionConfig
+
+analysis = CLDK.java(
+ analysis_level=AnalysisLevel.call_graph,
+ backend=Neo4jConnectionConfig(
+ uri="bolt://localhost:7687",
+ username="neo4j",
+ password="neo4j",
+ application_name="daytrader8", # == --app-name from the push
+ ),
+)
+
+symbol_table = analysis.get_symbol_table() # Dict[str, JCompilationUnit]
+cg = analysis.get_call_graph() # networkx.DiGraph
+klass = analysis.get_class("com.example.MyService")
+methods = analysis.get_methods_in_class("com.example.MyService")
+```
+
+The backend bulk-fetches nodes and relationships in a handful of Cypher queries and rebuilds the **same canonical model objects** the in-process analyzer produces — the same `JType` / `JCallable` symbol table and the same `networkx` call graph. Analysis is produced once, centrally, by a `codeanalyzer --emit neo4j` job; every consumer reads it cheaply.
+
+
+`application_name` must equal the `--app-name` the graph was loaded with — it scopes every query to that application's `:JApplication` anchor. Pass read-only credentials; the backend never writes. It is a context manager, so use `with` (or call `.close()`) to release the driver.
+
diff --git a/src/content/docs/schema.mdx b/src/content/docs/schema.mdx
new file mode 100644
index 00000000..54960778
--- /dev/null
+++ b/src/content/docs/schema.mdx
@@ -0,0 +1,137 @@
+---
+title: Output schema overview
+description: The two emit targets — the analysis.json document and the Neo4j property graph — the same IR serialized two ways, and how each is versioned.
+---
+
+import { Aside, CardGrid, LinkCard } from "@astrojs/starlight/components";
+
+codeanalyzer-java extracts one symbol table and call graph per run, then emits that intermediate representation through one of two targets selected by `--emit`:
+
+- **`--emit json`** (the default) — a single `analysis.json` document (or the same JSON on stdout). Self-contained, loaded whole by its consumer.
+- **`--emit neo4j`** — the same IR projected into a **Neo4j property graph**: a `graph.cypher` snapshot, or a live incremental push over Bolt. A queryable, persistent system of record that composes across many applications in one database.
+
+Both targets carry the same model — types, callables, fields, parameters, call sites, variables, enum constants, record components, initialization blocks, CRUD operations, comments, annotations, packages. The graph is a **lossless projection** of the IR, not a summary of it. This page describes the top-level shape of the JSON document and how it relates to the graph; the sub-pages cover each section in detail.
+
+
+`--emit` selects an *alternative* output, not an additive one. `--emit neo4j` projects the IR to the graph and returns **without** writing `analysis.json`; `--emit schema` returns even earlier — it publishes the schema contract and runs no project analysis at all. The default remains `--emit json`.
+
+
+## Top-level shape (`analysis.json`)
+
+```json
+{
+ "symbol_table": {
+ "/absolute/path/to/File.java": { /* JavaCompilationUnit */ }
+ },
+ "call_graph": [ /* edges — present only at analysis level 2 */ ],
+ "version": "2.4.1"
+}
+```
+
+| Key | Type | When present | Description |
+|-----|------|--------------|-------------|
+| `symbol_table` | object | always | Map of absolute file path → compilation unit. See [Symbol table](/codeanalyzer-java/schema/symbol-table/). |
+| `call_graph` | array | level 2 only | Caller→callee edges from WALA. See [Call graph](/codeanalyzer-java/schema/call-graph/). |
+| `version` | string | always | The analyzer version that produced this document, e.g. `"2.4.1"`. |
+
+## Serialization conventions
+
+The JSON is produced by Gson with a fixed configuration:
+
+- **Field naming:** `LOWER_CASE_WITH_UNDERSCORES` — Java fields like `filePath` serialize as `file_path`, `callableDeclarations` as `callable_declarations`, and so on.
+- **Nulls preserved:** `serializeNulls` is on, so absent values appear as explicit `null` rather than being omitted. Consumers can rely on keys existing.
+- **Pretty-printed**, with HTML escaping disabled (so `<`, `>`, `&` in code/strings stay literal).
+
+
+Throughout these schema pages, field names are shown as they appear in the JSON (snake_case). The corresponding Java entity fields are camelCase — `Callable.cyclomaticComplexity` becomes `cyclomatic_complexity` in the output.
+
+
+## The Neo4j projection
+
+The same compilation units, types, and call edges become first-class nodes and relationships in a Neo4j property graph. Every node label is **J-prefixed** (`:JApplication`, `:JCompilationUnit`, `:JType`, `:JCallable`, …) and every relationship type **`J_`-prefixed** (`:J_HAS_UNIT`, `:J_DECLARES_TYPE`, `:J_CALLS`, …). The prefix is deliberate: a Java graph shares one Neo4j database with the Python (`Py*` / `PY_*`) and TypeScript (`TS*` / `TS_*`) backends without colliding, so a polyglot portfolio lives in a single store.
+
+Each run is anchored at a `(:JApplication {name, schema_version})` node — the value of `--app-name` — and every analyzed file hangs off it via `(:JApplication)-[:J_HAS_UNIT]->(:JCompilationUnit)`. Because each application is rooted at its own anchor, many applications coexist side by side and you query across them with Cypher instead of parsing one giant JSON blob per project.
+
+```bash
+# Live incremental push over Bolt (fat jar) — app-scoped, content-hash diffed
+export NEO4J_PASSWORD=secret
+codeanalyzer -i /path/to/project -a 2 \
+ --emit neo4j --app-name daytrader8 \
+ --neo4j-uri bolt://localhost:7687 --neo4j-user neo4j --neo4j-database neo4j
+```
+
+The two `--emit neo4j` sub-modes are decided purely by whether a Bolt URI resolved (the `--neo4j-uri` flag or the `NEO4J_URI` env var):
+
+- **No URI → a `graph.cypher` snapshot.** Self-contained and re-runnable: constraints and indexes, a scoped wipe of *this* application's prior subgraph, then batched `UNWIND … MERGE` of nodes and edges. It expresses full truth, so it is not incremental. Load it with `cypher-shell < graph.cypher`.
+- **URI present → a live incremental Bolt push.** The driver reads the database's current state and updates **only what changed**: it diffs each compilation unit's `content_hash` (a SHA-256 over the unit), replaces only changed units' subgraphs via idempotent `MERGE` upserts, and on a full run prunes units whose source file vanished. Shared `:JPackage` / `:JAnnotation` nodes are upserted `MERGE`-only and left intact across apps.
+
+
+The Bolt writer and the Neo4j driver are intentionally **not** bundled in the GraalVM native binary (they are loaded reflectively, so native-image can prune the driver and Netty). In the prebuilt native binary, `--neo4j-uri` therefore degrades gracefully to writing `graph.cypher` with a warning. The real live Bolt push happens from the fat jar (`java -jar codeanalyzer-.jar …`).
+
+
+For the full node-label and relationship topology — every property on every node, the gating rules, and the constraints and indexes shipped as DDL — see the dedicated reference:
+
+
+
+## Versioning: two contracts, two version fields
+
+The JSON document and the graph each declare their own version, and the two are independent:
+
+| Field | Lives on | Identifies |
+|-------|----------|------------|
+| `version` | the root of `analysis.json` | the **analyzer** that produced the document, e.g. `"2.4.1"`. |
+| `schema_version` | the `:JApplication` node in the graph | the **graph schema contract**, currently `1.0.0`. |
+
+`schema_version` is stamped on the application anchor of every emitted graph, so any consumer can read it back and confirm the contract before traversing. The machine-readable form of that contract is `schema.neo4j.json`, which you can publish directly without analyzing a project:
+
+```bash
+# Print the property-graph schema contract to stdout (no project analysis)
+codeanalyzer --emit schema
+```
+
+Pass `-o ` to write it to `/schema.neo4j.json` instead of stdout. The document enumerates every declared node label, relationship type, and property; the projector is tested to never emit an undeclared label, relationship, or property, so the contract and the graph stay in lockstep.
+
+For the JSON document, the `version` field is the schema identifier: the CLDK Python SDK's Pydantic models (`JApplication`, `JType`, `JCallable`, …) are locked to a compatible version, and your own consumers should treat `version` as the schema key.
+
+When the JSON schema changes incompatibly, `version` bumps. One concrete example: imports changed from bare strings to structured objects (`{ path, is_static, is_wildcard }`). Tools reading an older `analysis.json` against newer code — or vice versa — should check the version. See the [legacy import schema guard](/codeanalyzer-java/guides/incremental-analysis/#schema-compatibility-guard).
+
+
+If you integrate codeanalyzer-java directly (not via the Python SDK), pin to a specific analyzer version and validate the `version` field before deserializing the JSON; for the graph, check `schema_version` on the `:JApplication` node before querying.
+
+
+## Reading either output from the Python SDK
+
+The CLDK Python SDK reconstructs the **same typed model objects** — `JType`, `JCallable`, and a `networkx` call graph — from either output. Point it at a project directory to run the in-process analyzer, or at a Bolt URI to read a graph that was populated out of band. The graph path needs no JDK, no native binary, and no project source on the consumer — only read-only credentials and the `application_name` that matches the `--app-name` the graph was loaded with:
+
+```python
+from cldk import CLDK
+from cldk.analysis import AnalysisLevel
+from cldk.analysis.commons.backend_config import Neo4jConnectionConfig
+
+analysis = CLDK.java(
+ analysis_level=AnalysisLevel.call_graph,
+ backend=Neo4jConnectionConfig(
+ uri="bolt://localhost:7687",
+ username="neo4j",
+ password="neo4j",
+ application_name="daytrader8", # == --app-name on the producing run
+ ),
+)
+symbol_table = analysis.get_symbol_table() # Dict[str, JCompilationUnit]
+cg = analysis.get_call_graph() # networkx.DiGraph
+```
+
+See [Python SDK (CLDK)](/codeanalyzer-java/integration/python-sdk/) for the full read-back API.
+
+## The two sections
+
+These describe the JSON document; the same model is mirrored node-for-node in the graph.
+
+
+
+
+
diff --git a/src/content/docs/schema/call-graph.mdx b/src/content/docs/schema/call-graph.mdx
new file mode 100644
index 00000000..feb59f3c
--- /dev/null
+++ b/src/content/docs/schema/call-graph.mdx
@@ -0,0 +1,126 @@
+---
+title: Call graph schema
+description: The call_graph section of analysis.json — WALA-derived caller→callee edges, their endpoints, and how to load them into a graph library or read them as J_CALLS edges from Neo4j.
+---
+
+import { Aside } from "@astrojs/starlight/components";
+
+At [analysis level 2](/codeanalyzer-java/guides/analysis-levels/), codeanalyzer-java runs WALA over the compiled program and adds a `call_graph` array to the output. Each element is one **caller→callee edge**.
+
+```json
+{
+ "call_graph": [
+ {
+ "source": { "file_path": "...", "type_declaration": "...", "signature": "...", "callable_declaration": "..." },
+ "target": { "file_path": "...", "type_declaration": "...", "signature": "...", "callable_declaration": "..." },
+ "type": "CALL",
+ "weight": "1"
+ }
+ ]
+}
+```
+
+## Edge shape
+
+```typescript
+{
+ source: CallableVertex // The caller
+ target: CallableVertex // The callee
+ type: string // Edge kind, e.g. "CALL"
+ weight: string // Call multiplicity (usually "1")
+}
+```
+
+## Vertex shape (`CallableVertex`)
+
+Both `source` and `target` identify a method or constructor:
+
+```typescript
+{
+ file_path: string // File the callable lives in
+ type_declaration: string // Declaring type
+ signature: string // "methodName(Type1, Type2)"
+ callable_declaration: string // Full declaration text
+}
+```
+
+The `signature` matches the keys used in the symbol table's `callable_declarations`, so you can join an edge endpoint back to its full [callable](/codeanalyzer-java/schema/symbol-table/#callable-jcallable) record (body, complexity, annotations, …).
+
+
+The call graph is a flat array of edges, not a nested tree. To answer "who calls X?" or "what does X reach?", load the edges into a graph structure and query it — see below.
+
+
+## Working with the edges
+
+Because edges are flat, the natural move is to load them into a graph library. The CLDK Python SDK does exactly this, exposing the call graph as a `networkx.DiGraph`:
+
+```python
+from cldk import CLDK
+from cldk.analysis import AnalysisLevel
+import networkx as nx
+
+analysis = CLDK.java(
+ project_path="commons-cli",
+ analysis_level=AnalysisLevel.call_graph, # -> runs with -a 2
+)
+
+cg = analysis.get_call_graph() # networkx.DiGraph
+nx.has_path(cg, source_node, sink_node) # reachability as a graph query
+```
+
+If you consume the JSON directly, the same idea applies — build adjacency from `source` → `target` and run your traversal of choice.
+
+## The same edges in the Neo4j graph
+
+When you emit to Neo4j (`--emit neo4j`) instead of JSON, these edges are projected as a first-class relationship rather than a flat array. Each caller→callee pair becomes a typed `J_CALLS` edge between the two `:JCallable` nodes:
+
+```cypher
+(:JCallable)-[:J_CALLS {type, weight, source_kind, destination_kind}]->(:JCallable)
+```
+
+The edge properties carry the same `type` and `weight` you see in JSON, plus `source_kind` and `destination_kind` describing the endpoints. The endpoints are the same `:JCallable` nodes the symbol-table projection already created — so a call edge and the method bodies it connects live in one graph, queryable together. See the [Neo4j graph schema](/codeanalyzer-java/schema/neo4j-graph/) for the full node-and-relationship reference.
+
+Two projection rules are worth stating plainly, because they shape what you can and can't query:
+
+- **`J_CALLS` exists only at `-a 2`.** Level 1 emits the lossless symbol-table subgraph with types, methods, and fields but no call edges — exactly mirroring a level-1 `analysis.json`. Combining `-t/--target-files` with `-a 2` downgrades the run to level 1, so a targeted incremental push refreshes structure without recomputing `J_CALLS`.
+- **`J_CALLS` is gated to resolved application callables.** A call edge is kept only when *both* endpoints were emitted as `:JCallable` nodes. Calls into the JDK or third-party jars therefore do not appear as `J_CALLS` edges — the same boundary as the in-memory call graph. The projector keys vertices off `callable_declaration` (the raw declaration signature) rather than the display `signature`, which is what lets constructor edges resolve to their target nodes instead of dangling (fix [#158](https://github.com/codellm-devkit/codeanalyzer-java/issues/158)).
+
+### Reachability as a Cypher traversal
+
+The `networkx` `has_path` check above has a direct graph-database analogue. Scope to one application by its `:JApplication` anchor — the `--app-name` the graph was loaded with — and ask Cypher for a path along `J_CALLS`:
+
+```cypher
+MATCH (app:JApplication {name: $appName})
+MATCH (app)-[:J_HAS_UNIT]->(:JCompilationUnit)-[:J_DECLARES_TYPE]->(:JType)
+ -[:J_HAS_CALLABLE]->(src:JCallable {signature: $sourceSig})
+MATCH (dst:JCallable {signature: $sinkSig})
+RETURN exists((src)-[:J_CALLS*1..]->(dst)) AS reachable
+```
+
+Because the graph is persistent and multi-tenant — many applications anchored at their own `:JApplication` in one database — this traversal runs without re-analyzing anything. The CLDK SDK reads the same edges back as a `networkx.DiGraph` over a read-only Neo4j connection, so the Python example above is unchanged except for the backend:
+
+```python
+from cldk import CLDK
+from cldk.analysis import AnalysisLevel
+from cldk.analysis.commons.backend_config import Neo4jConnectionConfig
+import networkx as nx
+
+analysis = CLDK.java(
+ analysis_level=AnalysisLevel.call_graph,
+ backend=Neo4jConnectionConfig(
+ uri="bolt://localhost:7687",
+ username="neo4j",
+ password="neo4j",
+ application_name="daytrader8", # == the --app-name the graph was loaded with
+ ),
+)
+
+cg = analysis.get_call_graph() # networkx.DiGraph, rebuilt from J_CALLS
+nx.has_path(cg, source_node, sink_node) # identical query, no JDK or project source
+```
+
+The analysis is produced once — out of band, by a job running `codeanalyzer -a 2 --emit neo4j` — and read cheaply everywhere after that. Read-only credentials are sufficient on the consumer.
+
+## Why a build is required
+
+WALA analyzes *compiled* classes and needs an entry point to anchor traversal. That's why level 2 builds the project by default ([Build integration](/codeanalyzer-java/guides/build-integration/)) and why a project with no `main` and no recognized framework [entry points](/codeanalyzer-java/frameworks/entry-points/) can yield an empty `call_graph`. See [Troubleshooting](/codeanalyzer-java/troubleshooting/) if that happens.
diff --git a/src/content/docs/schema/neo4j-graph.mdx b/src/content/docs/schema/neo4j-graph.mdx
new file mode 100644
index 00000000..7b3689b4
--- /dev/null
+++ b/src/content/docs/schema/neo4j-graph.mdx
@@ -0,0 +1,240 @@
+---
+title: Neo4j graph schema
+description: The property-graph projection of analysis.json — every node label, relationship type, constraint, and index, plus how the CLDK Python SDK reads the same model back out.
+---
+
+import Neo4jPropertyGraph from '../../../components/Neo4jPropertyGraph.astro';
+import { Aside } from "@astrojs/starlight/components";
+
+`--emit neo4j` projects the same analysis IR you get from `analysis.json` into a **Neo4j property graph** instead of a file. Nothing is dropped: every compilation unit, type, callable, field, parameter, call site, variable, enum constant, record component, initializer block, CRUD operation/query, comment, annotation, and package becomes a first-class node or relationship. This page is the contract for that graph — the labels, the relationships, the keys, and the DDL — and is generated from the same source as the machine-readable [`schema.neo4j.json`](#the-schema-contract).
+
+
+
+Why a graph instead of one big JSON document? An `analysis.json` describes exactly one application and must be loaded whole into memory to be useful. The graph composes: many applications live in one Neo4j database, each anchored at its own `:JApplication` node, and you query across all of them in Cypher rather than parsing giant blobs. Whole-monorepo and cross-service questions become a graph traversal, not a memory problem. The graph is the same lossless model — just persistent, queryable, and shared.
+
+
+This schema is the projection of the [symbol table](/codeanalyzer-java/schema/symbol-table/) and [call graph](/codeanalyzer-java/schema/call-graph/). Node properties carry the same `snake_case` field names you see in `analysis.json` (see [serialization conventions](/codeanalyzer-java/schema/#serialization-conventions)). The `J_CALLS` relationship is the level-2 call graph; everything else is level-1 structure.
+
+
+## Topology at a glance
+
+The graph is rooted at a single `:JApplication` anchor and fans out through the compilation units it owns. The diagram below mirrors the canonical `neo4j-schema.drawio`; the `J_CALLS` edge (drawn dashed) is added only at [analysis level 2](/codeanalyzer-java/guides/analysis-levels/).
+
+```mermaid
+graph LR
+ App[":JApplication"] -->|J_HAS_UNIT| CU[":JCompilationUnit"]
+ CU -->|J_DECLARES_TYPE| T[":JType :JSymbol"]
+ CU -->|J_IMPORTS| Pkg[":JPackage"]
+ T -->|J_HAS_NESTED_TYPE| T
+ T -->|J_HAS_CALLABLE| C[":JCallable :JSymbol"]
+ T -->|J_HAS_FIELD| F[":JField"]
+ T -->|J_EXTENDS / J_IMPLEMENTS| T
+ T -->|J_HAS_ENUM_CONSTANT| EC[":JEnumConstant"]
+ T -->|J_HAS_RECORD_COMPONENT| RC[":JRecordComponent"]
+ T -->|J_HAS_INIT_BLOCK| IB[":JInitializationBlock"]
+ C -->|J_HAS_PARAMETER| P[":JParameter"]
+ C -->|J_HAS_CALLSITE| CS[":JCallSite"]
+ C -->|J_DECLARES_VAR| V[":JVariable"]
+ CS -->|J_RESOLVES_TO| C
+ C -->|"J_CALLS (level 2)"| C
+ T -->|J_ANNOTATED_BY| An[":JAnnotation"]
+ C -->|J_HAS_CRUD_OPERATION| CO[":JCrudOperation"]
+ C -->|J_HAS_CRUD_QUERY| CQ[":JCrudQuery"]
+ C -->|J_HAS_COMMENT| Cm[":JComment"]
+```
+
+The `(:JApplication)-[:J_HAS_UNIT]->(:JCompilationUnit)` edge is the **scoping spine**: every project-owned node is reachable from exactly one application anchor. That is what lets one database host many apps without them colliding — and what `--app-name` controls.
+
+## Node labels
+
+Sixteen node labels, all `J`-prefixed. The `key` column is the property each label MERGEs on; it is enforced by a uniqueness constraint (see [DDL](#constraints-and-indexes)).
+
+| Label | Key | Notable properties |
+|-------|-----|--------------------|
+| `:JApplication` | `name` | `name`, `schema_version` |
+| `:JCompilationUnit` | `file_key` | `file_path`, `package_name`, `content_hash`, `comment_count`, `is_modified`, `_module` |
+| `:JType` | `id` | `fqn`, `name`, `kind`, `modifiers`, `annotations`, `extends_list`, `implements_list`, `is_interface`, `is_entrypoint_class` |
+| `:JCallable` | `id` | `signature`, `return_type`, `parameter_types`, `code`, `cyclomatic_complexity`, `is_constructor`, `is_entrypoint` |
+| `:JField` | `id` | `name`, `type`, `modifiers`, `annotations`, `variables`, `variable_initializers_json`, `_module` |
+| `:JParameter` | `id` | `name`, `type`, `annotations`, `modifiers`, `_module` |
+| `:JVariable` | `id` | `name`, `type`, `initializer`, `_module` |
+| `:JCallSite` | `id` | `method_name`, `receiver_type`, `callee_signature`, `is_static_call`, `is_constructor_call`, `_module` |
+| `:JEnumConstant` | `id` | `name`, `arguments`, `_module` |
+| `:JRecordComponent` | `id` | `name`, `type`, `default_value`, `is_var_args`, `_module` |
+| `:JInitializationBlock` | `id` | `file_path`, `code`, `is_static`, `cyclomatic_complexity`, `_module` |
+| `:JCrudOperation` | `id` | `operation_type`, `target_table`, `involved_columns`, `condition`, `joined_tables`, `_module` |
+| `:JCrudQuery` | `id` | `query_type`, `query_arguments`, `_module` |
+| `:JComment` | `id` | `content`, `is_javadoc`, `_module` |
+| `:JPackage` | `name` | `name` |
+| `:JAnnotation` | `name` | `name` |
+
+### Identity, merge labels, and the entrypoint marker
+
+A few conventions make the keys above unambiguous:
+
+- **`:JType` and `:JCallable` also carry a shared `:JSymbol` label.** That is the global-identity / MERGE key for anything that can be referenced from elsewhere in the graph. A single constraint on `:JSymbol(id)` enforces that no two symbols collide, across all types and callables. A type's `id` is its **fully-qualified name** (`com.example.MyService`); a callable's `id` is `#` (`com.example.MyService#doWork(java.lang.String)`), so overloads stay distinct.
+- **`:JCompilationUnit` keys on `file_key`, which is the file path.** `file_key` is the unique merge key; `file_path` is the same string carried as an ordinary property for convenience.
+- **`:JEntrypoint` is a marker label, not a node type.** It is added to the owning `:JCallable` (or `:JType`) when that element is a `main` method or a recognized [framework entry point](/codeanalyzer-java/frameworks/entry-points/). Find every entry point with `MATCH (c:JEntrypoint)`.
+
+
+Every project-owned leaf node (parameters, variables, call sites, fields, comments, …) carries an internal `_module` property equal to the `file_key` of the compilation unit it came from. It lets the incremental Bolt writer replace exactly one file's subgraph without touching the rest, and lets you trace any node back to its source file in one hop.
+
+
+### Shared vs. app-scoped nodes
+
+`:JPackage` and `:JAnnotation` are keyed only by `name` and are **shared across applications** in the same database — `java.util` is `java.util` for everyone. Every other node hangs off the `:JApplication` spine and belongs to exactly one app. The [per-application wipe](#multi-tenancy-and-the-app-name-anchor) deletes an app's own subgraph while leaving these shared nodes intact.
+
+## Relationship types
+
+Twenty relationship types, all `J_`-prefixed. Endpoints with a `|` accept any of the listed labels.
+
+| Relationship | From → To | Properties |
+|--------------|-----------|------------|
+| `J_HAS_UNIT` | `:JApplication` → `:JCompilationUnit` | — |
+| `J_DECLARES_TYPE` | `:JCompilationUnit` → `:JType` | — |
+| `J_HAS_NESTED_TYPE` | `:JType` → `:JType` | — |
+| `J_HAS_CALLABLE` | `:JType` → `:JCallable` | — |
+| `J_HAS_FIELD` | `:JType` → `:JField` | — |
+| `J_HAS_PARAMETER` | `:JCallable` → `:JParameter` | — |
+| `J_HAS_CALLSITE` | `:JCallable` \| `:JInitializationBlock` → `:JCallSite` | — |
+| `J_DECLARES_VAR` | `:JCallable` \| `:JInitializationBlock` → `:JVariable` | — |
+| `J_HAS_ENUM_CONSTANT` | `:JType` → `:JEnumConstant` | — |
+| `J_HAS_RECORD_COMPONENT` | `:JType` → `:JRecordComponent` | — |
+| `J_HAS_INIT_BLOCK` | `:JType` → `:JInitializationBlock` | — |
+| `J_EXTENDS` | `:JType` → `:JType` | gated |
+| `J_IMPLEMENTS` | `:JType` → `:JType` | gated |
+| `J_ANNOTATED_BY` | `:JType` \| `:JCallable` \| `:JField` → `:JAnnotation` | — |
+| `J_IMPORTS` | `:JCompilationUnit` → `:JType` \| `:JPackage` | `path`, `is_static`, `is_wildcard` |
+| `J_RESOLVES_TO` | `:JCallSite` → `:JCallable` | gated |
+| `J_CALLS` | `:JCallable` → `:JCallable` | `type`, `weight`, `source_kind`, `destination_kind` — gated, level 2 only |
+| `J_HAS_CRUD_OPERATION` | `:JCallable` \| `:JCallSite` → `:JCrudOperation` | — |
+| `J_HAS_CRUD_QUERY` | `:JCallable` \| `:JCallSite` → `:JCrudQuery` | — |
+| `J_HAS_COMMENT` | `:JCompilationUnit` \| `:JType` \| `:JCallable` \| `:JField` \| `:JCallSite` \| `:JVariable` \| `:JRecordComponent` \| `:JInitializationBlock` → `:JComment` | — |
+
+A single-type import (`import com.example.Foo;`) links to the `:JType`; a wildcard or static import links to the `:JPackage`. The `J_IMPORTS` edge carries `path`, `is_static`, and `is_wildcard` so the three cases stay distinguishable.
+
+
+`J_EXTENDS`, `J_IMPLEMENTS`, `J_RESOLVES_TO`, and `J_CALLS` are **gated**: an edge is kept only when *both* endpoints were emitted as nodes. A class that extends a JDK or third-party type whose source isn't in the project gets no `J_EXTENDS` edge (the supertype isn't a node), and a call into a library method gets no `J_CALLS` edge. This mirrors the `analysis.json` call graph, which also only resolves edges between callables it analyzed. `J_CALLS` additionally requires [analysis level 2](/codeanalyzer-java/guides/analysis-levels/) — at level 1 the graph is the lossless symbol table with no resolved call edges.
+
+
+## Constraints and indexes
+
+The writers run this DDL before any load, so every `MERGE` is an index seek rather than a label scan and the identity invariants are enforced by the database. Every statement is idempotent (`IF NOT EXISTS`).
+
+**15 uniqueness constraints** — one per keyed label. The first is the global identity constraint that backs the shared `:JSymbol` merge:
+
+```cypher
+CREATE CONSTRAINT j_symbol_id IF NOT EXISTS FOR (s:JSymbol) REQUIRE s.id IS UNIQUE;
+CREATE CONSTRAINT j_application_name IF NOT EXISTS FOR (a:JApplication) REQUIRE a.name IS UNIQUE;
+CREATE CONSTRAINT j_compilation_unit_key IF NOT EXISTS FOR (c:JCompilationUnit) REQUIRE c.file_key IS UNIQUE;
+-- … plus JPackage.name, JAnnotation.name, and per-id constraints on
+-- JCallSite, JField, JParameter, JVariable, JEnumConstant,
+-- JRecordComponent, JInitializationBlock, JCrudOperation,
+-- JCrudQuery, and JComment (15 total).
+```
+
+**4 indexes**, including a fulltext index over callable source for code search:
+
+```cypher
+CREATE INDEX j_callable_name IF NOT EXISTS FOR (c:JCallable) ON (c.name);
+CREATE INDEX j_type_name IF NOT EXISTS FOR (t:JType) ON (t.name);
+CREATE INDEX j_annotation_name_idx IF NOT EXISTS FOR (an:JAnnotation) ON (an.name);
+CREATE FULLTEXT INDEX j_code_fts IF NOT EXISTS FOR (c:JCallable) ON EACH [c.code, c.docstring];
+```
+
+The `j_code_fts` fulltext index lets you search method bodies and docstrings directly:
+
+```cypher
+CALL db.index.fulltext.queryNodes('j_code_fts', 'executeQuery AND prepareStatement')
+YIELD node, score
+RETURN node.signature, score
+ORDER BY score DESC;
+```
+
+## Multi-tenancy and the `--app-name` anchor
+
+`--app-name` names the single `:JApplication` anchor that scopes everything you push. It defaults to the base name of the `-i` input directory (and is the literal `application` when there is no input). Because the anchor name is unique (`j_application_name`), one Neo4j database can host many applications side by side, each rooted at its own anchor — and you query across them, or scope to one, in Cypher.
+
+```cypher
+// Every callable in one application
+MATCH (a:JApplication {name: 'daytrader8'})-[:J_HAS_UNIT]->(:JCompilationUnit)
+ -[:J_DECLARES_TYPE]->(:JType)-[:J_HAS_CALLABLE]->(c:JCallable)
+RETURN count(c);
+
+// Cross-application: which apps declare a type named "AccountServiceImpl"?
+MATCH (a:JApplication)-[:J_HAS_UNIT]->(:JCompilationUnit)
+ -[:J_DECLARES_TYPE]->(t:JType {name: 'AccountServiceImpl'})
+RETURN a.name, t.fqn;
+```
+
+Re-pushing an app is scoped, too. The `graph.cypher` snapshot wipes only the named app's prior subgraph — `MATCH (a:JApplication {name: })` then `DETACH DELETE` its units and descendants — leaving other apps and the shared `:JPackage` / `:JAnnotation` nodes untouched. The live Bolt writer goes further: it diffs each compilation unit's `content_hash` against the database and replaces **only the changed units' subgraphs**, pruning units whose source file vanished on a full run (pruning is skipped on a `-t` targeted run). See [the two emit modes](#producing-the-graph) below.
+
+
+Because all labels are `J`-prefixed and all relationships `J_`-prefixed, a Java graph shares a database cleanly with the Python (`Py*` / `PY_*`) and TypeScript (`TS*` / `TS_*`) backends — no collisions. A polyglot service's Java, Python, and TypeScript components can live in one graph and be queried together.
+
+
+## Producing the graph
+
+`--emit neo4j` has two sub-modes, decided purely by whether a Bolt URI resolved (the `--neo4j-uri` flag or the `NEO4J_URI` environment variable):
+
+- **No URI → `graph.cypher` snapshot.** A self-contained, re-runnable Cypher script: the DDL above, a scoped wipe of this app's prior subgraph, then batched `UNWIND … MERGE` for nodes and edges. It expresses the *full* truth of the analysis and is **not** incremental. Load it with `cypher-shell`.
+- **URI present → live incremental Bolt push.** The Bolt writer ensures the DDL, content-hash-diffs each compilation unit against the live database, and upserts **only what changed** via idempotent `MERGE`. Shared `:JPackage` / `:JAnnotation` nodes are MERGE-only.
+
+```bash
+# Snapshot: write graph.cypher, then load it
+codeanalyzer -i /path/to/project -a 2 --emit neo4j --app-name daytrader8 -o ./out
+cypher-shell -u neo4j -p "$NEO4J_PASSWORD" < ./out/graph.cypher
+
+# Live incremental push over Bolt (NEO4J_PASSWORD from the environment)
+export NEO4J_PASSWORD=…
+codeanalyzer -i /path/to/project -a 2 --emit neo4j --app-name daytrader8 \
+ --neo4j-uri bolt://localhost:7687 --neo4j-user neo4j --neo4j-database neo4j
+```
+
+`SCHEMA_VERSION` (currently `1.0.0`) is stamped on the `:JApplication` node of every emitted graph, so consumers can check the contract version before querying.
+
+
+The Neo4j driver is deliberately **not** bundled in the GraalVM native image (it is loaded reflectively so native-image can prune the driver and Netty). In the prebuilt native binary, passing `--neo4j-uri` therefore degrades gracefully — it writes `graph.cypher` and logs a warning instead of pushing. The real live Bolt push happens from the fat jar (`java -jar codeanalyzer.jar …`). Use the jar when you need the incremental push.
+
+
+## The schema contract
+
+`--emit schema` prints this entire schema as machine-readable JSON — labels, relationships, constraints, and indexes — without analyzing any project:
+
+```bash
+codeanalyzer --emit schema -o ./out # → ./out/schema.neo4j.json (stdout if -o is omitted)
+```
+
+The document is `schema.neo4j.json` (`schema_version` `1.0.0`). A conformance test asserts the projector never emits a label, relationship, or property the catalog doesn't declare, and that the checked-in `schema.neo4j.json` is current — so this page and the database stay in lockstep with the code.
+
+## Reading the graph from the Python SDK
+
+The big payoff: once a graph exists, the [CLDK Python SDK](/codeanalyzer-java/integration/python-sdk/) reads the **same typed model** back out of Neo4j without re-analyzing anything. There is **no JDK, no native binary, and no project source** on the consumer — only the Bolt URI and read-only credentials. Analysis is produced once, centrally, and read cheaply everywhere.
+
+```python
+# Read-only Neo4j backend — no analyzer engine, no JDK, no project source
+from cldk import CLDK
+from cldk.analysis import AnalysisLevel
+from cldk.analysis.commons.backend_config import Neo4jConnectionConfig
+
+analysis = CLDK.java(
+ analysis_level=AnalysisLevel.call_graph,
+ backend=Neo4jConnectionConfig(
+ uri="bolt://localhost:7687",
+ username="neo4j",
+ password="neo4j", # read-only credentials suffice
+ application_name="daytrader8", # == the --app-name the graph was pushed with
+ ),
+)
+
+symbol_table = analysis.get_symbol_table() # Dict[str, JCompilationUnit]
+cg = analysis.get_call_graph() # networkx.DiGraph
+klass = analysis.get_class("com.example.AccountServiceImpl")
+methods = analysis.get_methods_in_class("com.example.AccountServiceImpl")
+```
+
+The backend bulk-fetches nodes and relationships in a handful of Cypher queries and rebuilds the *same* canonical `JApplication` — symbol table of `JCompilationUnit` plus the `networkx` call graph — that the in-process analyzer produces. The `get_*` methods (`get_classes`, `get_methods`, `get_callers`, `get_callees`, `get_entry_point_methods`, `get_all_crud_operations`, …) return the identical typed objects (`JType`, `JCallable`).
+
+
+`application_name` must equal the `--app-name` the graph was pushed with — it is what scopes every query to the right `:JApplication`. The driver is an optional dependency: `pip install cldk[neo4j]` (or `pip install neo4j`). Because the graph is external, `project_path` is optional for the Neo4j backend, and the connection is a context manager (`with …` / `.close()`). Parity with the in-memory backend holds modulo documented projection-lossy fields (comments collapse to a docstring; call edges to external/library targets may be absent — the same gating described [above](#gated-edges)).
+
diff --git a/src/content/docs/schema/symbol-table.mdx b/src/content/docs/schema/symbol-table.mdx
new file mode 100644
index 00000000..515f6ad0
--- /dev/null
+++ b/src/content/docs/schema/symbol-table.mdx
@@ -0,0 +1,203 @@
+---
+title: Symbol table schema
+description: The full structure of the symbol_table section of analysis.json — compilation units, types, callables, fields, comments, and imports.
+---
+
+import { Aside } from "@astrojs/starlight/components";
+
+The `symbol_table` is the always-present structural model of the program. It is a map from **absolute file path** to a **compilation unit** (one `.java` file). Everything below is serialized in `snake_case` (see [serialization conventions](/codeanalyzer-java/schema/#serialization-conventions)); field types are shown in TypeScript-ish notation for clarity.
+
+
+This page documents the `symbol_table` as it appears in `analysis.json`. The same structure is projected losslessly into a Neo4j property graph with `--emit neo4j` — each entity below becomes a first-class node. See [Neo4j graph schema](/codeanalyzer-java/schema/neo4j-graph/) for the labels, relationships, and constraints, and the [property-graph output guide](/codeanalyzer-java/guides/neo4j-output/) for how to emit it.
+
+
+```json
+{
+ "symbol_table": {
+ "/abs/path/to/Foo.java": { /* JavaCompilationUnit */ }
+ }
+}
+```
+
+## Compilation unit
+
+A single `.java` source file.
+
+```typescript
+{
+ file_path: string // Absolute path
+ package_name: string // Package declaration
+ comments: JComment[] // File-level comments
+ imports: JImport[] // Import declarations
+ type_declarations: { // Top-level types in this file
+ [typeName: string]: JType
+ }
+ is_modified?: boolean // Set on incremental updates
+}
+```
+
+The `is_modified` flag is set when a file is re-analyzed through [incremental target-file analysis](/codeanalyzer-java/guides/incremental-analysis/).
+
+## Type (`JType`)
+
+A class, interface, enum, annotation, or record.
+
+```typescript
+{
+ is_class_or_interface_declaration: boolean
+ is_enum_declaration: boolean
+ is_annotation_declaration: boolean
+ is_record_declaration: boolean
+ is_interface: boolean
+ is_nested_type: boolean
+ is_inner_class: boolean
+ is_local_class: boolean
+
+ modifiers: string[] // "public", "abstract", ...
+ annotations: string[] // "@Deprecated", ...
+
+ extends_list: string[] // Superclass name(s), qualified
+ implements_list: string[] // Implemented interface names
+ parent_type: string | null // For nested / inner types
+ nested_type_declarations: string[] // Names of nested types
+
+ field_declarations: JField[]
+ callable_declarations: { // Methods + constructors
+ [signature: string]: JCallable
+ }
+ enum_constants: JEnumConstant[] // Enum members
+ record_components: JRecordComponent[] // Record components
+ initialization_blocks: JInitializationBlock[]
+ comments: JComment[]
+
+ is_entrypoint_class: boolean // true for main(String[]) classes
+}
+```
+
+The boolean discriminators (`is_enum_declaration`, `is_record_declaration`, …) tell you which kind of type this is. See [Entry points](/codeanalyzer-java/frameworks/entry-points/) for `is_entrypoint_class`.
+
+## Callable (`JCallable`)
+
+A method or constructor. Keyed in `callable_declarations` by its signature.
+
+```typescript
+{
+ signature: string // "methodName(Type1, Type2)"
+ declaration: string // Full declaration with modifiers
+
+ modifiers: string[] // "public", "static", "final", ...
+ annotations: string[]
+ return_type: string | null // null for constructors
+ thrown_exceptions: string[]
+
+ parameters: JParameter[] // Declared parameters
+ comments: JComment[]
+
+ code: string // Method body source
+ file_path: string
+ start_line: number // Span in source
+ end_line: number
+ code_start_line: number // Where the body begins
+
+ call_sites: JCallSite[] // Calls made inside this method
+ referenced_types: string[] // Types referenced in the body
+ accessed_fields: string[] // Fields read or written
+ variable_declarations: JVariableDeclaration[]
+
+ crud_operations: JCRUDOperation[] // Detected DB operations
+ crud_queries: JCRUDQuery[] // Detected query definitions
+
+ cyclomatic_complexity: number
+ is_constructor: boolean
+ is_implicit: boolean // Synthesized (e.g. default ctor)
+ is_entrypoint: boolean // main or framework entry point
+}
+```
+
+`call_sites` give you the syntactic calls within a body even at level 1; the semantic, resolved [call graph](/codeanalyzer-java/schema/call-graph/) is the level-2 `call_graph`. See [CRUD detection](/codeanalyzer-java/frameworks/crud/) for `crud_operations` / `crud_queries`.
+
+## Field (`JField`)
+
+```typescript
+{
+ comments: JComment[]
+ name: string[] // Declared names (supports multi-declarators)
+ type: string // Field type
+ start_line: number
+ end_line: number
+ modifiers: string[]
+ annotations: string[]
+ initializer: string // Initializer expression, if any
+}
+```
+
+## Comment (`JComment`)
+
+```typescript
+{
+ content: string // Comment text
+ start_line: number
+ end_line: number
+ start_column: number
+ end_column: number
+ is_javadoc: boolean // true for /** ... */ blocks
+}
+```
+
+Comments appear both at file level (on the compilation unit and type) and attached to individual callables.
+
+## Import (`JImport`)
+
+```typescript
+{
+ path: string // Fully-qualified name, or package for wildcards
+ is_static: boolean // true for "import static ..."
+ is_wildcard: boolean // true for "import ....*"
+}
+```
+
+
+Imports were previously emitted as bare strings. From 2.3.7 they are structured objects with `path`, `is_static`, and `is_wildcard`. Consumers and existing `analysis.json` files must use the new shape — see the [legacy import guard](/codeanalyzer-java/guides/incremental-analysis/#schema-compatibility-guard).
+
+
+## Other entities
+
+Records, enums, and initializer blocks have their own small entities referenced above:
+
+- **`JEnumConstant`** — an enum member, with its name, arguments, and comments.
+- **`JRecordComponent`** — a record component, with name, type, modifiers, and annotations.
+- **`JInitializationBlock`** — a static or instance initializer block, with its source and span.
+- **`JParameter`** — a callable parameter: name, type, annotations.
+- **`JVariableDeclaration`** — a local variable declared in a method body.
+- **`JCallSite`** — a single call expression inside a body (receiver, method name, argument types, location).
+
+## Projection to the property graph
+
+When you emit with `--emit neo4j`, every entity above maps to a Neo4j node label. The mapping is direct — the graph is a lossless projection of the same IR, so nothing is dropped:
+
+| `symbol_table` entity | Node label | Node key |
+| --- | --- | --- |
+| Compilation unit | `:JCompilationUnit` | `file_key` (the absolute file path) |
+| `JType` | `:JType` (`:JSymbol`) | `id` = the type's `fqn` |
+| `JCallable` | `:JCallable` (`:JSymbol`) | `id` = `#` |
+| `JField` | `:JField` | `id` = `#field#` |
+| `JParameter` | `:JParameter` | `id` |
+| `JVariableDeclaration` | `:JVariable` | `id` |
+| `JCallSite` | `:JCallSite` | `id` |
+| `JEnumConstant` | `:JEnumConstant` | `id` |
+| `JRecordComponent` | `:JRecordComponent` | `id` |
+| `JInitializationBlock` | `:JInitializationBlock` | `id` |
+| `JComment` | `:JComment` | `id` |
+
+`:JType` and `:JCallable` carry the shared `:JSymbol` merge label (one global `id` namespace), and `main(String[])` types and entry-point callables additionally get the `:JEntrypoint` marker.
+
+Containment is expressed as relationships: a compilation unit `J_DECLARES_TYPE` its top-level types, a type `J_HAS_CALLABLE` / `J_HAS_FIELD` / `J_HAS_ENUM_CONSTANT` / `J_HAS_RECORD_COMPONENT` / `J_HAS_INIT_BLOCK` its members, and a callable `J_HAS_PARAMETER` / `J_HAS_CALLSITE` / `J_DECLARES_VAR` its parts. Two things from `analysis.json` are promoted from inline values to shared, deduplicated nodes:
+
+- **Imports.** Each `JImport` becomes a `J_IMPORTS` relationship carrying `path`, `is_static`, and `is_wildcard`. A single-type import links to the imported `:JType`; a wildcard or static import links to a `:JPackage`. Multiple imports from one package stay distinct edges.
+- **Comments and annotations.** Comments become first-class `:JComment` nodes (via `J_HAS_COMMENT`) in addition to a convenience docstring property; annotation strings become shared `:JAnnotation` nodes (via `J_ANNOTATED_BY`) alongside the `annotations` string array.
+
+
+Neo4j property values are scalars or arrays — there is no map type. So a field's per-variable initializer map is serialized to a single `variable_initializers_json` string property on `:JField`. Every project-owned node also carries a `_module` property recording its provenance. These encodings exist only in the graph; `analysis.json` keeps the structured shapes documented above.
+
+
+For the semantic call graph that resolves these call sites across the whole program, continue to the [call graph schema](/codeanalyzer-java/schema/call-graph/).
diff --git a/src/content/docs/troubleshooting.mdx b/src/content/docs/troubleshooting.mdx
new file mode 100644
index 00000000..0421900c
--- /dev/null
+++ b/src/content/docs/troubleshooting.mdx
@@ -0,0 +1,176 @@
+---
+title: Troubleshooting
+description: Common codeanalyzer-java problems and fixes — missing JDK, unresolved types, empty call graphs, legacy import schema, native-image quirks, and Neo4j graph output.
+---
+
+import { Aside } from "@astrojs/starlight/components";
+
+Quick fixes for the issues you're most likely to hit. Run with `-v` first — most failures explain themselves in the verbose logs.
+
+## Reference table
+
+| Symptom | Likely cause | Fix |
+|---------|--------------|-----|
+| `java: command not found` | No JDK on `PATH` | Install Java 11+ and add it to `PATH` (see [Installation](/codeanalyzer-java/installing/)). |
+| `Cannot find symbol type X` / unresolved types | Dependencies not downloaded | Let codeanalyzer build (don't use `--no-build` on an uncompiled project), or ensure dependencies are resolvable. Use `--no-clean-dependencies` to inspect what was fetched. |
+| `call_graph` is empty | No entry point for WALA to anchor on | Verify the project has a `main(String[])` or a recognized framework [entry point](/codeanalyzer-java/frameworks/entry-points/). |
+| Legacy import schema error | Old `analysis.json` read by a new JAR | Regenerate `analysis.json` with codeanalyzer 2.3.7+ (see below). |
+| Build fails during level-2 analysis | Project doesn't build with the default command | Pass a working `-b ""`, or pre-build and use `--no-build`. |
+| Native binary throws, JAR doesn't | Stale `reflect-config.json` | Regenerate native-image config (see below). |
+| `analysis.json` truncated / corrupt | Process killed or out of disk | Check disk space and re-run; ensure the run completes. |
+| `--neo4j-uri` ignored, `graph.cypher` written instead | Running the native binary (no bundled driver) | Push over Bolt from the fat JAR, `java -jar` (see below). |
+| Graph empty / wrong app when reading from CLDK | `application_name` doesn't match the `--app-name` the graph was loaded with | Pass the same name in `Neo4jConnectionConfig` (see below). |
+| Stale or duplicate nodes after a rename | Snapshot wipe / Bolt prune are scoped per app; deletes only pruned on a full run | Re-run without `-t`, or re-load the snapshot (see below). |
+| Bolt push: `authentication failure` / wrong database | Default `neo4j`/`neo4j` credentials or unset `NEO4J_*` env | Set `NEO4J_PASSWORD` / `--neo4j-database` (see below). |
+| `J_CALLS` edges missing from the graph | Ran at level 1, or callee is an external library symbol | Run `-a 2` on a full (non-`-t`) analysis (see below). |
+
+## Empty call graph
+
+WALA traverses the call graph outward from entry points. If none are found, the graph can be empty even though the symbol table is complete.
+
+- Confirm the project actually has an entry point — a `main(String[])` or one of the [supported framework patterns](/codeanalyzer-java/frameworks/entry-points/).
+- Confirm the project **built**. WALA needs compiled classes; if the build silently failed, there's nothing to analyze. Re-run with `-v` and check the build log.
+- If you used `--no-build`, make sure the compiled output is actually present and current.
+
+## Unresolved types
+
+Symbol resolution depends on library dependencies being available. If types from third-party libraries show up as simple (unqualified) names:
+
+- Don't run `--no-build` on a project that isn't already built — dependency download is part of the normal flow.
+- Inspect what was fetched by adding `--no-clean-dependencies` and looking in `target/_library_dependencies/` (Maven) or `build/_library_dependencies/` (Gradle).
+- For multi-module projects, point `-f` at the reactor root `pom.xml` / `build.gradle`. See [Build integration](/codeanalyzer-java/guides/build-integration/).
+
+## Legacy import schema
+
+When merging [incremental updates](/codeanalyzer-java/guides/incremental-analysis/) into an existing `analysis.json`, you may see:
+
+> Existing analysis.json uses legacy import schema (imports as strings). Regenerate analysis with codeanalyzer 2.3.7 or newer.
+
+Older analyzers emitted imports as bare strings; from 2.3.7 they are structured objects (`{ path, is_static, is_wildcard }`). The merge guard refuses to mix the two. **Fix:** delete the old `analysis.json` and regenerate it with a current JAR before re-running incremental updates.
+
+## Native-image exceptions
+
+If the GraalVM [native binary](/codeanalyzer-java/installing/#option-2-native-binary-graalvm) throws random exceptions that `java -jar` does not, the reflection config is likely out of date. Regenerate it with the native-image agent and rebuild:
+
+```bash
+./gradlew fatJar
+java -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image-config \
+ -jar build/libs/codeanalyzer-2.3.7.jar -i -a 2 -v
+./gradlew nativeCompile
+```
+
+## Neo4j graph output
+
+These cover the [`--emit neo4j`](/codeanalyzer-java/guides/neo4j-output/) projection — the live Bolt push, the `graph.cypher` snapshot, and reading the graph back from the [CLDK Python SDK](/codeanalyzer-java/integration/python-sdk/).
+
+### `--neo4j-uri` is ignored and a `graph.cypher` file is written instead
+
+The Neo4j driver is deliberately **not** bundled into the GraalVM native binary — it's loaded reflectively so native-image can prune the driver and Netty. The prebuilt `codeanalyzer` native binary therefore cannot open a Bolt connection: when you pass `--neo4j-uri` it **degrades gracefully to writing `graph.cypher`** and logs a warning, rather than pushing anything live.
+
+If you actually wanted the live, incremental push, run it from the fat JAR instead:
+
+```bash
+NEO4J_PASSWORD=secret java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/project -a 2 \
+ --emit neo4j --app-name daytrader8 \
+ --neo4j-uri bolt://localhost:7687 --neo4j-user neo4j
+```
+
+This is the producer side of the [producer/consumer split](/codeanalyzer-java/guides/architecture/): the JAR runs out-of-band (a CI step, or a Kubernetes Job / CronJob) and pushes app-scoped subgraphs to a shared Neo4j over Bolt. If you only need the snapshot — for review, version control, or an air-gapped load — the native binary's degraded behaviour is fine; just load the file afterwards with `cypher-shell < graph.cypher`.
+
+
+The choice is made purely by whether a Bolt URI resolved (the `--neo4j-uri` flag or the `NEO4J_URI` env var). No URI → a `graph.cypher` snapshot. URI present **and running from the JAR** → a live Bolt push. URI present **but running the native binary** → the warning above and a `graph.cypher` snapshot.
+
+
+### Graph is empty, or you get the wrong app, when reading from CLDK
+
+The Neo4j-backed SDK is a pure read-only client — it never builds the graph, it only queries one that a `codeanalyzer --emit neo4j` job has already populated. Every query is scoped to a single application by `application_name`, which **must equal the `--app-name` the graph was loaded with**. A mismatch silently returns nothing, because the query anchors on a `:JApplication` node that doesn't exist.
+
+```python
+from cldk import CLDK
+from cldk.analysis import AnalysisLevel
+from cldk.analysis.commons.backend_config import Neo4jConnectionConfig
+
+analysis = CLDK.java(
+ analysis_level=AnalysisLevel.call_graph,
+ backend=Neo4jConnectionConfig(
+ uri="bolt://localhost:7687",
+ username="neo4j",
+ password="neo4j",
+ application_name="daytrader8", # == the CLI --app-name
+ ),
+)
+symbol_table = analysis.get_symbol_table() # Dict[str, JCompilationUnit]
+cg = analysis.get_call_graph() # networkx.DiGraph
+```
+
+Checklist:
+
+- **Match the name exactly.** Remember the default: when you populated the graph without `--app-name`, the anchor took the base name of the `-i` input directory (or the literal `application` if there was no input). Confirm what's actually in the database:
+
+ ```cypher
+ MATCH (a:JApplication) RETURN a.name, a.schema_version
+ ```
+
+- **Confirm the graph was populated at all.** If the producer ran the native binary with `--neo4j-uri`, it may have written `graph.cypher` instead of pushing (see above) — nothing reached the database.
+- **Install the driver.** Constructing the backend without it raises `CodeanalyzerExecutionException` with an install hint. Run `pip install cldk[neo4j]` (or `pip install neo4j`).
+- **Check the schema version.** The read-back expects an emitter at **2.4.0 or newer** (projection fixes landed in **2.4.1**). The `schema_version` stamped on the `:JApplication` node is `1.0.0`; keep the analyzer that writes the graph and the SDK that reads it on compatible versions.
+
+
+`project_path` is optional for the Neo4j backend — the SDK needs only the Bolt URI and read-only credentials, no JDK, binary, or source. But if you do pass `project_path` and omit `application_name`, the backend falls back to `Path(project_path).name`; if it still can't resolve a name it raises `application_name is required to scope queries to an application.`
+
+
+### Stale or duplicate nodes after a rename or delete
+
+Both emit modes are **scoped per application**, and they handle vanished files differently:
+
+- The **`graph.cypher` snapshot** opens with a wipe that deletes *only this app's* prior subgraph — `MATCH (a:JApplication {name: })` then detaches its units and descendants — before re-loading the full truth. Shared `:JPackage` / `:JAnnotation` nodes are intentionally left in place so other apps that reference them aren't disturbed. Re-loading the snapshot is therefore always a clean slate for that one app.
+- The **live Bolt push** is incremental. It diffs each compilation unit's `content_hash` and replaces only changed units' subgraphs. Orphan pruning — deleting units whose source file has vanished — runs **only on a full analysis**. A **targeted run (`-t`) skips pruning** so it can touch just the files you named, which means a deleted or renamed file's old nodes can linger.
+
+So if you renamed or deleted a class and its old nodes are still in the graph:
+
+- Re-run a **full** analysis (no `-t`) over Bolt so orphan pruning removes the vanished unit:
+
+ ```bash
+ NEO4J_PASSWORD=secret java -jar codeanalyzer-2.3.7.jar \
+ -i /path/to/project -a 2 \
+ --emit neo4j --app-name daytrader8 \
+ --neo4j-uri bolt://localhost:7687 --neo4j-user neo4j
+ ```
+
+- Or regenerate and re-load the `graph.cypher` snapshot, whose scoped wipe clears this app's subgraph wholesale before reloading.
+
+Renames produce *duplicates* (not just staleness) precisely because the old FQN and the new one are distinct node keys — the upsert can't know they're the same logical unit. A full run reconciles them.
+
+### Bolt connection or authentication failures
+
+Credentials and target database resolve from a flag first, then the matching environment variable, then a built-in default. Prefer the `NEO4J_*` environment variables — especially `NEO4J_PASSWORD` — so secrets stay off the command line and out of shell history.
+
+| Setting | Flag | Env var | Default |
+|---------|------|---------|---------|
+| URI | `--neo4j-uri` | `NEO4J_URI` | *(none — without it, no live push)* |
+| User | `--neo4j-user` | `NEO4J_USERNAME` | `neo4j` |
+| Password | `--neo4j-password` | `NEO4J_PASSWORD` | `neo4j` |
+| Database | `--neo4j-database` | `NEO4J_DATABASE` | *(server default)* |
+
+- An `authentication failure` usually means the run fell back to the `neo4j`/`neo4j` defaults against a server with real credentials. Export `NEO4J_PASSWORD` (and `NEO4J_USERNAME` if it isn't `neo4j`) before running.
+- A "database not found" error means `--neo4j-database` / `NEO4J_DATABASE` names a database that doesn't exist on the server. Leave it unset to use the server default, or create the database first.
+- The analyzer only needs write access. The SDK consumers that read the graph can — and in production should — use separate **read-only** credentials, which is the point of the producer/consumer split.
+
+### `J_CALLS` edges are missing
+
+`J_CALLS` is the level-2 call-graph edge between two application callables. If your queries find types and methods but no `J_CALLS` relationships:
+
+- **Run at `-a 2`.** Level 1 emits the lossless symbol-table subgraph with no `J_CALLS`. Only level 2 adds the WALA call-graph edges.
+- **Don't combine `-t` with `-a 2`.** A targeted run downgrades to level 1, so it refreshes the symbol-table subgraph but recomputes no call edges. Run a full `-a 2` analysis to (re)build `J_CALLS`.
+- **Expect gaps at the boundary.** `J_CALLS` is gated to both endpoints being emitted application callables. Calls into external/library targets that were never emitted as `:JCallable` nodes have no edge — this is documented projection-lossy behaviour, not a bug. Verify with:
+
+ ```cypher
+ MATCH (a:JApplication {name: "daytrader8"})-[:J_HAS_UNIT]->(:JCompilationUnit)
+ -[:J_DECLARES_TYPE]->(:JType)-[:J_HAS_CALLABLE]->(c:JCallable)
+ RETURN count { (c)-[:J_CALLS]->() } AS outgoing_calls
+ ```
+
+
+Still stuck? Re-run with `-v` and open an [issue](https://github.com/codellm-devkit/codeanalyzer-java/issues) with the command you ran, the verbose output, and the project's build system (Maven/Gradle).
+
diff --git a/src/content/docs/what-is-codeanalyzer.mdx b/src/content/docs/what-is-codeanalyzer.mdx
new file mode 100644
index 00000000..d5984e14
--- /dev/null
+++ b/src/content/docs/what-is-codeanalyzer.mdx
@@ -0,0 +1,129 @@
+---
+title: What is codeanalyzer-java?
+description: The mental model — a standalone JVM tool that turns a Java project into one versioned JSON document, or projects it into a Neo4j property graph, combining a Javaparser symbol table and a WALA call graph.
+---
+
+import Neo4jPropertyGraph from '../../components/Neo4jPropertyGraph.astro';
+import { Aside, LinkCard, CardGrid } from "@astrojs/starlight/components";
+
+**codeanalyzer-java** is a standalone, self-contained JVM tool that performs static analysis on enterprise Java applications. You hand it a project (or a single source string); it extracts a comprehensive symbol table and, at analysis level 2, an interprocedural call graph — and emits them either as the canonical `analysis.json` document or as a **Neo4j property graph** (`--emit neo4j`).
+
+It is the JVM analysis engine behind [CodeLLM-DevKit (CLDK)](https://github.com/codellm-devkit/python-sdk)'s Java support. The Python SDK does not re-implement Java analysis — it either shells out to this binary and deserializes the JSON into typed models, or, when you point it at Neo4j, reads the same typed models straight from the graph. You can also run the binary directly and consume the output yourself.
+
+
+
+## The mental model
+
+```mermaid
+flowchart LR
+ A["Java project (or source string)"] --> B["codeanalyzer (fat JAR / native image)"]
+ B --> IR["analysis IR (symbol table + call graph)"]
+ IR -->|"default"| C["analysis.json"]
+ IR -->|"--emit neo4j"| G["Neo4j property graph (J* labels, J_* relationships)"]
+ C --> D["CLDK Python SDK (JavaAnalysis)"]
+ C --> E["Your own tooling / agent / script"]
+ G -->|"no URI"| SNAP["graph.cypher snapshot (scoped wipe + UNWIND ... MERGE)"]
+ G -->|"--neo4j-uri (Bolt)"| LIVE["live Neo4j cluster (incremental content_hash diff)"]
+ SNAP -->|"cypher-shell < graph.cypher"| LIVE
+ LIVE --> SDK["CLDK Neo4j backend (read-only, no JDK / source)"]
+ LIVE --> Q["Cypher / dashboards / agents"]
+```
+
+There are a few things to keep straight:
+
+1. **The project** — the Java source you want to understand. codeanalyzer can resolve types either from a built project's dependencies or from a single in-memory source string.
+2. **The binary** — `codeanalyzer`, a single fat JAR (or a GraalVM native image) bundling WALA, Javaparser, and everything else. No server, no database are required *to run it*; it reads source/binaries and writes its output.
+3. **The output** — by default a versioned `analysis.json` with `symbol_table` and (at analysis level 2) `call_graph` keys. With `--emit neo4j`, the same intermediate representation is instead projected into a Neo4j property graph — losslessly, as first-class nodes and relationships.
+
+## Two analysis engines, one IR, two outputs
+
+codeanalyzer-java combines two complementary static-analysis technologies:
+
+- **Javaparser + Symbol Solver** does the *syntactic* work — parsing `.java` files into ASTs and resolving types — to build the **symbol table**: every class, interface, enum, and record, with their fields, methods, constructors, comments, and imports.
+- **WALA** (the T.J. Watson Libraries for Analysis) does the *semantic* work — building a class hierarchy and an interprocedural **call graph** from the compiled program.
+
+The symbol table is always produced. The call graph is produced only when you ask for analysis level 2 (`-a 2`). That single intermediate representation is then serialized one of two ways — and the output target is an *alternative*, not additive:
+
+- `--emit json` (the default) writes `analysis.json`.
+- `--emit neo4j` projects the IR into a graph instead and returns **without** writing `analysis.json`.
+
+See [Architecture](/codeanalyzer-java/guides/architecture/) and [Analysis levels](/codeanalyzer-java/guides/analysis-levels/).
+
+
+Both outputs are versioned contracts. Each `analysis.json` carries a `version` field (e.g. `"2.3.7"`), and every emitted graph stamps `schema_version` `1.0.0` on its `:JApplication` anchor node. That stability is what lets CLDK's models — and your own consumers — deserialize the output reliably across runs, across backends, and across languages. You can publish the graph contract on its own with `--emit schema`, which prints `schema.neo4j.json` and requires no project analysis at all.
+
+
+## The Neo4j property graph
+
+`--emit neo4j` turns a per-project JSON file into a **queryable, persistent system of record**. The graph is a lossless projection of the IR: compilation units, types, callables, fields, parameters, call sites, variables, enum constants, record components, initialization blocks, CRUD operations and queries, comments, annotations, and packages all become first-class nodes. Java labels are `J`-prefixed and relationship types `J_`-prefixed (e.g. `(:JApplication)-[:J_HAS_UNIT]->(:JCompilationUnit)`, `(:JCallable)-[:J_CALLS]->(:JCallable)`), so a Java graph can share one Neo4j database with the Python (`Py*`/`PY_*`) and TypeScript (`TS*`/`TS_*`) backends without colliding.
+
+Every application is anchored at its own `:JApplication` node, keyed by `--app-name`. That anchor is the **tenancy boundary**: one Neo4j database can host many applications side by side, each rooted at its own `:JApplication`, and you query across all of them with Cypher instead of loading giant JSON blobs into memory. Whole-monorepo and cross-service analysis becomes a graph traversal, not a memory problem.
+
+There are two emit modes, decided purely by whether a Bolt URI resolves (from `--neo4j-uri` or the `NEO4J_URI` env var):
+
+- **No URI → a `graph.cypher` snapshot.** Self-contained and re-runnable: constraints and indexes, a scoped wipe of *this* application's prior subgraph, then batched `UNWIND ... MERGE` loads of the full truth. The snapshot is not incremental — it expresses the whole current state — and you load it with `cypher-shell < graph.cypher`.
+- **URI present → a live, incremental Bolt push.** The writer reads the database's current state and updates only what changed: it diffs each compilation unit's `content_hash` (SHA-256 over the unit) against the live DB, replaces only changed units' subgraphs via idempotent `MERGE` upserts, upserts shared `:JPackage`/`:JAnnotation` nodes MERGE-only, and on a full run prunes units whose source file has vanished (orphan pruning is skipped on a targeted `-t` run).
+
+```bash
+# Snapshot: no URI, writes ./graph.cypher
+codeanalyzer -i /path/to/daytrader8 -a 2 --emit neo4j --app-name daytrader8
+
+# Live incremental push over Bolt (prefer the NEO4J_PASSWORD env var)
+export NEO4J_PASSWORD=secret
+codeanalyzer -i /path/to/daytrader8 -a 2 --emit neo4j \
+ --app-name daytrader8 \
+ --neo4j-uri bolt://localhost:7687 \
+ --neo4j-user neo4j \
+ --neo4j-database neo4j
+```
+
+
+The Neo4j driver is deliberately **not** bundled in the GraalVM native binary (it is loaded reflectively so native-image can prune the driver and Netty). In the prebuilt native binary, `--neo4j-uri` therefore degrades gracefully to writing a `graph.cypher` snapshot, with a warning. The real live Bolt push happens from the fat jar (`java -jar`). Use the jar in your producer job when you want incremental pushes.
+
+
+Once the graph exists, the Python SDK can read from it instead of re-analyzing. Point CLDK at the Bolt URI with a `Neo4jConnectionConfig` and it reconstructs the **same** typed `JApplication` (symbol table + `networkx` call graph) as the in-process analyzer — with no JDK, no native binary, and no project source on the consumer. It only needs the graph and read-only credentials. The `application_name` here must match the `--app-name` the graph was loaded with:
+
+```python
+# Java project — read-only Neo4j backend (no JDK, no binary, no source)
+from cldk import CLDK
+from cldk.analysis import AnalysisLevel
+from cldk.analysis.commons.backend_config import Neo4jConnectionConfig
+
+analysis = CLDK.java(
+ analysis_level=AnalysisLevel.call_graph,
+ backend=Neo4jConnectionConfig(
+ uri="bolt://localhost:7687",
+ username="neo4j",
+ password="neo4j", # read-only credentials suffice
+ application_name="daytrader8", # == the producer's --app-name
+ ),
+)
+symbol_table = analysis.get_symbol_table() # Dict[str, JCompilationUnit]
+cg = analysis.get_call_graph() # networkx.DiGraph
+klass = analysis.get_class("com.example.MyService")
+methods = analysis.get_methods_in_class("com.example.MyService")
+```
+
+This is the enterprise unlock: analysis is **produced once, centrally** (a CI / Kubernetes Job or CronJob running the jar pushes app-scoped subgraphs into a shared cluster) and **read cheaply everywhere** (agents, the SDK, and dashboards are lightweight read-only clients that scale independently of the heavier analysis pods). See [Reading from Neo4j with the Python SDK](/codeanalyzer-java/integration/python-sdk/) and the [Neo4j graph schema](/codeanalyzer-java/schema/).
+
+## What it is good at
+
+- **Enterprise Java** — it understands Maven and Gradle projects, downloads dependencies for type resolution, and recognizes Spring, JAX-RS, Struts, and Servlet entry points.
+- **Structured output for tools** — the output is meant to be consumed by code, not read by humans. Method bodies, source spans, cyclomatic complexity, call sites, and accessed fields are all captured.
+- **Reachability groundwork** — the call graph is explicit caller→callee edges, ready to load into a graph library and query.
+- **Graph-native consumption at portfolio scale** — with `--emit neo4j`, reachability and cross-service queries run as Cypher traversals over a persistent, multi-application graph. A fulltext index (`j_code_fts`) over callable code and docstrings makes the same graph searchable, and read-only credentials / RBAC let many consumers depend on it as governed infrastructure.
+
+## What it is not
+
+- It is **not** a linter or a bug finder — it extracts facts, it does not pass judgment.
+- It is **not** an incremental *server* — each invocation is a batch run. The Bolt push is still a batch invocation; it just updates the graph *incrementally* (content-hash diff, only changed units re-pushed) instead of rewriting it. The default JSON path writes a fresh `analysis.json` each run, though [incremental target-file analysis](/codeanalyzer-java/guides/incremental-analysis/) can patch an existing one.
+- It does **not** require the Python SDK — that is one consumer among several. And the SDK, in turn, does **not** require this binary when it reads from Neo4j: the graph is populated out of band, and the SDK only polls it.
+
+## Next steps
+
+
+
+
+
+
+
diff --git a/src/main/java/com/ibm/cldk/CodeAnalyzer.java b/src/main/java/com/ibm/cldk/CodeAnalyzer.java
deleted file mode 100644
index 37eb7799..00000000
--- a/src/main/java/com/ibm/cldk/CodeAnalyzer.java
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
-Copyright IBM Corporation 2023, 2024
-
-Licensed under the Apache Public License 2.0, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-
-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.
- */
-package com.ibm.cldk;
-
-import com.github.javaparser.Problem;
-import com.google.common.reflect.TypeToken;
-import com.google.gson.FieldNamingPolicy;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import com.ibm.cldk.entities.JavaCompilationUnit;
-import com.ibm.cldk.utils.BuildProject;
-import com.ibm.cldk.utils.Log;
-import java.io.File;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.lang.reflect.Type;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-import org.apache.commons.lang3.tuple.Pair;
-import picocli.CommandLine;
-import picocli.CommandLine.Command;
-import picocli.CommandLine.Option;
-
-class VersionProvider implements CommandLine.IVersionProvider {
-
- public String[] getVersion() throws Exception {
- String version = getClass().getPackage().getImplementationVersion();
- return new String[] { version != null ? version : "unknown" };
- }
-}
-
-/**
- * The type Code analyzer.
- */
-@Command(name = "codeanalyzer", mixinStandardHelpOptions = true, sortOptions = false, versionProvider = VersionProvider.class, description = "Analyze java application.")
-public class CodeAnalyzer implements Runnable {
-
- @Option(names = { "-i", "--input" }, description = "Path to the project root directory.")
- private static String input;
-
- @Option(names = { "-t",
- "--target-files" }, description = "Paths to files to be analyzed from the input application.")
- private static List targetFiles;
-
- @Option(names = { "-s",
- "--source-analysis" }, description = "Analyze a single string of java source code instead the project.")
- private static String sourceAnalysis;
-
- @Option(names = { "-o",
- "--output" }, description = "Destination directory to save the output graphs. By default, the SDG formatted as a JSON will be printed to the console.")
- private static String output;
-
- @Option(names = { "-b", "--build-cmd" }, description = "Custom build command. Defaults to auto build.")
- private static String build;
-
- @Option(names = {
- "--no-build" }, description = "Do not build your application. Use this option if you have already built your application.")
- private static boolean noBuild = false;
-
- @Option(names = { "--no-clean-dependencies" }, description = "Do not attempt to auto-clean dependencies")
- public static boolean noCleanDependencies = false;
-
- @Option(names = { "-f",
- "--project-root-path" }, description = "Path to the root pom.xml/build.gradle file of the project.")
- public static String projectRootPom;
-
- @Option(names = { "-a",
- "--analysis-level" }, description = "Level of analysis to perform. Options: 1 (for just symbol table); 2 (for call graph). Default: 1")
- public static int analysisLevel = 1;
-
- @Option(names = { "--include-test-classes" }, hidden = true, description = "Print logs to console.")
- public static boolean includeTestClasses = false;
-
- @Option(names = { "-v", "--verbose" }, description = "Print logs to console.")
- private static boolean verbose = false;
-
- private static final String outputFileName = "analysis.json";
-
- public static Gson gson = new GsonBuilder()
- .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
- .setPrettyPrinting()
- .serializeNulls() // Fix for issue #108
- .disableHtmlEscaping()
- .create();
-
- /**
- * The entry point of application.
- *
- * @param args the input arguments
- */
- public static void main(String[] args) {
- int exitCode = new CommandLine(new CodeAnalyzer()).execute(args);
- System.exit(exitCode);
- }
-
- @Override
- public void run() {
- // Set log level based on quiet option
- Log.setVerbosity(verbose);
- try {
- analyze();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- private static void analyze() throws Exception {
-
- JsonObject combinedJsonObject = new JsonObject();
- Map symbolTable;
- projectRootPom = projectRootPom == null ? input : projectRootPom;
- // First of all if, sourceAnalysis is provided, we will analyze the source code
- // instead of the project.
- if (sourceAnalysis != null) {
- // Construct symbol table for source code
- Log.debug("Single file analysis.");
- Pair, Map>> symbolTableExtractionResult = SymbolTable
- .extractSingle(sourceAnalysis);
- symbolTable = symbolTableExtractionResult.getLeft();
- } else {
- // download library dependencies of project for type resolution
- String dependencies = null;
- try {
- if (BuildProject.downloadLibraryDependencies(input, projectRootPom)) {
- dependencies = String.valueOf(BuildProject.libDownloadPath);
- } else {
- Log.warn("Failed to download library dependencies of project");
- }
- } catch (IllegalStateException illegalStateException) {
- Log.warn("Failed to download library dependencies of project");
- }
-
- boolean analysisFileExists = output != null
- && Files.exists(Paths.get(output + File.separator + outputFileName));
-
- // if target files are specified, compute symbol table information for the given
- // files
- if (targetFiles != null) {
- Log.info(targetFiles.size() + "target files specified for analysis: " + targetFiles);
-
- // if target files specified for analysis level 2, downgrade to analysis level 1
- if (analysisLevel > 1) {
- Log.warn("Incremental analysis is supported at analysis level 1 only; "
- + "performing analysis level 1 for target files");
- analysisLevel = 1;
- }
-
- // Previous code was pointing to toList, which has been introduced in Java 16
- // symbolTable = SymbolTable.extract(Paths.get(input),
- // targetFiles.stream().map(Paths::get).toList()).getLeft();
- // extract symbol table for the specified files
- symbolTable = SymbolTable
- .extract(Paths.get(input), targetFiles.stream().map(Paths::get).collect(Collectors.toList()))
- .getLeft();
-
- // if analysis file exists, update it with new symbol table information for the
- // specified fiels
- if (analysisFileExists) {
- // read symbol table information from existing analysis file
- Map existingSymbolTable = readSymbolTableFromFile(
- new File(output, outputFileName));
- if (existingSymbolTable != null) {
- // for each file, tag its symbol table information as "updated" and update
- // existing symbol table
- for (String targetFile : targetFiles) {
- String targetPathAbs = Paths.get(targetFile).toAbsolutePath().toString();
- JavaCompilationUnit javaCompilationUnit = symbolTable.get(targetPathAbs);
- javaCompilationUnit.setModified(true);
- existingSymbolTable.put(targetPathAbs, javaCompilationUnit);
- }
- }
- symbolTable = existingSymbolTable;
- }
- } else {
- // construct symbol table for project, write parse problems to file in output
- // directory if specified
- Pair, Map>> symbolTableExtractionResult = SymbolTable
- .extractAll(Paths.get(input));
-
- symbolTable = symbolTableExtractionResult.getLeft();
- }
-
- if (analysisLevel > 1) {
- // Save SDG, and Call graph as JSON
- // If noBuild is not true, and build is also not provided, we will use "auto" as
- // the build command
- build = build == null ? "auto" : build;
- // Is noBuild is true, we will not build the project
- build = noBuild ? null : build;
- List sdgEdges = SystemDependencyGraph.construct(input, dependencies, build);
- combinedJsonObject.add("call_graph", gson.toJsonTree(sdgEdges));
- }
- }
- // Cleanup library dependencies directory
- BuildProject.cleanLibraryDependencies();
-
- // Convert the JavaCompilationUnit to JSON and add to consolidated json object
- String symbolTableJSONString = gson.toJson(symbolTable);
- JsonElement symbolTableJSON = gson.fromJson(symbolTableJSONString, JsonElement.class);
- combinedJsonObject.add("symbol_table", symbolTableJSON);
-
- // Add version number to the output JSON
- try {
- String[] versions = new VersionProvider().getVersion();
- if (versions.length > 0) {
- combinedJsonObject.addProperty("version", versions[0]);
- } else {
- combinedJsonObject.addProperty("version", "unknown");
- }
- } catch (Exception e) {
- combinedJsonObject.addProperty("version", "error retrieving version");
- }
- String consolidatedJSONString = gson.toJson(combinedJsonObject);
- emit(consolidatedJSONString);
- }
-
- private static void emit(String consolidatedJSONString) throws IOException {
- if (output == null) {
- System.out.println(consolidatedJSONString);
- } else {
- Path outputPath = Paths.get(output);
- if (!Files.exists(outputPath)) {
- Files.createDirectories(outputPath);
- }
- // If output is not null, export to a file
- File file = new File(output, "analysis.json");
- try (FileWriter fileWriter = new FileWriter(file)) {
- fileWriter.write(consolidatedJSONString);
- Log.done("Analysis output saved at " + output);
- } catch (IOException e) {
- Log.error("Error writing to file: " + e.getMessage());
- }
- }
- }
-
- private static boolean hasLegacyImportSchema(JsonObject symbolTableJson) {
- if (symbolTableJson == null) {
- return false;
- }
- for (Map.Entry entry : symbolTableJson.entrySet()) {
- JsonElement compilationUnitElement = entry.getValue();
- if (!compilationUnitElement.isJsonObject()) {
- continue;
- }
- JsonObject compilationUnitJson = compilationUnitElement.getAsJsonObject();
- if (!compilationUnitJson.has("imports") || !compilationUnitJson.get("imports").isJsonArray()) {
- continue;
- }
- for (JsonElement importElement : compilationUnitJson.getAsJsonArray("imports")) {
- if (importElement.isJsonPrimitive() && importElement.getAsJsonPrimitive().isString()) {
- return true;
- }
- }
- }
- return false;
- }
-
- private static Map readSymbolTableFromFile(File analysisJsonFile) {
- Type symbolTableType = new TypeToken>() {
- }.getType();
- try (FileReader reader = new FileReader(analysisJsonFile)) {
- JsonObject jsonObject = JsonParser.parseReader(reader).getAsJsonObject();
- JsonObject symbolTableJson = jsonObject.getAsJsonObject("symbol_table");
- if (hasLegacyImportSchema(symbolTableJson)) {
- throw new IllegalStateException("Existing analysis.json uses legacy import schema (imports as strings). Regenerate analysis with codeanalyzer 2.3.7 or newer.");
- }
- return gson.fromJson(symbolTableJson, symbolTableType);
- } catch (IOException e) {
- Log.error("Error reading analysis file: " + e.getMessage());
- }
- return null;
- }
-}
diff --git a/src/main/java/com/ibm/cldk/SymbolTable.java b/src/main/java/com/ibm/cldk/SymbolTable.java
deleted file mode 100644
index c7a203d6..00000000
--- a/src/main/java/com/ibm/cldk/SymbolTable.java
+++ /dev/null
@@ -1,1290 +0,0 @@
-package com.ibm.cldk;
-
-import com.github.javaparser.*;
-import com.github.javaparser.ast.*;
-import com.github.javaparser.ast.body.*;
-import com.github.javaparser.ast.comments.Comment;
-import com.github.javaparser.ast.comments.JavadocComment;
-import com.github.javaparser.ast.expr.*;
-import com.github.javaparser.ast.nodeTypes.NodeWithJavadoc;
-import com.github.javaparser.ast.nodeTypes.NodeWithName;
-import com.github.javaparser.ast.stmt.*;
-import com.github.javaparser.ast.type.ReferenceType;
-import com.github.javaparser.ast.type.Type;
-import com.github.javaparser.printer.DefaultPrettyPrinter;
-import com.github.javaparser.printer.lexicalpreservation.LexicalPreservingPrinter;
-import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
-import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration;
-import com.github.javaparser.resolution.types.ResolvedType;
-import com.github.javaparser.symbolsolver.JavaSymbolSolver;
-import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
-import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
-import com.github.javaparser.symbolsolver.utils.SymbolSolverCollectionStrategy;
-import com.github.javaparser.utils.ProjectRoot;
-import com.github.javaparser.utils.SourceRoot;
-import com.google.common.collect.Table;
-import com.google.common.collect.Tables;
-import com.ibm.cldk.entities.*;
-import com.ibm.cldk.javaee.CRUDFinderFactory;
-import com.ibm.cldk.javaee.EntrypointsFinderFactory;
-import com.ibm.cldk.javaee.utils.enums.CRUDOperationType;
-import com.ibm.cldk.javaee.utils.enums.CRUDQueryType;
-import com.ibm.cldk.utils.Log;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.*;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-import org.apache.commons.lang3.tuple.Pair;
-
-@SuppressWarnings({"unchecked", "rawtypes"})
-public class SymbolTable {
-
- private static JavaSymbolSolver javaSymbolSolver;
- private static Set unresolvedTypes = new HashSet<>();
- private static Set unresolvedExpressions = new HashSet<>();
-
- /**
- * Processes the given compilation unit to extract information about classes
- * and interfaces declared in the unit and returns a JSON object containing
- * the extracted information.
- *
- * @param parseResult compilation unit to be processed
- * @return JSON object containing extracted information
- */
- // Let's store the known callables here for future use.
- public static Table declaredMethodsAndConstructors = Tables
- .newCustomTable(new HashMap<>(), () -> new HashMap<>() {
- @Override
- public Callable get(Object key) {
- if (key instanceof String) {
- Optional> matchingEntry = this.entrySet().stream()
- .filter(entry -> isMethodSignatureMatch((String) key, entry.getKey())).findFirst();
- if (matchingEntry.isPresent()) {
- return matchingEntry.get().getValue();
- }
- }
- return super.get(key);
- }
-
- private boolean isMethodSignatureMatch(String fullSignature, String searchSignature) {
- String methodName = fullSignature.split("\\(")[0];
- String searchMethodName = searchSignature.split("\\(")[0];
-
- // Check method name match
- if (!methodName.equals(searchMethodName)) {
- return false;
- }
-
- // Extract parameters, split by comma, and trim
- String[] fullParams = fullSignature
- .substring(fullSignature.indexOf("(") + 1, fullSignature.lastIndexOf(")")).split(",");
- String[] searchParams = searchSignature
- .substring(searchSignature.indexOf("(") + 1, searchSignature.lastIndexOf(")")).split(",");
-
- // Allow matching with fewer search parameters
- if (searchParams.length != fullParams.length) {
- return false;
- }
-
- return IntStream.range(0, searchParams.length).allMatch(i -> {
- String fullParamTrimmed = fullParams[i].trim();
- String searchParamTrimmed = searchParams[i].trim();
- return fullParamTrimmed.endsWith(searchParamTrimmed);
- });
- }
- });
-
- private static JavaCompilationUnit processCompilationUnit(CompilationUnit parseResult) {
- JavaCompilationUnit cUnit = new JavaCompilationUnit();
-
- cUnit.setFilePath(parseResult.getStorage().map(s -> s.getPath().toString()).orElse(""));
-
- // Set file level comment
- parseResult.getAllComments().stream().findFirst().ifPresent(c -> {
- com.ibm.cldk.entities.Comment fileComment = new com.ibm.cldk.entities.Comment();
- fileComment.setContent(c.getContent());
- fileComment.setStartLine(c.getRange().isPresent() ? c.getRange().get().begin.line : -1);
- fileComment.setEndLine(c.getRange().isPresent() ? c.getRange().get().end.line : -1);
- fileComment.setStartColumn(c.getRange().isPresent() ? c.getRange().get().begin.column : -1);
- fileComment.setEndColumn(c.getRange().isPresent() ? c.getRange().get().end.column : -1);
- fileComment.setJavadoc(c.isJavadocComment());
- cUnit.getComments().add(fileComment);
- });
-
- // Add class comment
- cUnit.setComments(
- parseResult.getAllComments().stream().map(c -> {
- com.ibm.cldk.entities.Comment fileComment = new com.ibm.cldk.entities.Comment();
- fileComment.setContent(c.getContent());
- fileComment.setStartLine(c.getRange().isPresent() ? c.getRange().get().begin.line : -1);
- fileComment.setEndLine(c.getRange().isPresent() ? c.getRange().get().end.line : -1);
- fileComment.setStartColumn(c.getRange().isPresent() ? c.getRange().get().begin.column : -1);
- fileComment.setEndColumn(c.getRange().isPresent() ? c.getRange().get().end.column : -1);
- fileComment.setJavadoc(c.isJavadocComment());
- return fileComment;
- })
- .collect(Collectors.toList()));
-
- // Set package name
- cUnit.setPackageName(parseResult.getPackageDeclaration().map(NodeWithName::getNameAsString).orElse(""));
- // Add javadoc comment
- // Add imports
- cUnit.setImports(
- parseResult.getImports().stream().map(importDecl -> {
- Import importNode = new Import();
- importNode.setPath(importDecl.getNameAsString());
- importNode.setStatic(importDecl.isStatic());
- importNode.setWildcard(importDecl.isAsterisk());
- return importNode;
- }).collect(Collectors.toList()));
-
- // create array node for type declarations
- cUnit.setTypeDeclarations(parseResult.findAll(TypeDeclaration.class).stream()
- .filter(typeDecl -> typeDecl.getFullyQualifiedName().isPresent()).map(typeDecl -> {
- // get type name and initialize the type object
- String typeName = typeDecl.getFullyQualifiedName().get().toString();
- com.ibm.cldk.entities.Type typeNode = new com.ibm.cldk.entities.Type();
-
- if (typeDecl instanceof ClassOrInterfaceDeclaration) {
- ClassOrInterfaceDeclaration classDecl = (ClassOrInterfaceDeclaration) typeDecl;
-
- // Add interfaces implemented by class
- typeNode.setImplementsList(classDecl.getImplementedTypes().stream()
- .map(SymbolTable::resolveType).collect(Collectors.toList()));
-
- // Add class modifiers
- typeNode.setModifiers(classDecl.getModifiers().stream().map(m -> m.toString().strip())
- .collect(Collectors.toList()));
-
- // Add class annotations
- typeNode.setAnnotations(classDecl.getAnnotations().stream().map(a -> a.toString().strip())
- .collect(Collectors.toList()));
-
- // add booleans indicating interfaces and inner/local classes
- typeNode.setInterface(classDecl.isInterface());
- typeNode.setInnerClass(classDecl.isInnerClass());
- typeNode.setLocalClass(classDecl.isLocalClassDeclaration());
-
- // Add extends
- typeNode.setExtendsList(classDecl.getExtendedTypes().stream().map(SymbolTable::resolveType)
- .collect(Collectors.toList()));
-
- } else if (typeDecl instanceof EnumDeclaration) {
- EnumDeclaration enumDecl = (EnumDeclaration) typeDecl;
-
- // Add interfaces implemented by enum
- typeNode.setImplementsList(enumDecl.getImplementedTypes().stream().map(SymbolTable::resolveType)
- .collect(Collectors.toList()));
-
- // Add enum modifiers
- typeNode.setModifiers(enumDecl.getModifiers().stream().map(m -> m.toString().strip())
- .collect(Collectors.toList()));
-
- // Add enum annotations
- typeNode.setAnnotations(enumDecl.getAnnotations().stream().map(a -> a.toString().strip())
- .collect(Collectors.toList()));
-
- // Add enum constants
- typeNode.setEnumConstants(enumDecl.getEntries().stream()
- .map(SymbolTable::processEnumConstantDeclaration).collect(Collectors.toList()));
- } else if (typeDecl instanceof RecordDeclaration) {
- RecordDeclaration recordDecl = (RecordDeclaration) typeDecl;
-
- // Set that this is a record declaration
- typeNode.setRecordDeclaration(typeDecl.isRecordDeclaration());
-
- // Add interfaces implemented by record
- typeNode.setImplementsList(recordDecl.getImplementedTypes().stream()
- .map(SymbolTable::resolveType).collect(Collectors.toList()));
-
- // Add record modifiers
- typeNode.setModifiers(recordDecl.getModifiers().stream().map(m -> m.toString().strip())
- .collect(Collectors.toList()));
-
- // Add record annotations
- typeNode.setAnnotations(recordDecl.getAnnotations().stream().map(a -> a.toString().strip())
- .collect(Collectors.toList()));
-
- // Add record components
- typeNode.setRecordComponents(processRecordComponents(recordDecl));
- } else {
- // TODO: handle AnnotationDeclaration, RecordDeclaration
- // set the common type attributes only
- typeNode = new com.ibm.cldk.entities.Type();
- }
-
- /*
- * set common attributes of types that available in type declarations:
- * is nested type, is class or interface declaration, is enum declaration,
- * comments, parent class, callable declarations, field declarations
- */
- // Discover initialization blocks
- typeNode.setInitializationBlocks(typeDecl.findAll(InitializerDeclaration.class).stream()
- .map(initializerDeclaration -> {
- return createInitializationBlock(initializerDeclaration, parseResult.getStorage()
- .map(s -> s.getPath().toString()).orElse(""));
- })
- .collect(Collectors.toList()));
- // Set fields indicating nested, class/interface, enum, annotation, and record
- // types
- typeNode.setNestedType(typeDecl.isNestedType());
- typeNode.setClassOrInterfaceDeclaration(typeDecl.isClassOrInterfaceDeclaration());
- typeNode.setEnumDeclaration(typeDecl.isEnumDeclaration());
- typeNode.setAnnotationDeclaration(typeDecl.isAnnotationDeclaration());
-
- // Add class comment
- typeNode.setComments(
- typeDecl.getAllContainedComments().stream()
-// .filter(c -> c.getParentNode().isEmpty() || (c.getParentNode().isPresent() && parseResult.getPrimaryType().get().equals(c.getCommentedNode().get())))
- .map(c -> {
- com.ibm.cldk.entities.Comment typeNodeComment = new com.ibm.cldk.entities.Comment();
- typeNodeComment.setContent(c.getContent());
- typeNodeComment.setStartLine(c.getRange().isPresent() ? c.getRange().get().begin.line : -1);
- typeNodeComment.setEndLine(c.getRange().isPresent() ? c.getRange().get().end.line : -1);
- typeNodeComment.setStartColumn(c.getRange().isPresent() ? c.getRange().get().begin.column : -1);
- typeNodeComment.setEndColumn(c.getRange().isPresent() ? c.getRange().get().end.column : -1);
- typeNodeComment.setJavadoc(c.isJavadocComment());
- return typeNodeComment;
- })
- .collect(Collectors.toList()));
-
- // Get JavaDoc comments
- // Check to see if there is a java doc comment if so, add it to the comments list
- if (getJavadoc(typeDecl).isPresent()) {
- typeNode.getComments().add(getJavadoc(typeDecl).get());
- }
-
- // add parent class (for nested type declarations)
- typeNode.setParentType(typeDecl.getParentNode().get() instanceof TypeDeclaration
- ? ((TypeDeclaration>) typeDecl.getParentNode().get())
- .getFullyQualifiedName().get()
- : "");
-
- typeNode.setNestedTypeDeclarations(typeDecl.findAll(TypeDeclaration.class).stream()
- .filter(typ -> typ.isClassOrInterfaceDeclaration() || typ.isEnumDeclaration())
- .filter(typ -> typ.getParentNode().isPresent() && typ.getParentNode().get() == typeDecl)
- .map(typ -> typ.getFullyQualifiedName().get().toString()).collect(Collectors.toList()));
-
- // Add information about declared fields (filtering to fields declared in the
- // type, not in a nested type)
- typeNode.setFieldDeclarations(typeDecl.findAll(FieldDeclaration.class).stream()
- .filter(f -> f.getParentNode().isPresent() && f.getParentNode().get() == typeDecl)
- .map(SymbolTable::processFieldDeclaration).collect(Collectors.toList()));
- List fieldNames = new ArrayList<>();
- typeNode.getFieldDeclarations().stream().map(Field::getVariables).forEach(fieldNames::addAll);
-
- // Add information about declared methods (filtering to methods declared in the
- // class, not in a nested class)
- typeNode.setCallableDeclarations(typeDecl.findAll(CallableDeclaration.class).stream()
- .filter(c -> c.getParentNode().isPresent() && c.getParentNode().get() == typeDecl)
- .map(meth -> {
- Pair callableDeclaration = processCallableDeclaration(meth,
- fieldNames, typeName, parseResult.getStorage().map(s -> s.getPath().toString())
- .orElse(""));
- declaredMethodsAndConstructors.put(typeName, callableDeclaration.getLeft(),
- callableDeclaration.getRight());
- return callableDeclaration;
- }).collect(Collectors.toMap(p -> p.getLeft(), p -> p.getRight())));
-
- // Add information about if the TypeNode is an entry point class
- typeNode.setEntrypointClass(isEntryPointClass(typeDecl));
-
- return Pair.of(typeName, typeNode);
-
- }).collect(Collectors.toMap(p -> p.getLeft(), p -> p.getRight())));
-
- return cUnit;
- }
-
- private static InitializationBlock createInitializationBlock(InitializerDeclaration initializerDeclaration,
- String filePath) {
- InitializationBlock initializationBlock = new InitializationBlock();
- initializationBlock.setFilePath(filePath);
-
- com.ibm.cldk.entities.Comment comment = new com.ibm.cldk.entities.Comment();
-
- // Add class comment
- initializationBlock.setComments(
- initializerDeclaration.getAllContainedComments().stream()
- .map(c -> {
- com.ibm.cldk.entities.Comment typeNodeComment = new com.ibm.cldk.entities.Comment();
- typeNodeComment.setContent(c.getContent());
- typeNodeComment.setStartLine(c.getRange().isPresent() ? c.getRange().get().begin.line : -1);
- typeNodeComment.setEndLine(c.getRange().isPresent() ? c.getRange().get().end.line : -1);
- typeNodeComment.setStartColumn(c.getRange().isPresent() ? c.getRange().get().begin.column : -1);
- typeNodeComment.setEndColumn(c.getRange().isPresent() ? c.getRange().get().end.column : -1);
- typeNodeComment.setJavadoc(c.isJavadocComment());
- return typeNodeComment;
- })
- .collect(Collectors.toList()));
-
- // Check to see if there is a java doc comment if so, add it to the comments list
- getJavadoc(initializerDeclaration).ifPresent(value -> initializationBlock.getComments().add(value));
-
-
- // Set annotations
- initializationBlock.setAnnotations(initializerDeclaration.getAnnotations().stream()
- .map(a -> a.toString().strip()).collect(Collectors.toList()));
- // add exceptions declared in "throws" clause
- initializationBlock.setThrownExceptions(initializerDeclaration.getBody().getStatements().stream()
- .filter(Statement::isThrowStmt).map(throwStmt -> {
- try {
- return javaSymbolSolver.calculateType(throwStmt.asThrowStmt().getExpression()).describe();
- } catch (Exception e) {
- return throwStmt.asThrowStmt().getExpression().toString();
- }
- }).collect(Collectors.toList()));
- initializationBlock.setCode(LexicalPreservingPrinter.print(initializerDeclaration.getBody()));
- initializationBlock.setStartLine(
- initializerDeclaration.getRange().isPresent() ? initializerDeclaration.getRange().get().begin.line
- : -1);
- initializationBlock.setEndLine(
- initializerDeclaration.getRange().isPresent() ? initializerDeclaration.getRange().get().end.line : -1);
- initializationBlock.setStatic(initializerDeclaration.isStatic());
- initializationBlock
- .setReferencedTypes(getReferencedTypes(Optional.ofNullable(initializerDeclaration.getBody())));
- initializationBlock.setAccessedFields(
- getAccessedFields(Optional.ofNullable(initializerDeclaration.getBody()), Collections.emptyList(), ""));
- initializationBlock.setCallSites(getCallSites(Optional.ofNullable(initializerDeclaration.getBody())));
- initializationBlock.setVariableDeclarations(
- getVariableDeclarations(Optional.ofNullable(initializerDeclaration.getBody())));
- initializationBlock.setCyclomaticComplexity(getCyclomaticComplexity(initializerDeclaration));
- return initializationBlock;
- }
-
- private static Optional getJavadoc(NodeWithJavadoc bodyDeclaration) {
- Optional javadocComment = bodyDeclaration.getJavadocComment();
- if (!javadocComment.isPresent()) {
- return Optional.empty();
- }
- com.ibm.cldk.entities.Comment javadoc = new com.ibm.cldk.entities.Comment();
- javadoc.setContent(javadocComment.get().getContent().isEmpty() || javadocComment.get().getContent().isBlank() ? "" : javadocComment.get().getContent());
- javadoc.setStartLine(javadocComment.get().getRange().get().begin.line);
- javadoc.setEndLine(javadocComment.get().getRange().get().end.line);
- javadoc.setStartColumn(javadocComment.get().getRange().get().begin.column);
- javadoc.setEndColumn(javadocComment.get().getRange().get().end.column);
- javadoc.setJavadoc(!(javadocComment.get().getContent().isEmpty() && javadocComment.get().getContent().isBlank()));
- return Optional.of(javadoc);
- }
-
- /**
- * Processes the given record to extract information about the
- * declared field and returns a JSON object containing the extracted
- * information.
- *
- * @param recordDecl field declaration to be processed
- * @return Field object containing extracted information
- */
- private static List processRecordComponents(RecordDeclaration recordDecl) {
- return recordDecl.getParameters().stream().map(
- parameter -> {
- RecordComponent recordComponent = new RecordComponent();
- com.ibm.cldk.entities.Comment comment = new com.ibm.cldk.entities.Comment();
- if (parameter.getComment().isPresent()) {
- Comment parsedComment = parameter.getComment().get();
- comment.setContent(parsedComment.getContent());
- parsedComment.getRange().ifPresent(range -> {
- comment.setStartLine(range.begin.line);
- comment.setEndLine(range.end.line);
- comment.setStartColumn(range.begin.column);
- comment.setEndColumn(range.end.column);
- });
-
- } else {
- comment.setContent("");
- comment.setStartLine(-1);
- comment.setEndLine(-1);
- comment.setStartColumn(-1);
- comment.setEndColumn(-1);
- }
-
- recordComponent.setComment(comment);
- recordComponent.setName(parameter.getNameAsString());
- recordComponent.setType(resolveType(parameter.getType()));
- recordComponent.setAnnotations(parameter.getAnnotations().stream().map(a -> a.toString().strip())
- .collect(Collectors.toList()));
- recordComponent.setModifiers(parameter.getModifiers().stream().map(a -> a.toString().strip())
- .collect(Collectors.toList()));
- recordComponent.setVarArgs(parameter.isVarArgs());
- recordComponent.setDefaultValue(
- mapRecordConstructorDefaults(recordDecl).getOrDefault(parameter.getNameAsString(), null));
- return recordComponent;
- }).collect(Collectors.toList());
- }
-
- private static Map mapRecordConstructorDefaults(RecordDeclaration recordDecl) {
-
- return recordDecl.getCompactConstructors().stream()
- .flatMap(constructor -> constructor.findAll(AssignExpr.class).stream()) // Flatten all assignments
- .filter(assignExpr -> assignExpr.getTarget().isNameExpr()) // Ensure assignment is to a parameter
- .collect(Collectors.toMap(
- assignExpr -> assignExpr.getTarget().asNameExpr().getNameAsString(), // Key: Parameter Name
- assignExpr -> Optional.ofNullable(assignExpr.getValue()).map(valueExpr -> { // Value: Default
- // Value
- return valueExpr.isStringLiteralExpr() ? valueExpr.asStringLiteralExpr().asString()
- : valueExpr.isBooleanLiteralExpr() ? valueExpr.asBooleanLiteralExpr().getValue()
- : valueExpr.isCharLiteralExpr() ? valueExpr.asCharLiteralExpr().getValue()
- : valueExpr.isDoubleLiteralExpr()
- ? valueExpr.asDoubleLiteralExpr().asDouble()
- : valueExpr.isIntegerLiteralExpr()
- ? valueExpr.asIntegerLiteralExpr().asNumber()
- : valueExpr.isLongLiteralExpr()
- ? valueExpr.asLongLiteralExpr().asNumber()
- : valueExpr.isNullLiteralExpr() ? null
- : valueExpr.toString();
- }).orElse("null"))); // Default: store as a string
- }
-
- private static boolean isEntryPointClass(TypeDeclaration typeDecl) {
- return EntrypointsFinderFactory.getEntrypointFinders()
- .anyMatch(finder -> finder.isEntrypointClass(typeDecl));
- }
-
- /**
- * Process enum constant declaration.
- *
- * @param enumConstDecl enum constant declaration to be processed
- * @return EnumConstant object containing extracted information
- */
- private static EnumConstant processEnumConstantDeclaration(EnumConstantDeclaration enumConstDecl) {
- EnumConstant enumConstant = new EnumConstant();
-
- // add enum constant name
- enumConstant.setName(enumConstDecl.getNameAsString());
-
- // add enum constant arguments
- enumConstant.setArguments(
- enumConstDecl.getArguments().stream().map(Node::toString).collect(Collectors.toList()));
-
- return enumConstant;
- }
-
- /**
- * Process parameter declarations on callables.
- *
- * @param paramDecl parameter declaration to be processed
- */
- private static ParameterInCallable processParameterDeclaration(Parameter paramDecl) {
- ParameterInCallable parameter = new ParameterInCallable();
- parameter.setType(resolveType(paramDecl.getType()));
- parameter.setName(paramDecl.getName().toString());
- parameter.setAnnotations(
- paramDecl.getAnnotations().stream().map(a -> a.toString().strip()).collect(Collectors.toList()));
- parameter.setModifiers(
- paramDecl.getModifiers().stream().map(a -> a.toString().strip()).collect(Collectors.toList()));
- parameter.setStartLine(paramDecl.getRange().isPresent() ? paramDecl.getRange().get().begin.line : -1);
- parameter.setStartColumn(paramDecl.getRange().isPresent() ? paramDecl.getRange().get().begin.column : -1);
- parameter.setEndLine(paramDecl.getRange().isPresent() ? paramDecl.getRange().get().end.line : -1);
- parameter.setEndColumn(paramDecl.getRange().isPresent() ? paramDecl.getRange().get().end.column : -1);
- return parameter;
- }
-
- /**
- * Processes the given callable declaration to extract information about the
- * declared method or constructor and returns a JSON object containing the
- * extracted information.
- *
- * @param callableDecl callable (method or constructor) to be processed
- * @return Callable object containing extracted information
- */
- @SuppressWarnings("unchecked")
- private static Pair processCallableDeclaration(CallableDeclaration callableDecl,
- List classFields, String typeName, String filePath) {
- Callable callableNode = new Callable();
-
- // Set file path
- callableNode.setFilePath(filePath);
-
- // add callable signature
- callableNode.setSignature(getTypeErasureSignature(callableDecl));
-
- // add comment associated with method/constructor
- callableNode.setComments(
- callableDecl.getAllContainedComments().stream()
- .map(c -> {
- com.ibm.cldk.entities.Comment methodComment = new com.ibm.cldk.entities.Comment();
- methodComment.setContent(c.getContent());
- methodComment.setStartLine(c.getRange().isPresent() ? c.getRange().get().begin.line : -1);
- methodComment.setEndLine(c.getRange().isPresent() ? c.getRange().get().end.line : -1);
- methodComment.setStartColumn(c.getRange().isPresent() ? c.getRange().get().begin.column : -1);
- methodComment.setEndColumn(c.getRange().isPresent() ? c.getRange().get().end.column : -1);
- methodComment.setJavadoc(c.isJavadocComment());
- return methodComment;
- })
- .collect(Collectors.toList()));
-
- // Check to see if there are JavaDoc comments
- getJavadoc(callableDecl).ifPresent(value -> callableNode.getComments().add(value));
-
- // add annotations on method/constructor
- callableNode.setAnnotations((List) callableDecl.getAnnotations().stream()
- .map(mod -> mod.toString().strip()).collect(Collectors.toList()));
-
- // add method or constructor modifiers
- callableNode.setModifiers((List) callableDecl.getModifiers().stream().map(mod -> mod.toString().strip())
- .collect(Collectors.toList()));
-
- // add exceptions declared in "throws" clause
- callableNode.setThrownExceptions(((NodeList) callableDecl.getThrownExceptions()).stream()
- .map(SymbolTable::resolveType).collect(Collectors.toList()));
-
- // add the complete declaration string, including modifiers, throws, and
- // parameter names
- callableNode
- .setDeclaration(callableDecl.getDeclarationAsString(true, true, true).strip().replaceAll("//.*\n", ""));
-
- // add information about callable parameters: for each parameter, type, name,
- // annotations,
- // modifiers
- callableNode.setParameters((List) callableDecl.getParameters().stream()
- .map(param -> processParameterDeclaration((Parameter) param)).collect(Collectors.toList()));
-
- callableNode.setEntrypoint(isEntryPointMethod(callableDecl));
-
- // A method declaration may not have a body if it is an abstract method. A
- // constructor always has a body. So, we need to check if the body is present before processing it
- // and capture it using the Optional type.
- Optional body = (callableDecl instanceof MethodDeclaration)
- ? ((MethodDeclaration) callableDecl).getBody()
- : Optional.ofNullable(((ConstructorDeclaration) callableDecl).getBody());
-
- // Same as above, a constructor declaration may not have a return type
- // and method declaration always has a return type.
- callableNode.setReturnType(
- (callableDecl instanceof MethodDeclaration) ? resolveType(((MethodDeclaration) callableDecl).getType())
- : null);
-
- callableNode.setConstructor(callableDecl instanceof ConstructorDeclaration);
- callableNode.setStartLine(callableDecl.getRange().isPresent() ? callableDecl.getRange().get().begin.line : -1);
- callableNode.setEndLine(callableDecl.getRange().isPresent() ? callableDecl.getRange().get().end.line : -1);
- callableNode.setReferencedTypes(getReferencedTypes(body));
- try {
- callableNode.setCode(body.isPresent() ? LexicalPreservingPrinter.print(body.get()) : "");
- } catch (UnsupportedOperationException uoe) {
- Log.warn("LexicalPreservingPrinter.print() failed on method " + callableDecl.getSignature() +
- " of type "+typeName);
- Log.warn("Reverting to default printing");
- Log.warn(body.get().toString());
- callableNode.setCode(body.get().toString());
- }
- callableNode.setCodeStartLine(body.isPresent()? body.get().getBegin().get().line : -1);
-
- callableNode.setAccessedFields(getAccessedFields(body, classFields, typeName));
- callableNode.setCallSites(getCallSites(body));
- callableNode.setCrudOperations(
- callableNode.getCallSites().stream()
- .map(CallSite::getCrudOperation)
- .filter(Objects::nonNull)
- .collect(Collectors.toList()));
- callableNode.setCrudQueries(
- callableNode.getCallSites().stream()
- .map(CallSite::getCrudQuery)
- .filter(Objects::nonNull)
- .collect(Collectors.toList()));
- callableNode.setVariableDeclarations(getVariableDeclarations(body));
- callableNode.setCyclomaticComplexity(getCyclomaticComplexity(callableDecl));
-
- String callableSignature = getTypeErasureSignature(callableDecl);
- return Pair.of(callableSignature, callableNode);
- }
-
- /**
- * Returns type erasure signature for the given callable. Returns regular signature if an
- * error occurs in getting erased types.
- *
- * @param callableDecl: Callable to compute type erasure signature for
- * @return String representing type erasure or regular signature
- */
- private static String getTypeErasureSignature(CallableDeclaration callableDecl) {
- try {
- StringBuilder signature = new StringBuilder(
- (callableDecl instanceof MethodDeclaration) ? callableDecl.getNameAsString() : ""
- );
- List erasureParameterTypes = new ArrayList<>();
- for (Object param : callableDecl.getParameters()) {
- Parameter parameter = (Parameter) param;
- ResolvedType resolvedType = parameter.getType().resolve();
- if (parameter.isVarArgs()) {
- erasureParameterTypes.add(resolvedType.erasure().describe() + "[]");
- } else {
- erasureParameterTypes.add(resolvedType.erasure().describe());
- }
- }
- signature.append("(");
- signature.append(String.join(", ", erasureParameterTypes));
- signature.append(")");
- return signature.toString();
- } catch (Throwable e) {
- Log.warn("Could not compute type erasure signature for "+callableDecl.getSignature().asString()+
- "; computing regular signature");
- return callableDecl.getSignature().asString();
- }
- }
-
- /**
- * Returns type erasure signature for the given method or constructor declaration
- * resolved for a call site.
- *
- * @param methodDecl: Resolved method/constructor to compute type erasure signature for
- * @return String representing type erasure signature
- */
- private static String getTypeErasureSignature(ResolvedMethodLikeDeclaration methodDecl) {
- StringBuilder signature = new StringBuilder(methodDecl.getName());
- List erasureParameterTypes = new ArrayList<>();
- for (int i = 0; i < methodDecl.getNumberOfParams(); i++) {
- erasureParameterTypes.add(methodDecl.getParam(i).getType().erasure().describe());
- }
- signature.append("(");
- signature.append(String.join(", ", erasureParameterTypes));
- signature.append(")");
- return signature.toString();
- }
-
- private static boolean isEntryPointMethod(CallableDeclaration callableDecl) {
- return EntrypointsFinderFactory.getEntrypointFinders()
- .anyMatch(finder -> finder.isEntrypointMethod(callableDecl));
- }
-
-
- /**
- * Computes cyclomatic complexity for the given callable.
- *
- * @param callableDeclaration Callable to compute cyclomatic complexity for
- * @return cyclomatic complexity
- */
- private static int getCyclomaticComplexity(CallableDeclaration callableDeclaration) {
- int ifStmtCount = callableDeclaration.findAll(IfStmt.class).size();
- int loopStmtCount = callableDeclaration.findAll(DoStmt.class).size()
- + callableDeclaration.findAll(ForStmt.class).size()
- + callableDeclaration.findAll(ForEachStmt.class).size()
- + callableDeclaration.findAll(WhileStmt.class).size();
- int switchCaseCount = callableDeclaration.findAll(SwitchStmt.class).stream()
- .map(stmt -> stmt.getEntries().size()).reduce(0, Integer::sum);
- int conditionalExprCount = callableDeclaration.findAll(ConditionalExpr.class).size();
- int catchClauseCount = callableDeclaration.findAll(CatchClause.class).size();
- return ifStmtCount + loopStmtCount + switchCaseCount + conditionalExprCount + catchClauseCount + 1;
- }
-
- private static int getCyclomaticComplexity(InitializerDeclaration initializerDeclaration) {
- int ifStmtCount = initializerDeclaration.findAll(IfStmt.class).size();
- int loopStmtCount = initializerDeclaration.findAll(DoStmt.class).size()
- + initializerDeclaration.findAll(ForStmt.class).size()
- + initializerDeclaration.findAll(ForEachStmt.class).size()
- + initializerDeclaration.findAll(WhileStmt.class).size();
- int switchCaseCount = initializerDeclaration.findAll(SwitchStmt.class).stream()
- .map(stmt -> stmt.getEntries().size()).reduce(0, Integer::sum);
- int conditionalExprCount = initializerDeclaration.findAll(ConditionalExpr.class).size();
- int catchClauseCount = initializerDeclaration.findAll(CatchClause.class).size();
- return ifStmtCount + loopStmtCount + switchCaseCount + conditionalExprCount + catchClauseCount + 1;
- }
-
- /**
- * Processes the given field declaration to extract information about the
- * declared field and returns a JSON object containing the extracted
- * information.
- *
- * @param fieldDecl field declaration to be processed
- * @return Field object containing extracted information
- */
- private static Field processFieldDeclaration(FieldDeclaration fieldDecl) {
- Field field = new Field();
-
- // add comment associated with field
- com.ibm.cldk.entities.Comment comment = new com.ibm.cldk.entities.Comment();
- if (fieldDecl.getComment().isPresent()) {
- Comment parsedComment = fieldDecl.getComment().get();
- comment.setContent(parsedComment.getContent());
- parsedComment.getRange().ifPresent(range -> {
- comment.setStartLine(range.begin.line);
- comment.setEndLine(range.end.line);
- comment.setStartColumn(range.begin.column);
- comment.setEndColumn(range.end.column);
- });
- }
- field.setComment(comment);
-
- // add annotations on field
- field.setAnnotations(
- fieldDecl.getAnnotations().stream().map(a -> a.toString().strip()).collect(Collectors.toList()));
-
- // add variable names
- field.setVariables(
- fieldDecl.getVariables().stream().map(v -> v.getName().asString()).collect(Collectors.toList()));
-
- // add field modifiers
- field.setModifiers(
- fieldDecl.getModifiers().stream().map(m -> m.toString().strip()).collect(Collectors.toList()));
-
- // add field type
- field.setType(resolveType(fieldDecl.getCommonType()));
-
- // add field start and end lines
- field.setStartLine(fieldDecl.getRange().isPresent() ? fieldDecl.getRange().get().begin.line : null);
-
- field.setEndLine(fieldDecl.getRange().get().end.line);
-
- return field;
- }
-
- /**
- * Computes and returns the set of types references in a block of statement
- * (method or constructor body).
- *
- * @param blockStmt Block statement to compute referenced types for
- * @return List of types referenced in the block statement
- */
- private static List getReferencedTypes(Optional blockStmt) {
- Set referencedTypes = new HashSet<>();
- blockStmt.ifPresent(
- bs -> bs.findAll(VariableDeclarator.class).stream().filter(vd -> vd.getType().isClassOrInterfaceType())
- .map(vd -> resolveType(vd.getType())).forEach(referencedTypes::add));
-
- // add types of accessed fields to the set of referenced types
- blockStmt.ifPresent(
- bs -> bs.findAll(FieldAccessExpr.class).stream().filter(faExpr -> faExpr.getParentNode().isPresent()
- && !(faExpr.getParentNode().get() instanceof FieldAccessExpr)).map(faExpr -> {
- if (faExpr.getParentNode().isPresent()
- && faExpr.getParentNode().get() instanceof CastExpr) {
- return resolveType(((CastExpr) faExpr.getParentNode().get()).getType());
- } else {
- return resolveExpression(faExpr);
- }
- }).filter(type -> !type.isEmpty()).forEach(referencedTypes::add));
-
- // TODO: add resolved method access expressions
- return new ArrayList<>(referencedTypes);
- }
-
- /**
- * Returns information about variable declarations in the given callable.
- * The information includes var name, var type, var initializer, and
- * position.
- *
- * @param blockStmt Callable to compute var declaration information for
- * @return list of variable declarations
- */
- private static List getVariableDeclarations(Optional blockStmt) {
- List varDeclarations = new ArrayList<>();
- if (blockStmt.isEmpty()) {
- return varDeclarations;
- }
- for (VariableDeclarator declarator : blockStmt.get().findAll(VariableDeclarator.class)) {
- VariableDeclaration varDeclaration = new VariableDeclaration();
- com.ibm.cldk.entities.Comment comment = new com.ibm.cldk.entities.Comment();
- if (declarator.getComment().isPresent()) {
- Comment parsedComment = declarator.getComment().get();
- comment.setContent(parsedComment.getContent().isBlank() ? "" : parsedComment.getContent().isEmpty() ? "" : parsedComment.getContent());
- parsedComment.getRange().ifPresent(range -> {
- comment.setStartLine(range.begin.line);
- comment.setEndLine(range.end.line);
- comment.setStartColumn(range.begin.column);
- comment.setEndColumn(range.end.column);
- });
- }
- varDeclaration.setComment(comment);
- varDeclaration.setName(declarator.getNameAsString());
- varDeclaration.setType(resolveType(declarator.getType()));
- varDeclaration.setInitializer(
- declarator.getInitializer().isPresent() ? declarator.getInitializer().get().toString() : "");
- if (declarator.getRange().isPresent()) {
- varDeclaration.setStartLine(declarator.getRange().get().begin.line);
- varDeclaration.setStartColumn(declarator.getRange().get().begin.column);
- varDeclaration.setEndLine(declarator.getRange().get().end.line);
- varDeclaration.setEndColumn(declarator.getRange().get().end.column);
- } else {
- varDeclaration.setStartLine(-1);
- varDeclaration.setStartColumn(-1);
- varDeclaration.setEndLine(-1);
- varDeclaration.setEndColumn(-1);
- }
- varDeclarations.add(varDeclaration);
- }
- return varDeclarations;
- }
-
- /**
- * Computes and returns the list of fields accessed in the given callable
- * body. The returned values contain field names qualified by names of the
- * declaring types.
- *
- * @param callableBody Callable body to compute accessed fields for
- * @return List of fully qualified field names
- */
- private static List getAccessedFields(Optional callableBody, List classFields,
- String typeName) {
- Set accessedFields = new HashSet<>();
-
- // process field access expressions in the callable
- callableBody.ifPresent(
- cb -> cb.findAll(FieldAccessExpr.class).stream().filter(faExpr -> faExpr.getParentNode().isPresent()
- && !(faExpr.getParentNode().get() instanceof FieldAccessExpr)).map(faExpr -> {
- String fieldDeclaringType = resolveExpression(faExpr.getScope());
- if (!fieldDeclaringType.isEmpty()) {
- return fieldDeclaringType + "." + faExpr.getNameAsString();
- } else {
- return faExpr.getNameAsString();
- }
- }).forEach(accessedFields::add));
-
- // process all names expressions in callable and match against names of declared
- // fields
- // in class TODO: handle local variable declarations with the same name
- if (callableBody.isPresent()) {
- for (NameExpr nameExpr : callableBody.get().findAll(NameExpr.class)) {
- for (String fieldName : classFields) {
- if (nameExpr.getNameAsString().equals(fieldName)) {
- accessedFields.add(typeName + "." + nameExpr.getNameAsString());
- }
- }
- }
- }
-
- return new ArrayList<>(accessedFields);
- }
-
- /**
- * Returns information about call sites in the given callable. The
- * information includes: the method name, the declaring type name, and types
- * of arguments used in method call.
- *
- * @param callableBody callable to compute call-site information for
- * @return list of call sites
- */
- @SuppressWarnings({ "OptionalUsedAsFieldOrParameterType" })
- private static List getCallSites(Optional callableBody) {
- List callSites = new ArrayList<>();
- if (callableBody.isEmpty()) {
- return callSites;
- }
- for (MethodCallExpr methodCallExpr : callableBody.get().findAll(MethodCallExpr.class)) {
- // resolve declaring type for called method
- boolean isStaticCall = false;
- String declaringType = "";
- String receiverName = "";
- String returnType = "";
- if (methodCallExpr.getScope().isPresent()) {
- Expression scopeExpr = methodCallExpr.getScope().get();
- receiverName = scopeExpr.toString();
- declaringType = resolveExpression(scopeExpr);
- if (declaringType.contains(" | ")) {
- declaringType = declaringType.split(" \\| ")[0];
- }
- String declaringTypeName = declaringType.contains(".")
- ? declaringType.substring(declaringType.lastIndexOf(".") + 1)
- : declaringType;
- if (declaringTypeName.equals(scopeExpr.toString())) {
- isStaticCall = true;
- }
- }
-
- // compute return type for method call taking into account typecast of return
- // value
- if (methodCallExpr.getParentNode().isPresent()
- && methodCallExpr.getParentNode().get() instanceof CastExpr) {
- returnType = resolveType(((CastExpr) methodCallExpr.getParentNode().get()).getType());
- } else {
- returnType = resolveExpression(methodCallExpr);
- }
-
- // resolve callee and get signature
- String calleeSignature = "";
- try {
- calleeSignature = getTypeErasureSignature(methodCallExpr.resolve());
- } catch (Throwable exception) {
- Log.debug("Could not resolve method call: " + methodCallExpr + ": " + exception.getMessage());
- }
-
- // Resolve access qualifier
- AccessSpecifier accessSpecifier = AccessSpecifier.NONE;
- try {
- ResolvedMethodDeclaration resolvedMethodDeclaration = methodCallExpr.resolve();
- accessSpecifier = resolvedMethodDeclaration.accessSpecifier();
- } catch (Throwable exception) {
- Log.debug("Could not resolve access specifier for method call: " + methodCallExpr + ": "
- + exception.getMessage());
- }
- // resolve arguments of the method call to types
- List argumentTypes = methodCallExpr.getArguments().stream().map(SymbolTable::resolveExpression)
- .collect(Collectors.toList());
- // Get argument string from the callsite
- List listOfArgumentStrings = methodCallExpr.getArguments().stream().map(Expression::toString)
- .collect(Collectors.toList());
- // Determine if this call site is potentially a CRUD operation.
- CRUDOperation crudOperation = null;
- Optional crudOperationType = findCRUDOperation(declaringType,
- methodCallExpr.getNameAsString());
- if (crudOperationType.isPresent()) {
- // We found a CRUD operation, so we need to populate the details of the call
- // site this CRUD operation.
- int lineNumber = methodCallExpr.getRange().isPresent() ? methodCallExpr.getRange().get().begin.line
- : -1;
- crudOperation = new CRUDOperation();
- crudOperation.setLineNumber(lineNumber);
- crudOperation.setOperationType(crudOperationType.get());
- }
- // Determine if this call site is potentially a CRUD query.
- CRUDQuery crudQuery = null;
- Optional crudQueryType = findCRUDQuery(declaringType, methodCallExpr.getNameAsString(),
- Optional.of(listOfArgumentStrings));
- if (crudQueryType.isPresent()) {
- // We found a CRUD query, so we need to populate the details of the call site
- // this CRUD query.
- int lineNumber = methodCallExpr.getRange().isPresent() ? methodCallExpr.getRange().get().begin.line
- : -1;
- crudQuery = new CRUDQuery();
- crudQuery.setLineNumber(lineNumber);
- crudQuery.setQueryType(crudQueryType.get());
- crudQuery.setQueryArguments(listOfArgumentStrings);
- }
- // add a new call site object
-
-
- callSites.add(createCallSite(methodCallExpr, methodCallExpr.getNameAsString(), receiverName, declaringType,
- argumentTypes, listOfArgumentStrings, returnType, calleeSignature, isStaticCall, false, crudOperation, crudQuery,
- accessSpecifier));
- }
-
- for (ObjectCreationExpr objectCreationExpr : callableBody.get().findAll(ObjectCreationExpr.class)) {
- // resolve declaring type for called method
- String instantiatedType = resolveType(objectCreationExpr.getType());
-
- // resolve arguments of the constructor call to types
- List argumentTypes = objectCreationExpr.getArguments().stream().map(SymbolTable::resolveExpression)
- .collect(Collectors.toList());
-
- // get argument expressions for constructor call
- List argumentExpressions = objectCreationExpr.getArguments().stream().map(Expression::toString)
- .collect(Collectors.toList());
-
- // resolve callee and get signature
- String calleeSignature = "";
- try {
- calleeSignature = getTypeErasureSignature(objectCreationExpr.resolve());
- } catch (Throwable exception) {
- Log.debug("Could not resolve constructor call: " + objectCreationExpr + ": " + exception.getMessage());
- }
-
- // add a new call site object
- callSites
- .add(createCallSite(objectCreationExpr, "",
- objectCreationExpr.getScope().isPresent() ? objectCreationExpr.getScope().get().toString()
- : "",
- instantiatedType, argumentTypes, argumentExpressions, instantiatedType, calleeSignature, false, true, null, null,
- AccessSpecifier.NONE));
- }
-
- return callSites;
- }
-
- @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
- private static Optional findCRUDQuery(String declaringType, String nameAsString,
- Optional> arguments) {
- return CRUDFinderFactory.getCRUDFinders().map(
- finder -> {
- if (finder.isReadQuery(declaringType, nameAsString, arguments)) {
- return CRUDQueryType.READ;
- } else if (finder.isWriteQuery(declaringType, nameAsString, arguments)) {
- return CRUDQueryType.WRITE;
- } else if (finder.isNamedQuery(declaringType, nameAsString, arguments)) {
- return CRUDQueryType.NAMED;
- } else
- return null;
- })
- .filter(Objects::nonNull)
- .findFirst();
- }
-
- private static Optional findCRUDOperation(String declaringType, String nameAsString) {
- return CRUDFinderFactory.getCRUDFinders().map(
- finder -> {
- if (finder.isCreateOperation(declaringType, nameAsString)) {
- return CRUDOperationType.CREATE;
- } else if (finder.isReadOperation(declaringType, nameAsString)) {
- return CRUDOperationType.READ;
- } else if (finder.isUpdateOperation(declaringType, nameAsString)) {
- return CRUDOperationType.UPDATE;
- } else if (finder.isDeleteOperation(declaringType, nameAsString)) {
- return CRUDOperationType.DELETE;
- } else
- return null;
- })
- .filter(Objects::nonNull)
- .findFirst();
- }
-
- /**
- * Creates and returns a new CallSite object for the given expression, which
- * can be a method-call or object-creation expression.
- *
- * @param callExpr
- * @param calleeName
- * @param receiverExpr
- * @param receiverType
- * @param argumentTypes
- * @param argumentExpr
- * @param returnType
- * @param calleeSignature
- * @param isStaticCall
- * @param isConstructorCall
- * @param crudOperation,
- * @param crudQuery,
- * @param accessSpecifier
- * @return
- */
- private static CallSite createCallSite(
- Expression callExpr,
- String calleeName,
- String receiverExpr,
- String receiverType,
- List argumentTypes,
- List argumentExpr,
- String returnType,
- String calleeSignature,
- boolean isStaticCall,
- boolean isConstructorCall,
- CRUDOperation crudOperation,
- CRUDQuery crudQuery,
- AccessSpecifier accessSpecifier) {
- CallSite callSite = new CallSite();
-
- com.ibm.cldk.entities.Comment comment = new com.ibm.cldk.entities.Comment();
- callExpr.findAncestor(Node.class).ifPresent(stmt -> {
- stmt.getComment().ifPresent(c -> {
- comment.setContent(c.getContent());
- c.getRange().ifPresent(range -> {
- comment.setStartLine(range.begin.line);
- comment.setEndLine(range.end.line);
- comment.setStartColumn(range.begin.column);
- comment.setEndColumn(range.end.column);
- });
- callSite.setComment(comment);
- });
- });
- callSite.setMethodName(calleeName);
- callSite.setReceiverExpr(receiverExpr);
- callSite.setReceiverType(receiverType);
- callSite.setArgumentTypes(argumentTypes);
- callSite.setArgumentExpr(argumentExpr);
- callSite.setReturnType(returnType);
- callSite.setCalleeSignature(calleeSignature);
- callSite.setStaticCall(isStaticCall);
- callSite.setConstructorCall(isConstructorCall);
- callSite.setPrivate(accessSpecifier.equals(AccessSpecifier.PRIVATE));
- callSite.setPublic(accessSpecifier.equals(AccessSpecifier.PUBLIC));
- callSite.setProtected(accessSpecifier.equals(AccessSpecifier.PROTECTED));
- callSite.setUnspecified(accessSpecifier.equals(AccessSpecifier.NONE));
- callSite.setCrudOperation(crudOperation);
- callSite.setCrudQuery(crudQuery);
- if (callExpr.getRange().isPresent()) {
- callSite.setStartLine(callExpr.getRange().get().begin.line);
- callSite.setStartColumn(callExpr.getRange().get().begin.column);
- callSite.setEndLine(callExpr.getRange().get().end.line);
- callSite.setEndColumn(callExpr.getRange().get().end.column);
- } else {
- callSite.setStartLine(-1);
- callSite.setStartColumn(-1);
- callSite.setEndLine(-1);
- callSite.setEndColumn(-1);
- }
- return callSite;
- }
-
- /**
- * Calculates type for the given expression and returns the resolved type
- * name, or empty string if exception occurs during type resolution.
- *
- * @param expression Expression to be resolved
- * @return Resolved type name or empty string if type resolution fails
- */
- private static String resolveExpression(Expression expression) {
- // perform expression resolution if resolution of this expression did not fail
- // previously
- if (!unresolvedExpressions.contains(expression.toString())) {
- try {
- ResolvedType resolvedType = javaSymbolSolver.calculateType(expression);
- if (resolvedType.isReferenceType() || resolvedType.isUnionType()) {
- return resolvedType.describe();
- }
- } catch (Throwable exception) {
- Log.debug("Could not resolve expression: " + expression + ": " + exception.getMessage());
- unresolvedExpressions.add(expression.toString());
- }
- }
- return "";
- }
-
- /**
- * Resolves the given type and returns string representation of the resolved
- * type. If type resolution fails, returns string representation (name) of
- * the type.
- *
- * @param type Type to be resolved
- * @return Resolved (qualified) type name
- */
- private static String resolveType(Type type) {
- // perform type resolution if resolution of this type did not fail previously
- if (!unresolvedTypes.contains(type.asString())) {
- try {
- return type.resolve().describe();
- } catch (Throwable e) {
- Log.warn("Could not resolve type: " + type.asString() + ": " + e.getMessage());
- unresolvedTypes.add(type.asString());
- }
- }
- return type.asString();
- }
-
- /**
- * Collects all source roots (e.g., "src/main/java", "src/test/java") under
- * the given project root path using the symbol solver collection strategy.
- * Parses all source files under each source root and returns the complete
- * symbol table as map of file path and java compilation unit pairs.
- *
- * @param projectRootPath root path of the project to be analyzed
- * @return Pair of extracted symbol table map and parse problems map for
- * project
- * @throws IOException
- */
- private static final String[] EXCLUDED_SOURCE_ROOTS = {
- Paths.get("src", "test", "resources").toString(),
- Paths.get("src", "it", "resources").toString(),
- Paths.get("src", "xdocs-examples").toString()
- };
- private static boolean excludeSourceRoot(Path sourceRoot) {
- for (String excludedSrcRoot : EXCLUDED_SOURCE_ROOTS) {
- if (Pattern.compile(excludedSrcRoot).matcher(sourceRoot.toString()).find()) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Sets up lexical preserving printer for the given compilation unit in a safe manner by checking
- * whether any node in the unit is missing ranges, which can result in exception.
- *
- * @param compilationUnit Compilation unit to be set with lexical preserving printer
- * @return compilation unit set up with lexical preserving printer or the original compilation
- * unit if the unit contains range-missing nodes
- */
- private static CompilationUnit safeLexicalPreservingPrinterSetup(CompilationUnit compilationUnit) {
- // setup lexical-preserving printer only if CU has no missing-range nodes
- boolean hasNodeWithMissingRange = compilationUnit.findAll(Node.class).stream()
- .anyMatch(n -> !n.getRange().isPresent());
- if (!hasNodeWithMissingRange) {
- return LexicalPreservingPrinter.setup(compilationUnit);
- }
- return compilationUnit;
- }
-
- public static Pair, Map>> extractAll(Path projectRootPath)
- throws IOException {
- ParserConfiguration config = new ParserConfiguration()
- .setStoreTokens(true)
- .setAttributeComments(true)
- .setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_21);
- SymbolSolverCollectionStrategy symbolSolverCollectionStrategy = new SymbolSolverCollectionStrategy(config);
- ProjectRoot projectRoot = symbolSolverCollectionStrategy.collect(projectRootPath);
- javaSymbolSolver = (JavaSymbolSolver) symbolSolverCollectionStrategy.getParserConfiguration()
- .getSymbolResolver().get();
- Map symbolTable = new LinkedHashMap<>();
- Map> parseProblems = new HashMap<>();
- for (SourceRoot sourceRoot : projectRoot.getSourceRoots()) {
- if (excludeSourceRoot(sourceRoot.getRoot())) {
- continue;
- }
- sourceRoot.setParserConfiguration(config);
- for (ParseResult parseResult : sourceRoot.tryToParse()) {
- if (parseResult.isSuccessful()) {
- CompilationUnit compilationUnit = safeLexicalPreservingPrinterSetup(parseResult.getResult().get());
- symbolTable.put(compilationUnit.getStorage().get().getPath().toString(),
- processCompilationUnit(compilationUnit));
- } else {
- parseProblems.put(sourceRoot.getRoot().toString(), parseResult.getProblems());
- }
- }
- }
- return Pair.of(symbolTable, parseProblems);
- }
-
- public static Pair, Map>> extractSingle(String code)
- throws IOException {
- Map symbolTable = new LinkedHashMap();
- Map parseProblems = new HashMap>();
- // Setting up symbol solvers
- CombinedTypeSolver combinedTypeSolver = new CombinedTypeSolver();
- combinedTypeSolver.add(new ReflectionTypeSolver());
-
- ParserConfiguration parserConfiguration = new ParserConfiguration()
- .setStoreTokens(true)
- .setAttributeComments(true)
- .setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_21);
- parserConfiguration.setSymbolResolver(new JavaSymbolSolver(combinedTypeSolver));
-
- JavaParser javaParser = new JavaParser(parserConfiguration);
- ParseResult parseResult = javaParser.parse(code);
- if (parseResult.isSuccessful()) {
- CompilationUnit compilationUnit = safeLexicalPreservingPrinterSetup(parseResult.getResult().get());
- Log.debug("Successfully parsed code. Now processing compilation unit");
- symbolTable.put("", processCompilationUnit(compilationUnit));
- } else {
- Log.error(parseResult.getProblems().toString());
- parseProblems.put("code", parseResult.getProblems());
- }
- return Pair.of(symbolTable, parseProblems);
- }
-
- /**
- * Parses the given set of Java source files from the given project and
- * constructs the symbol table.
- *
- * @param projectRootPath
- * @param javaFilePaths
- * @return
- * @throws IOException
- */
- public static Pair, Map>> extract(Path projectRootPath,
- List javaFilePaths) throws IOException {
-
- // create symbol solver and parser configuration
- SymbolSolverCollectionStrategy symbolSolverCollectionStrategy = new SymbolSolverCollectionStrategy();
- ProjectRoot projectRoot = symbolSolverCollectionStrategy.collect(projectRootPath);
- javaSymbolSolver = (JavaSymbolSolver) symbolSolverCollectionStrategy.getParserConfiguration()
- .getSymbolResolver().get();
- Log.info("Setting parser language level to JAVA_21");
- ParserConfiguration parserConfiguration = new ParserConfiguration()
- .setStoreTokens(true)
- .setAttributeComments(true)
- .setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_21);
- parserConfiguration.setSymbolResolver(javaSymbolSolver);
-
- // create java parser with the configuration
- JavaParser javaParser = new JavaParser(parserConfiguration);
-
- Map symbolTable = new LinkedHashMap();
- Map parseProblems = new HashMap>();
-
- // parse all given files and return pair of symbol table and parse problems
- for (Path javaFilePath : javaFilePaths) {
- ParseResult parseResult = javaParser.parse(javaFilePath);
- if (parseResult.isSuccessful()) {
- CompilationUnit compilationUnit = safeLexicalPreservingPrinterSetup(parseResult.getResult().get());
- System.out.println("Successfully parsed file: " + javaFilePath.toString());
- symbolTable.put(compilationUnit.getStorage().get().getPath().toString(),
- processCompilationUnit(compilationUnit));
- } else {
- Log.error(parseResult.getProblems().toString());
- parseProblems.put(javaFilePath.toString(), parseResult.getProblems());
- }
- }
- return Pair.of(symbolTable, parseProblems);
- }
-
- public static void main(String[] args) throws IOException {
- extractAll(Paths.get(args[0]));
- }
-
-}
diff --git a/src/main/java/com/ibm/cldk/SystemDependencyGraph.java b/src/main/java/com/ibm/cldk/SystemDependencyGraph.java
deleted file mode 100644
index 066f46ab..00000000
--- a/src/main/java/com/ibm/cldk/SystemDependencyGraph.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
-Copyright IBM Corporation 2023, 2024
-
-Licensed under the Apache Public License 2.0, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-
-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.
-*/
-
-package com.ibm.cldk;
-
-import static com.ibm.cldk.CodeAnalyzer.analysisLevel;
-import static com.ibm.cldk.utils.AnalysisUtils.*;
-
-import com.ibm.cldk.entities.*;
-import com.ibm.cldk.utils.AnalysisUtils;
-import com.ibm.cldk.utils.Log;
-import com.ibm.cldk.utils.ScopeUtils;
-import com.ibm.wala.cast.ir.ssa.AstIRFactory;
-import com.ibm.wala.cast.java.translator.jdt.ecj.ECJClassLoaderFactory;
-import com.ibm.wala.classLoader.CallSiteReference;
-import com.ibm.wala.classLoader.JavaLanguage;
-import com.ibm.wala.classLoader.Language;
-import com.ibm.wala.ipa.callgraph.*;
-import com.ibm.wala.ipa.callgraph.AnalysisOptions.ReflectionOptions;
-import com.ibm.wala.ipa.callgraph.impl.Util;
-import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
-import com.ibm.wala.ipa.cha.ClassHierarchyException;
-import com.ibm.wala.ipa.cha.ClassHierarchyFactory;
-import com.ibm.wala.ipa.cha.IClassHierarchy;
-import com.ibm.wala.ipa.modref.ModRef;
-import com.ibm.wala.ipa.slicer.MethodEntryStatement;
-import com.ibm.wala.ipa.slicer.SDG;
-import com.ibm.wala.ipa.slicer.Slicer;
-import com.ibm.wala.ipa.slicer.Statement;
-import com.ibm.wala.types.ClassLoaderReference;
-import com.ibm.wala.util.collections.HashMapFactory;
-import com.ibm.wala.util.graph.Graph;
-import com.ibm.wala.util.graph.GraphSlicer;
-import com.ibm.wala.util.graph.traverse.DFS;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.util.*;
-import java.util.function.BiFunction;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import org.apache.commons.io.output.NullOutputStream;
-import org.jgrapht.graph.DefaultDirectedGraph;
-import org.jgrapht.nio.json.JSONExporter;
-
-
-@Data
-abstract class Dependency {
- public CallableVertex source;
- public CallableVertex target;
-}
-
-@Data
-@EqualsAndHashCode(callSuper = true)
-class SDGDependency extends Dependency {
- public String sourceKind;
- public String destinationKind;
- public String type;
- public String weight;
-
- public SDGDependency(CallableVertex source, CallableVertex target, SystemDepEdge edge) {
- super.source = source;
- super.target = target;
- this.sourceKind = edge.getSourceKind();
- this.destinationKind = edge.getDestinationKind();
- this.type = edge.getType();
- this.weight = String.valueOf(edge.getWeight());
- }
-}
-
-@Data
-@EqualsAndHashCode(callSuper = true)
-class CallDependency extends Dependency {
- public String type;
- public String weight;
-
- public CallDependency(CallableVertex source, CallableVertex target, AbstractGraphEdge edge) {
- this.source = source;
- this.target = target;
- this.type = edge.toString();
- this.weight = String.valueOf(edge.getWeight());
- }
-}
-
-/**
- * The type Sdg 2 json.
- */
-public class SystemDependencyGraph {
-
- /**
- * Get a JGraphT graph exporter to save graph as JSON.
- *
- * @return the graph exporter
- */
-
- private static JSONExporter getGraphExporter() {
- JSONExporter exporter = new JSONExporter<>();
- exporter.setEdgeAttributeProvider(AbstractGraphEdge::getAttributes);
- exporter.setVertexAttributeProvider(CallableVertex::getAttributes);
- return exporter;
- }
-
- /**
- * Convert SDG to a formal Graph representation.
- *
- * @param callGraph
- * @return
- */
- private static org.jgrapht.Graph buildOnlyCallGraph(CallGraph callGraph) {
-
- org.jgrapht.Graph graph = new DefaultDirectedGraph<>(
- AbstractGraphEdge.class);
- callGraph.getEntrypointNodes()
- .forEach(p -> {
- // Get call statements that may execute in a given method
- Iterator outGoingCalls = p.iterateCallSites();
- outGoingCalls.forEachRemaining(n -> {
- callGraph.getPossibleTargets(p, n).stream()
- .filter(o -> AnalysisUtils.isApplicationClass(o.getMethod().getDeclaringClass()))
- .forEach(o -> {
-
- // Add the source nodes to the graph as vertices
- Map source = Optional.ofNullable(getCallableFromSymbolTable(p.getMethod())).orElseGet(() -> createAndPutNewCallableInSymbolTable(p.getMethod()));
- CallableVertex source_vertex = new CallableVertex(source);
-
- // Add the target nodes to the graph as vertices
- Map target = Optional.ofNullable(getCallableFromSymbolTable(o.getMethod())).orElseGet(() -> createAndPutNewCallableInSymbolTable(o.getMethod()));
- CallableVertex target_vertex = new CallableVertex(target);
-
- if (!source.equals(target) && target != null) {
- // Get the edge between the source and the target
- graph.addVertex(source_vertex);
- graph.addVertex(target_vertex);
- AbstractGraphEdge cgEdge = graph.getEdge(source_vertex, target_vertex);
- if (cgEdge instanceof CallEdge) {
- ((CallEdge) cgEdge).incrementWeight();
- } else {
- graph.addEdge(source_vertex, target_vertex, new CallEdge());
- }
- }
- });
- });
- });
-
- return graph;
- }
-
- /**
- * Construct a System Dependency Graph from a given input.
- *
- * @param input the input
- * @param dependencies the dependencies
- * @param build The build options
- * @return A List of triples containing the source, destination, and edge type
- * @throws IOException the io exception
- * @throws ClassHierarchyException the class hierarchy exception
- * @throws IllegalArgumentException the illegal argument exception
- * @throws CallGraphBuilderCancelException the call graph builder cancel
- * exception
- */
- public static List construct(
- String input, String dependencies, String build)
- throws IOException, ClassHierarchyException, IllegalArgumentException, CallGraphBuilderCancelException {
-
- // Initialize scope
- AnalysisScope scope = ScopeUtils.createScope(input, dependencies, build);
- IClassHierarchy cha = ClassHierarchyFactory.make(scope,
- new ECJClassLoaderFactory(scope.getExclusions()));
- Log.done("There were a total of " + cha.getNumberOfClasses() + " classes of which "
- + AnalysisUtils.getNumberOfApplicationClasses(cha) + " are application classes.");
-
- // Initialize javaee options
- AnalysisOptions options = new AnalysisOptions();
- Iterable entryPoints = AnalysisUtils.getEntryPoints(cha);
- options.setEntrypoints(entryPoints);
- options.getSSAOptions().setDefaultValues(com.ibm.wala.ssa.SymbolTable::getDefaultValue);
- options.setReflectionOptions(ReflectionOptions.NONE);
- IAnalysisCacheView cache = new AnalysisCacheImpl(AstIRFactory.makeDefaultFactory(),
- options.getSSAOptions());
-
- // Build call graph
- Log.info("Building call graph.");
-
- // Some fu to remove WALA's console out...
- PrintStream originalOut = System.out;
- PrintStream originalErr = System.err;
- long start_time = System.currentTimeMillis();
- CallGraph callGraph;
- CallGraphBuilder builder;
- try {
- System.setOut(new PrintStream(NullOutputStream.INSTANCE));
- System.setErr(new PrintStream(NullOutputStream.INSTANCE));
- builder = Util.makeRTABuilder(options, cache, cha);
- callGraph = builder.makeCallGraph(options, null);
- } finally {
- System.setOut(originalOut);
- System.setErr(originalErr);
- }
-
- Log.done("Finished construction of call graph. Took "
- + Math.ceil((double) (System.currentTimeMillis() - start_time) / 1000) + " seconds.");
-
- // set cyclomatic complexity for callables in the symbol table
- callGraph.forEach(cgNode -> {
- Callable callable = getCallableObjectFromSymbolTable(cgNode.getMethod()).getRight();
- if (callable != null) {
- callable.setCyclomaticComplexity(getCyclomaticComplexity(cgNode.getIR()));
- }
- });
-
- org.jgrapht.Graph graph;
-
- graph = buildOnlyCallGraph(callGraph);
-
- List edges = graph.edgeSet().stream()
- .map(abstractGraphEdge -> {
- CallableVertex source = graph.getEdgeSource(abstractGraphEdge);
- CallableVertex target = graph.getEdgeTarget(abstractGraphEdge);
- if (abstractGraphEdge instanceof CallEdge) {
- return new CallDependency(source, target, abstractGraphEdge);
- } else {
- return new SDGDependency(source, target, (SystemDepEdge) abstractGraphEdge);
- }
- })
- .collect(Collectors.toList());
-
- return edges;
- }
-}
diff --git a/src/main/java/com/ibm/cldk/entities/AbstractGraphEdge.java b/src/main/java/com/ibm/cldk/entities/AbstractGraphEdge.java
deleted file mode 100644
index 34fbc251..00000000
--- a/src/main/java/com/ibm/cldk/entities/AbstractGraphEdge.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
-Copyright IBM Corporation 2023, 2024
-
-Licensed under the Apache Public License 2.0, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-
-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.
-*/
-
-package com.ibm.cldk.entities;
-
-import static com.ibm.cldk.CodeAnalyzer.gson;
-
-import com.ibm.wala.ipa.callgraph.CGNode;
-import com.ibm.wala.ipa.slicer.Statement;
-import com.ibm.wala.shrike.shrikeCT.InvalidClassFileException;
-import com.ibm.wala.ssa.IR;
-import com.ibm.wala.ssa.SSAInstruction;
-import java.io.Serializable;
-import java.util.Map;
-import lombok.Getter;
-import org.jgrapht.nio.Attribute;
-
-/**
- * The type Abstract graph edge.
- */
-@Getter
-public abstract class AbstractGraphEdge implements Serializable {
- /**
- * The Context.
- * -- GETTER --
- * Gets context.
- *
- * @return the context
-
- */
- public final String context;
- /**
- * The Weight.
- * -- GETTER --
- * Gets weight.
- *
- * @return the weight
-
- */
- public Integer weight = 1;
-
- /**
- * Instantiates a new Abstract graph edge.
- */
- protected AbstractGraphEdge() {
- this(null);
- }
-
- /**
- * Instantiates a new Abstract graph edge.
- *
- * @param context the context
- */
- protected AbstractGraphEdge(String context) {
- this.context = context;
- }
-
- /**
- * Increment weight.
- */
- public void incrementWeight() {
- this.weight += 1;
- }
-
- /**
- * Gets id.
- *
- * @return the id
- */
- public Integer getId() {
- return this.hashCode();
- }
-
- /**
- * Gets statement position.
- *
- * @param statement the statement
- * @return the statement position
- */
- Integer getStatementPosition(Statement statement) {
- CGNode statementNode = statement.getNode();
- IR statementIR = statementNode.getIR();
- Integer pos = null;
- // TODO: check this assumption: the same source instruction maps to several
- // SSAInstructions,
- // therefore it is sufficient to return the position of the first statement.
- for (SSAInstruction inst : statementNode.getIR().getInstructions()) {
- try {
- pos = statementIR.getMethod().getSourcePosition(inst.iIndex()).getLastLine();
- return pos;
- } catch (InvalidClassFileException e) {
- throw new RuntimeException(e);
- } catch (NullPointerException npe) {
- return -1;
- }
- }
- return pos;
- }
-
- @Override
- public String toString() {
- return gson.toJson(this);
- }
-
- /**
- * Gets attributes.
- *
- * @return the attributes
- */
- public abstract Map getAttributes();
-
- public abstract Map getAttributesMap();
-}
diff --git a/src/main/java/com/ibm/cldk/entities/AbstractGraphVertex.java b/src/main/java/com/ibm/cldk/entities/AbstractGraphVertex.java
deleted file mode 100644
index 0381b23c..00000000
--- a/src/main/java/com/ibm/cldk/entities/AbstractGraphVertex.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.ibm.cldk.entities;
-
-import java.io.Serializable;
-import java.util.Map;
-import org.jgrapht.nio.Attribute;
-
-
-public abstract class AbstractGraphVertex implements Serializable {
-
- public abstract Map getAttributes();
-
- @Override
- public boolean equals(Object obj) {
- return super.equals(obj);
- }
-
- @Override
- public int hashCode() {
- return super.hashCode();
- }
-}
diff --git a/src/main/java/com/ibm/cldk/entities/CRUDOperation.java b/src/main/java/com/ibm/cldk/entities/CRUDOperation.java
deleted file mode 100644
index d318f10a..00000000
--- a/src/main/java/com/ibm/cldk/entities/CRUDOperation.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.ibm.cldk.entities;
-
-import com.ibm.cldk.javaee.utils.enums.CRUDOperationType;
-import com.ibm.cldk.utils.annotations.NotImplemented;
-import java.util.List;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-@Data
-@NoArgsConstructor
-@AllArgsConstructor
-public class CRUDOperation {
- private int lineNumber = -1;
- private CRUDOperationType operationType;
-
- @NotImplemented
- private String targetTable = null;
- @NotImplemented
- private List involvedColumns;
- @NotImplemented
- private String condition;
- @NotImplemented
- private List joinedTables;
-}
diff --git a/src/main/java/com/ibm/cldk/entities/CRUDQuery.java b/src/main/java/com/ibm/cldk/entities/CRUDQuery.java
deleted file mode 100644
index 45bd9ce4..00000000
--- a/src/main/java/com/ibm/cldk/entities/CRUDQuery.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.ibm.cldk.entities;
-
-import com.ibm.cldk.javaee.utils.enums.CRUDQueryType;
-import java.util.List;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-@Data
-@NoArgsConstructor
-@AllArgsConstructor
-public class CRUDQuery {
- private int lineNumber = -1;
- private List queryArguments;
- private CRUDQueryType queryType;
-}
diff --git a/src/main/java/com/ibm/cldk/entities/CallEdge.java b/src/main/java/com/ibm/cldk/entities/CallEdge.java
deleted file mode 100644
index b4c407e2..00000000
--- a/src/main/java/com/ibm/cldk/entities/CallEdge.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
-Copyright IBM Corporation 2023, 2024
-
-Licensed under the Apache Public License 2.0, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-
-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.
-*/
-
-package com.ibm.cldk.entities;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-import org.jgrapht.nio.Attribute;
-import org.jgrapht.nio.DefaultAttribute;
-
-/**
- * The type Call edge.
- */
-public class CallEdge extends AbstractGraphEdge {
- /**
- * The constant serialVersionUID.
- */
- public static final long serialVersionUID = -8284030936836318929L;
- /**
- * The Type.
- */
- public final String type;
-
- /**
- * Instantiates a new Call edge.
- */
- public CallEdge() {
- super();
- this.type = toString();
- }
-
- /**
- * Instantiates a new Call edge.
- *
- * @param context the context
- */
- public CallEdge(String context) {
- super(context);
- this.type = toString();
- }
-
- @Override
- public String toString() {
- return "CALL_DEP";
- }
-
- @Override
- public boolean equals(Object o) {
- return (o instanceof CallEdge) && (toString().equals(o.toString()));
- }
-
- public Map getAttributes() {
- Map map = new LinkedHashMap<>();
- map.put("type", DefaultAttribute.createAttribute(toString()));
- map.put("weight", DefaultAttribute.createAttribute(String.valueOf(getWeight())));
- return map;
- }
-
- public Map getAttributesMap() {
- Map map = new LinkedHashMap<>();
- map.put("type", toString());
- map.put("weight", String.valueOf(getWeight()));
- return map;
- }
-}
diff --git a/src/main/java/com/ibm/cldk/entities/CallSite.java b/src/main/java/com/ibm/cldk/entities/CallSite.java
deleted file mode 100644
index 0c45ee80..00000000
--- a/src/main/java/com/ibm/cldk/entities/CallSite.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package com.ibm.cldk.entities;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import lombok.Data;
-
-/**
- * Represents a call site within source code, encapsulating information about method invocations
- * and their contextual details.
- *
- *
- * A call site contains information about the method being called, its receiver,
- * arguments, return type, and various properties that characterize the method call.
- * It also tracks the position of the call site within the source file.
- *
- *
- *
- * This class leverages Lombok's {@code @Data} annotation to automatically generate
- * getters, setters, {@code toString()}, {@code equals()}, and {@code hashCode()} methods.
- *
- *
- * @author Rahul Krishna
- * @version 2.3.0
- */
-@Data
-@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
-public class CallSite {
- /** Name of the method being called */
- private String methodName;
-
- /** Comment associated with the call site */
- private Comment comment;
-
- /** Expression representing the receiver of the method call */
- private String receiverExpr;
-
- /** Type of the receiver object */
- private String receiverType;
-
- /** List of argument types for the method call */
- private List argumentTypes;
-
- /** List of argument expressions for the method call */
- private List argumentExpr;
-
- /** Return type of the called method */
- private String returnType;
-
- /** Full signature of the callee method */
- private String calleeSignature;
-
- /** Flag indicating if the method has public access */
- private boolean isPublic = false;
-
- /** Flag indicating if the method has protected access */
- private boolean isProtected = false;
-
- /** Flag indicating if the method has private access */
- private boolean isPrivate = false;
-
- /** Flag indicating if the method has unspecified access */
- private boolean isUnspecified = false;
-
- /** Flag indicating if this is a static method call */
- private boolean isStaticCall;
-
- /** Flag indicating if this is a constructor call */
- private boolean isConstructorCall;
-
- /** CRUD operation associated with this call site, if any */
- private CRUDOperation crudOperation = null;
-
- /** CRUD query associated with this call site, if any */
- private CRUDQuery crudQuery = null;
-
- /** Starting line number of the call site in the source file */
- private int startLine;
-
- /** Starting column number of the call site in the source file */
- private int startColumn;
-
- /** Ending line number of the call site in the source file */
- private int endLine;
-
- /** Ending column number of the call site in the source file */
- private int endColumn;
-}
diff --git a/src/main/java/com/ibm/cldk/entities/Callable.java b/src/main/java/com/ibm/cldk/entities/Callable.java
deleted file mode 100644
index dc25ac99..00000000
--- a/src/main/java/com/ibm/cldk/entities/Callable.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package com.ibm.cldk.entities;
-
-import java.util.ArrayList;
-import java.util.List;
-import lombok.Data;
-
-/**
- * Represents a callable entity in the source code, such as a method or constructor.
- *
- *
- * This class encapsulates information about the callable's file path, signature, comments,
- * annotations, modifiers, thrown exceptions, declaration, parameters, code, position within
- * the source file, return type, and various properties that characterize the callable.
- *
- *
- *
- * This class leverages Lombok's {@code @Data} annotation to automatically generate
- * getters, setters, {@code toString()}, {@code equals()}, and {@code hashCode()} methods.
- *
- *
- *
- * Example usage:
- *
- * Callable callable = new Callable();
- * callable.setFilePath("src/main/java/com/ibm/cldk/entities/Example.java");
- * callable.setSignature("public void exampleMethod()");
- * callable.setStartLine(10);
- * callable.setEndLine(20);
- * callable.setReturnType("void");
- * callable.setConstructor(false);
- *
- *
- *
- * @author Rahul Krishna
- * @version 2.3.0
- */
-@Data
-public class Callable {
- /** The file path where the callable entity is defined. */
- private String filePath;
-
- /** The signature of the callable entity. */
- private String signature;
-
- /** A list of comments associated with the callable entity. */
- private List comments;
-
- /** A list of annotations applied to the callable entity. */
- private List annotations;
-
- /** A list of modifiers applied to the callable entity (e.g., public, private). */
- private List modifiers;
-
- /** A list of exceptions thrown by the callable entity. */
- private List thrownExceptions;
-
- /** The declaration of the callable entity. */
- private String declaration;
-
- /** A list of parameters for the callable entity. */
- private List parameters;
-
- /** The code of the callable entity. */
- private String code;
-
- /** The starting line number of the callable entity in the source file. */
- private int startLine;
-
- /** The ending line number of the callable entity in the source file. */
- private int endLine;
-
- /** The starting line number of the callable code in the source file. */
- private int codeStartLine;
-
- /** The return type of the callable entity. */
- private String returnType = null;
-
- /** Indicates whether the callable entity is implicit. */
- private boolean isImplicit = false;
-
- /** Indicates whether the callable entity is a constructor. */
- private boolean isConstructor = false;
-
- /** A list of types referenced by the callable entity. */
- private List referencedTypes;
-
- /** A list of fields accessed by the callable entity. */
- private List accessedFields;
-
- /** A list of call sites within the callable entity. */
- private List callSites;
-
- /** A list of variable declarations within the callable entity. */
- private List variableDeclarations;
-
- /** A list of CRUD operations associated with the callable entity. */
- private List crudOperations = new ArrayList<>();
-
- /** A list of CRUD queries associated with the callable entity. */
- private List crudQueries = new ArrayList<>();
-
- /** The cyclomatic complexity of the callable entity. */
- private int cyclomaticComplexity;
-
- /** Indicates whether the callable entity is an entry point. */
- private boolean isEntrypoint = false;
-}
diff --git a/src/main/java/com/ibm/cldk/entities/CallableVertex.java b/src/main/java/com/ibm/cldk/entities/CallableVertex.java
deleted file mode 100644
index 7e7e25a5..00000000
--- a/src/main/java/com/ibm/cldk/entities/CallableVertex.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.ibm.cldk.entities;
-
-import static com.ibm.cldk.CodeAnalyzer.gson;
-
-import java.util.Map;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import org.jgrapht.nio.Attribute;
-import org.jgrapht.nio.DefaultAttribute;
-
-@Data
-@EqualsAndHashCode(callSuper = true)
-public class CallableVertex extends AbstractGraphVertex {
- private String filePath;
- private String typeDeclaration;
- private String signature;
- private String callableDeclaration;
-
- public CallableVertex(Map callable) {
- this.filePath = callable.get("filePath");
- this.typeDeclaration = callable.get("typeDeclaration");
- this.signature = callable.get("signature");
- this.callableDeclaration = callable.get("callableDeclaration");
- }
-
- @Override
- public String toString() {
- return gson.toJson(this);
- }
-
- @Override
- public Map getAttributes() {
- return Map.ofEntries(
- Map.entry("filePath", DefaultAttribute.createAttribute(getFilePath())),
- Map.entry("typeDeclaration", DefaultAttribute.createAttribute(getTypeDeclaration())),
- Map.entry("signature", DefaultAttribute.createAttribute(getSignature())),
- Map.entry("callableDeclaration", DefaultAttribute.createAttribute(getCallableDeclaration())));
- }
-}
diff --git a/src/main/java/com/ibm/cldk/entities/Comment.java b/src/main/java/com/ibm/cldk/entities/Comment.java
deleted file mode 100644
index e248a1a0..00000000
--- a/src/main/java/com/ibm/cldk/entities/Comment.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.ibm.cldk.entities;
-
-import lombok.Data;
-
-/**
- * Represents a comment entity extracted from source code.
- * This class encapsulates information about the content, position,
- * and type of a comment within a source file.
- *
- *
- * The comment can be of various types, including Javadoc, block comments, or line comments.
- * The class also keeps track of the comment's position within the file (line and column numbers).
- *
- *
- *
- * This class leverages Lombok's {@code @Data} annotation to automatically generate
- * getters, setters, {@code toString()}, {@code equals()}, and {@code hashCode()} methods.
- *
- *
- * Example usage:
- *
- * Comment comment = new Comment();
- * comment.setContent("This is a sample comment.");
- * comment.setStartLine(10);
- * comment.setEndLine(12);
- * comment.setJavadoc(true);
- *
- *
- * @author Rahul Krishna
- * @version 2.3.0
- */
-@Data
-public class Comment {
-
- /**
- * The textual content of the comment.
- */
- private String content;
-
- /**
- * The starting line number of the comment in the source file.
- *
- * Defaults to {@code -1} if the position is unknown.
- *
- */
- private int startLine = -1;
-
- /**
- * The ending line number of the comment in the source file.
- *
- * Defaults to {@code -1} if the position is unknown.
- *
- */
- private int endLine = -1;
-
- /**
- * The starting column number of the comment in the source file.
- *
- * Defaults to {@code -1} if the position is unknown.
- *
- */
- private int startColumn = -1;
-
- /**
- * The ending column number of the comment in the source file.
- *
- * Defaults to {@code -1} if the position is unknown.
- *
- */
- private int endColumn = -1;
-
- /**
- * Indicates whether the comment is a Javadoc comment.
- *
- * Javadoc comments are special block comments used for generating documentation
- * and typically start with {@code /**}.
- *
- */
- private boolean isJavadoc = false;
-}
diff --git a/src/main/java/com/ibm/cldk/entities/EnumConstant.java b/src/main/java/com/ibm/cldk/entities/EnumConstant.java
deleted file mode 100644
index 0ac11ff9..00000000
--- a/src/main/java/com/ibm/cldk/entities/EnumConstant.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.ibm.cldk.entities;
-
-import java.util.List;
-import lombok.Data;
-
-@Data
-public class EnumConstant {
- private String name;
- private List arguments;
-}
diff --git a/src/main/java/com/ibm/cldk/entities/Field.java b/src/main/java/com/ibm/cldk/entities/Field.java
deleted file mode 100644
index e1f9f6ad..00000000
--- a/src/main/java/com/ibm/cldk/entities/Field.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.ibm.cldk.entities;
-
-import java.util.List;
-import lombok.Data;
-
-@Data
-public class Field {
- private Comment comment;
- private String name;
- private String type;
- private Integer startLine;
- private Integer endLine;
- private List variables;
- private List modifiers;
- private List annotations;
-}
diff --git a/src/main/java/com/ibm/cldk/entities/Import.java b/src/main/java/com/ibm/cldk/entities/Import.java
deleted file mode 100644
index 6688b9a6..00000000
--- a/src/main/java/com/ibm/cldk/entities/Import.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.ibm.cldk.entities;
-
-import lombok.Data;
-
-/** Represents an import declaration in a Java compilation unit. */
-@Data
-public class Import {
- private String path;
- private boolean isStatic = false;
- private boolean isWildcard = false;
-}
diff --git a/src/main/java/com/ibm/cldk/entities/InitializationBlock.java b/src/main/java/com/ibm/cldk/entities/InitializationBlock.java
deleted file mode 100644
index 8c106477..00000000
--- a/src/main/java/com/ibm/cldk/entities/InitializationBlock.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.ibm.cldk.entities;
-
-import java.util.List;
-import java.util.stream.Collector;
-import lombok.Data;
-
-@Data
-public class InitializationBlock {
- private String filePath;
- private List comments;
- private List annotations;
- private List thrownExceptions;
- private String code;
- private int startLine;
- private int endLine;
- private boolean isStatic;
- private List referencedTypes;
- private List accessedFields;
- private List callSites;
- private List variableDeclarations;
- private int cyclomaticComplexity;
-
-}
diff --git a/src/main/java/com/ibm/cldk/entities/JavaCompilationUnit.java b/src/main/java/com/ibm/cldk/entities/JavaCompilationUnit.java
deleted file mode 100644
index 0c71fa44..00000000
--- a/src/main/java/com/ibm/cldk/entities/JavaCompilationUnit.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.ibm.cldk.entities;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import lombok.Data;
-
-@Data
-public class JavaCompilationUnit {
- private String filePath;
- private String packageName;
- private List comments = new ArrayList<>();
- private List imports;
- private Map typeDeclarations;
- private boolean isModified;
-}
diff --git a/src/main/java/com/ibm/cldk/entities/ParameterInCallable.java b/src/main/java/com/ibm/cldk/entities/ParameterInCallable.java
deleted file mode 100644
index 9699dd9c..00000000
--- a/src/main/java/com/ibm/cldk/entities/ParameterInCallable.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package com.ibm.cldk.entities;
-
-import java.util.List;
-import lombok.Data;
-
-/**
- * Represents a parameter in a callable entity (e.g., method or constructor).
- *
- *
- * This class encapsulates information about the parameter's type, name, annotations,
- * modifiers, and its position within the source file.
- *
- *
- *
- * This class leverages Lombok's {@code @Data} annotation to automatically generate
- * getters, setters, {@code toString()}, {@code equals()}, and {@code hashCode()} methods.
- *
- *
- *
- * Example usage:
- *
- * ParameterInCallable param = new ParameterInCallable();
- * param.setType("String");
- * param.setName("exampleParam");
- * param.setAnnotations(Arrays.asList("NotNull"));
- * param.setModifiers(Arrays.asList("final"));
- * param.setStartLine(10);
- * param.setEndLine(10);
- * param.setStartColumn(5);
- * param.setEndColumn(20);
- *
- *
- *
- * @author Rahul Krishna
- * @version 2.3.0
- */
-@Data
-public class ParameterInCallable {
- /** The type of the parameter (e.g., int, String). */
- private String type;
-
- /** The name of the parameter. */
- private String name;
-
- /** A list of annotations applied to the parameter. */
- private List annotations;
-
- /** A list of modifiers applied to the parameter (e.g., final, static). */
- private List modifiers;
-
- /** The starting line number of the parameter in the source file. */
- private int startLine;
-
- /** The ending line number of the parameter in the source file. */
- private int endLine;
-
- /** The starting column number of the parameter in the source file. */
- private int startColumn;
-
- /** The ending column number of the parameter in the source file. */
- private int endColumn;
-}
diff --git a/src/main/java/com/ibm/cldk/entities/RecordComponent.java b/src/main/java/com/ibm/cldk/entities/RecordComponent.java
deleted file mode 100644
index 48052a16..00000000
--- a/src/main/java/com/ibm/cldk/entities/RecordComponent.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.ibm.cldk.entities;
-
-import java.util.ArrayList;
-import java.util.List;
-import lombok.Data;
-
-/**
- * Represents a component of a record in the source code.
- *
- *
- * This class encapsulates information about the component's name, type, modifiers,
- * annotations, default value, and whether it is a varargs parameter.
- *
- *
- *
- * This class leverages Lombok's {@code @Data} annotation to automatically generate
- * getters, setters, {@code toString()}, {@code equals()}, and {@code hashCode()} methods.
- *
- *
- *
- * Example usage:
- *
- * RecordComponent component = new RecordComponent();
- * component.setName("exampleComponent");
- * component.setType("String");
- * component.setModifiers(Arrays.asList("private"));
- * component.setAnnotations(Arrays.asList("NotNull"));
- * component.setDefaultValue("defaultValue");
- * component.setVarArgs(false);
- *
- *
- *
- * @author Rahul Krishna
- * @version 2.3.0
- */
-@Data
-public class RecordComponent {
- /** The comment associated with the record component. */
- private Comment comment;
-
- /** The name of the record component. */
- private String name;
-
- /** The type of the record component. */
- private String type;
-
- /** A list of modifiers applied to the record component (e.g., final, static). */
- private List modifiers;
-
- /** A list of annotations applied to the record component. */
- private List annotations = new ArrayList<>();
-
- /** The default value of the record component, stored as a string representation. */
- private Object defaultValue = null;
-
- /** Indicates whether the record component is a varargs parameter. */
- private boolean isVarArgs = false;
-}
diff --git a/src/main/java/com/ibm/cldk/entities/SystemDepEdge.java b/src/main/java/com/ibm/cldk/entities/SystemDepEdge.java
deleted file mode 100644
index 00261840..00000000
--- a/src/main/java/com/ibm/cldk/entities/SystemDepEdge.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
-Copyright IBM Corporation 2023, 2024
-
-Licensed under the Apache Public License 2.0, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-
-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.
-*/
-
-package com.ibm.cldk.entities;
-
-import com.ibm.wala.ipa.slicer.Statement;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import org.apache.commons.lang3.builder.HashCodeBuilder;
-import org.jgrapht.nio.Attribute;
-import org.jgrapht.nio.DefaultAttribute;
-
-/**
- * The type System dep edge.
- */
-public class SystemDepEdge extends AbstractGraphEdge {
- /**
- * The constant serialVersionUID.
- */
- public static final long serialVersionUID = -8284030936836318929L;
- /**
- * The Source pos.
- */
- public final Integer sourcePos;
- /**
- * The Destination pos.
- */
- public final Integer destinationPos;
- /**
- * The Type.
- */
- public final String type;
- public final String sourceKind;
- public final String destinationKind;
-
- /**
- * Instantiates a new System dep edge.
- *
- * @param sourceStatement the source statement
- * @param destinationStatement the destination statement
- * @param type the type
- */
- public SystemDepEdge(Statement sourceStatement, Statement destinationStatement, String type) {
- super();
- this.sourceKind = sourceStatement.getKind().toString();
- this.destinationKind = destinationStatement.getKind().toString();
- this.type = type;
- this.sourcePos = getStatementPosition(sourceStatement);
- this.destinationPos = getStatementPosition(destinationStatement);
- }
-
- @Override
- public int hashCode() {
- return new HashCodeBuilder(17, 37).append(sourcePos).append(destinationPos).append(context).append(type)
- .build();
- }
-
- @Override
- public boolean equals(Object o) {
- return (o instanceof SystemDepEdge) && (this.toString().equals(o.toString()))
- && Integer.valueOf(this.hashCode()).equals(o.hashCode())
- && this.type.equals(((SystemDepEdge) o).getType());
- }
-
-
- public String getSourceKind() {
- return sourceKind;
- }
-
- public String getDestinationKind() {
- return destinationKind;
- }
-
- /**
- * Gets type.
- *
- * @return the type
- */
- public String getType() {
- return type;
- }
-
- /**
- * Gets source pos.
- *
- * @return the source pos
- */
- public Integer getSourcePos() {
- return sourcePos;
- }
-
- /**
- * Gets destination pos.
- *
- * @return the destination pos
- */
- public Integer getDestinationPos() {
- return destinationPos;
- }
-
- public Map getAttributes() {
- Map map = new LinkedHashMap<>();
- map.put("source_kind", DefaultAttribute.createAttribute(getSourceKind()));
- map.put("type", DefaultAttribute.createAttribute(getType()));
- map.put("destination_kind", DefaultAttribute.createAttribute(getDestinationKind()));
- map.put("weight", DefaultAttribute.createAttribute(String.valueOf(getWeight())));
- return map;
- }
-
- public Map getAttributesMap() {
- Map map = new LinkedHashMap<>();
- map.put("source_kind", getSourceKind());
- map.put("type", getType());
- map.put("destination_kind", getDestinationKind());
- map.put("weight", String.valueOf(getWeight()));
- return map;
- }
-}
diff --git a/src/main/java/com/ibm/cldk/entities/Type.java b/src/main/java/com/ibm/cldk/entities/Type.java
deleted file mode 100644
index d7ae5cab..00000000
--- a/src/main/java/com/ibm/cldk/entities/Type.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.ibm.cldk.entities;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import lombok.Data;
-
-/**
- * Represents a type in the system with various characteristics.
- * This class uses Lombok's @Data annotation to generate boilerplate code.
- *
- * @author Rahul Krishna
- * @version 2.3.0
- */
-@Data
-public class Type {
- /** Indicates if this type is nested. */
- private boolean isNestedType;
-
- /** Indicates if this type is a class or interface declaration. */
- private boolean isClassOrInterfaceDeclaration;
-
- /** Indicates if this type is an enum declaration. */
- private boolean isEnumDeclaration;
-
- /** Indicates if this type is an annotation declaration. */
- private boolean isAnnotationDeclaration;
-
- /** Indicates if this type is a record declaration. */
- private boolean isRecordDeclaration;
-
- /** Indicates if this type is an interface. */
- private boolean isInterface;
-
- /** Indicates if this type is an inner class. */
- private boolean isInnerClass;
-
- /** Indicates if this type is a local class. */
- private boolean isLocalClass;
-
- /** List of types that this type extends. */
- private List extendsList = new ArrayList<>();
-
- /** List of comments associated with this type. */
- private List comments;
-
- /** List of interfaces that this type implements. */
- private List implementsList = new ArrayList<>();
-
- /** List of modifiers for this type. */
- private List modifiers = new ArrayList<>();
-
- /** List of annotations for this type. */
- private List annotations = new ArrayList<>();
-
- /** The parent type of this type. */
- private String parentType;
-
- /** List of nested type declarations within this type. */
- private List nestedTypeDeclarations = new ArrayList<>();
-
- /** Map of callable declarations within this type. */
- private Map callableDeclarations = new HashMap<>();
-
- /** List of field declarations within this type. */
- private List fieldDeclarations = new ArrayList<>();
-
- /** List of enum constants within this type. */
- private List enumConstants = new ArrayList<>();
-
- /** List of record components within this type. */
- private List recordComponents = new ArrayList<>();
-
- /** List of initialization blocks within this type. */
- private List initializationBlocks = new ArrayList<>();
-
- /** Indicates if this type is an entry point class. */
- private boolean isEntrypointClass = false;
-}
diff --git a/src/main/java/com/ibm/cldk/entities/VariableDeclaration.java b/src/main/java/com/ibm/cldk/entities/VariableDeclaration.java
deleted file mode 100644
index c9284aea..00000000
--- a/src/main/java/com/ibm/cldk/entities/VariableDeclaration.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.ibm.cldk.entities;
-
-import lombok.Data;
-
-/**
- * Represents a variable declaration in the source code.
- *
- *
- * This class encapsulates information about the variable's name, type, initializer,
- * and its position within the source file. It also includes an optional comment
- * associated with the variable declaration.
- *
- *
- *
- * This class leverages Lombok's {@code @Data} annotation to automatically generate
- * getters, setters, {@code toString()}, {@code equals()}, and {@code hashCode()} methods.
- *
- *
- *
- * Example usage:
- *
- * VariableDeclaration varDecl = new VariableDeclaration();
- * varDecl.setName("exampleVar");
- * varDecl.setType("String");
- * varDecl.setInitializer("\"defaultValue\"");
- * varDecl.setStartLine(10);
- * varDecl.setEndLine(10);
- * varDecl.setStartColumn(5);
- * varDecl.setEndColumn(20);
- *
- *
- *
- * @author Rahul Krishna
- * @version 2.3.0
- */
-@Data
-public class VariableDeclaration {
- /** The comment associated with the variable declaration. */
- private Comment comment;
-
- /** The name of the variable. */
- private String name;
-
- /** The type of the variable. */
- private String type;
-
- /** The initializer of the variable, stored as a string representation. */
- private String initializer;
-
- /** The starting line number of the variable declaration in the source file. */
- private int startLine = -1;
-
- /** The starting column number of the variable declaration in the source file. */
- private int startColumn = -1;
-
- /** The ending line number of the variable declaration in the source file. */
- private int endLine = -1;
-
- /** The ending column number of the variable declaration in the source file. */
- private int endColumn = -1;
-}
diff --git a/src/main/java/com/ibm/cldk/javaee/CRUDFinderFactory.java b/src/main/java/com/ibm/cldk/javaee/CRUDFinderFactory.java
deleted file mode 100644
index 02411db2..00000000
--- a/src/main/java/com/ibm/cldk/javaee/CRUDFinderFactory.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.ibm.cldk.javaee;
-
-import com.ibm.cldk.javaee.jakarta.JPACRUDFinder;
-import com.ibm.cldk.javaee.jdbc.JDBCCRUDFinder;
-import com.ibm.cldk.javaee.spring.SpringCRUDFinder;
-import com.ibm.cldk.javaee.utils.interfaces.AbstractCRUDFinder;
-import java.util.stream.Stream;
-import org.apache.commons.lang3.NotImplementedException;
-
-public class CRUDFinderFactory {
- public static AbstractCRUDFinder getCRUDFinder(String framework) {
- switch (framework.toLowerCase()) {
- case "jpa":
- case "jakarta":
- return new JPACRUDFinder();
- case "spring":
- case "springboot":
- return new SpringCRUDFinder();
- case "jdbc":
- return new JDBCCRUDFinder();
- case "camel":
- throw new NotImplementedException("Camel CRUD finder not implemented yet");
- case "struts":
- throw new NotImplementedException("Struts CRUD finder not implemented yet");
- default:
- throw new IllegalArgumentException("Unknown framework: " + framework);
- }
- }
-
- public static Stream getCRUDFinders() {
- return Stream.of(new JPACRUDFinder(), new SpringCRUDFinder(), new JDBCCRUDFinder());
- }
-}
diff --git a/src/main/java/com/ibm/cldk/javaee/EntrypointsFinderFactory.java b/src/main/java/com/ibm/cldk/javaee/EntrypointsFinderFactory.java
deleted file mode 100644
index 96da1838..00000000
--- a/src/main/java/com/ibm/cldk/javaee/EntrypointsFinderFactory.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.ibm.cldk.javaee;
-
-import com.ibm.cldk.javaee.camel.CamelEntrypointFinder;
-import com.ibm.cldk.javaee.jakarta.JakartaEntrypointFinder;
-import com.ibm.cldk.javaee.jax.JaxRsEntrypointFinder;
-import com.ibm.cldk.javaee.spring.SpringEntrypointFinder;
-import com.ibm.cldk.javaee.struts.StrutsEntrypointFinder;
-import com.ibm.cldk.javaee.utils.interfaces.AbstractEntrypointFinder;
-import java.util.stream.Stream;
-import org.apache.commons.lang3.NotImplementedException;
-
-public class EntrypointsFinderFactory {
- public static AbstractEntrypointFinder getEntrypointFinder(String framework) {
- switch (framework.toLowerCase()) {
- case "jakarta":
- return new JakartaEntrypointFinder();
- case "spring":
- return new SpringEntrypointFinder();
- case "camel":
- throw new NotImplementedException("Camel CRUD finder not implemented yet");
- case "struts":
- throw new NotImplementedException("Struts CRUD finder not implemented yet");
- default:
- throw new IllegalArgumentException("Unknown framework: " + framework);
- }
- }
-
- public static Stream getEntrypointFinders() {
- return Stream.of(new JakartaEntrypointFinder(), new StrutsEntrypointFinder(), new SpringEntrypointFinder(), new CamelEntrypointFinder(), new JaxRsEntrypointFinder());
- }
-}
diff --git a/src/main/java/com/ibm/cldk/javaee/camel/CamelEntrypointFinder.java b/src/main/java/com/ibm/cldk/javaee/camel/CamelEntrypointFinder.java
deleted file mode 100644
index dfdc6935..00000000
--- a/src/main/java/com/ibm/cldk/javaee/camel/CamelEntrypointFinder.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.ibm.cldk.javaee.camel;
-
-import com.github.javaparser.ast.body.CallableDeclaration;
-import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
-import com.github.javaparser.ast.body.TypeDeclaration;
-import com.github.javaparser.resolution.UnsolvedSymbolException;
-import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
-import com.ibm.cldk.javaee.utils.interfaces.AbstractEntrypointFinder;
-import com.ibm.cldk.utils.Log;
-import com.ibm.cldk.utils.annotations.NotImplemented;
-
-@NotImplemented(comment = "This class is not implemented yet. Leaving this here to refactor entrypoint detection.")
-public class CamelEntrypointFinder extends AbstractEntrypointFinder {
- /**
- * Detect if the method is an entrypoint.
- *
- * @param typeDecl@return True if the method is an entrypoint, false otherwise.
- */
- @Override
- public boolean isEntrypointClass(TypeDeclaration typeDecl) {
- if (!(typeDecl instanceof ClassOrInterfaceDeclaration)) {
- return false;
- }
-
- ClassOrInterfaceDeclaration classDecl = (ClassOrInterfaceDeclaration) typeDecl;
-
- // Check Camel class annotations
- if (classDecl.getAnnotations().stream().anyMatch(a -> a.getNameAsString().contains("Component"))) {
- return true;
- }
-
- // Check Camel parent classes and interfaces
- try {
- ResolvedReferenceTypeDeclaration resolved = classDecl.resolve();
- return resolved.getAllAncestors().stream().anyMatch(ancestor -> {
- String name = ancestor.getQualifiedName();
- return name.contains("RouteBuilder") || name.contains("Processor") || name.contains("Producer")
- || name.contains("Consumer");
- });
- } catch (RuntimeException e) {
- Log.warn("Could not resolve class: " + e.getMessage());
- }
-
- return false;
- }
-
- /**
- * @param callableDecl
- * @return
- */
- @Override
- public boolean isEntrypointMethod(CallableDeclaration callableDecl) {
- return false;
- }
-}
diff --git a/src/main/java/com/ibm/cldk/javaee/jakarta/JPACRUDFinder.java b/src/main/java/com/ibm/cldk/javaee/jakarta/JPACRUDFinder.java
deleted file mode 100644
index 1f9fa4cf..00000000
--- a/src/main/java/com/ibm/cldk/javaee/jakarta/JPACRUDFinder.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.ibm.cldk.javaee.jakarta;
-
-import com.ibm.cldk.javaee.utils.enums.CRUDOperationType;
-import com.ibm.cldk.javaee.utils.enums.JPAQueryMethod;
-import com.ibm.cldk.javaee.utils.interfaces.AbstractCRUDFinder;
-import java.util.List;
-import java.util.Optional;
-
-public class JPACRUDFinder extends AbstractCRUDFinder {
-
- // Detect CREATE Operation
- @Override
- public boolean isCreateOperation(String receiverType, String name) {
- return receiverType.endsWith("EntityManager") && name.equals("persist");
- }
-
- // Detect DELETE Operation
- @Override
- public boolean isDeleteOperation(String receiverType, String name) {
- return receiverType.endsWith("EntityManager") && name.equals("remove");
- }
-
- // Detect UPDATE Operation, including query executions
- @Override
- public boolean isUpdateOperation(String receiverType, String name) {
- if (receiverType.endsWith("Query")) {
- Optional operation = JPAQueryMethod.getOperationForMethod(name);
- // There's a caveat here because UPDATE/DELETE operations are both represented by the same method.
- // See https://github.com/codellm-devkit/codeanalyzer-java/issues/100#issuecomment-2644492440
- return operation.isPresent() && (operation.get() == CRUDOperationType.UPDATE);
- }
- return receiverType.endsWith("EntityManager") && name.equals("merge");
- }
-
- // Detect READ Operation, including query executions using the JPAQueryMethod enum
- @Override
- public boolean isReadOperation(String receiverType, String name) {
- if (receiverType.endsWith("EntityManager") && name.equals("find")) {
- return true;
- }
-
- if (receiverType.endsWith("Query")) {
- Optional operation = JPAQueryMethod.getOperationForMethod(name);
- return operation.isPresent() && operation.get() == CRUDOperationType.READ;
- }
-
- return false;
- }
-
- // Detect CRUD Query Creation (Only query definitions, not execution)
- @Override
- public boolean isCRUDQueryCreation(String declaringType, String methodName) {
- return declaringType.endsWith("EntityManager") &&
- (methodName.equals("createQuery") || methodName.equals("createNamedQuery"));
- }
-
- /**
- * @param declaringType
- * @param nameAsString
- * @param arguments
- * @return
- */
- @Override
- public boolean isReadQuery(String declaringType, String nameAsString, Optional> arguments) {
- return isCRUDQueryCreation(declaringType, nameAsString) && arguments.stream().anyMatch(args -> {
- String query = args.get(0).toLowerCase();
- return query.startsWith("select");
- });
- }
-
- /**
- * @param declaringType
- * @param nameAsString
- * @param arguments
- * @return
- */
- @Override
- public boolean isWriteQuery(String declaringType, String nameAsString, Optional> arguments) {
- return isCRUDQueryCreation(declaringType, nameAsString) && arguments.stream().anyMatch(args -> {
- String query = args.get(0).toLowerCase();
- return query.startsWith("update") || query.startsWith("delete") || query.startsWith("insert");
- });
- }
-
- /**
- * @param declaringType
- * @param nameAsString
- * @param arguments
- * @return
- */
- @Override
- public boolean isNamedQuery(String declaringType, String nameAsString, Optional> arguments) {
- return declaringType.endsWith("EntityManager") && nameAsString.equals("createNamedQuery");
- }
-}
diff --git a/src/main/java/com/ibm/cldk/javaee/jakarta/JakartaEntrypointFinder.java b/src/main/java/com/ibm/cldk/javaee/jakarta/JakartaEntrypointFinder.java
deleted file mode 100644
index 91e5e9cf..00000000
--- a/src/main/java/com/ibm/cldk/javaee/jakarta/JakartaEntrypointFinder.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.ibm.cldk.javaee.jakarta;
-
-import com.github.javaparser.ast.NodeList;
-import com.github.javaparser.ast.body.CallableDeclaration;
-import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
-import com.github.javaparser.ast.body.Parameter;
-import com.github.javaparser.ast.body.TypeDeclaration;
-import com.github.javaparser.ast.type.ClassOrInterfaceType;
-import com.ibm.cldk.javaee.utils.interfaces.AbstractEntrypointFinder;
-
-@SuppressWarnings({"unchecked", "rawtypes"})
-public class JakartaEntrypointFinder extends AbstractEntrypointFinder {
- @Override
- public boolean isEntrypointClass(TypeDeclaration typeDecl) {
- if (!(typeDecl instanceof ClassOrInterfaceDeclaration)) {
- return false;
- }
-
- ClassOrInterfaceDeclaration classDecl = (ClassOrInterfaceDeclaration) typeDecl;
-
- // Check annotations
- if (classDecl.getAnnotations().stream()
- .anyMatch(a -> a.getNameAsString().contains("WebServlet") || a.getNameAsString().contains("WebFilter")
- || a.getNameAsString().contains("WebListener") || a.getNameAsString().contains("ServerEndpoint")
- || a.getNameAsString().contains("MessageDriven")
- || a.getNameAsString().contains("WebService"))) {
- return true;
- }
-
- // Check types
- return classDecl.getExtendedTypes().stream()
- .map(ClassOrInterfaceType::getNameAsString)
- .anyMatch(n -> n.contains("HttpServlet") || n.contains("GenericServlet"))
- || classDecl.getImplementedTypes().stream().map(
- ClassOrInterfaceType::asString).anyMatch(
- n -> n.contains("ServletContextListener")
- || n.contains("HttpSessionListener")
- || n.contains("ServletRequestListener")
- || n.contains("MessageListener"));
- }
-
- @Override
- public boolean isEntrypointMethod(CallableDeclaration callableDecl) {
- return ((NodeList) callableDecl.getParameters()).stream()
- .anyMatch(parameter -> parameter.getType().asString().contains("HttpServletRequest") ||
- parameter.getType().asString().contains("HttpServletResponse"));
- }
-}
diff --git a/src/main/java/com/ibm/cldk/javaee/jax/JaxRsEntrypointFinder.java b/src/main/java/com/ibm/cldk/javaee/jax/JaxRsEntrypointFinder.java
deleted file mode 100644
index 97d9f6cf..00000000
--- a/src/main/java/com/ibm/cldk/javaee/jax/JaxRsEntrypointFinder.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.ibm.cldk.javaee.jax;
-
-import com.github.javaparser.ast.body.CallableDeclaration;
-import com.github.javaparser.ast.body.TypeDeclaration;
-import com.ibm.cldk.javaee.utils.interfaces.AbstractEntrypointFinder;
-import java.util.List;
-
-@SuppressWarnings({"unchecked", "rawtypes"})
-public class JaxRsEntrypointFinder extends AbstractEntrypointFinder {
- /**
- * Detect if the method is an entrypoint.
- *
- * @return True if the method is an entrypoint, false otherwise.
- */
- @Override
- public boolean isEntrypointClass(TypeDeclaration typeDeclaration) {
- List callableDeclarations = typeDeclaration.findAll(CallableDeclaration.class);
- for (CallableDeclaration callableDeclaration : callableDeclarations) {
- if (callableDeclaration.getAnnotations().stream().anyMatch(a -> a.toString().contains("POST"))
- || callableDeclaration.getAnnotations().stream().anyMatch(a -> a.toString().contains("PUT"))
- || callableDeclaration.getAnnotations().stream().anyMatch(a -> a.toString().contains("GET"))
- || callableDeclaration.getAnnotations().stream().anyMatch(a -> a.toString().contains("HEAD"))
- || callableDeclaration.getAnnotations().stream().anyMatch(a -> a.toString().contains("DELETE"))) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * @param callableDecl
- * @return
- */
- @Override
- public boolean isEntrypointMethod(CallableDeclaration callableDecl) {
- return callableDecl.getAnnotations().stream().anyMatch(a -> a.toString().contains("POST") || a.toString().contains("PUT")
- || a.toString().contains("GET") || a.toString().contains("HEAD")
- || a.toString().contains("DELETE"));
- }
-}
diff --git a/src/main/java/com/ibm/cldk/javaee/jdbc/JDBCCRUDFinder.java b/src/main/java/com/ibm/cldk/javaee/jdbc/JDBCCRUDFinder.java
deleted file mode 100644
index 2a85ee47..00000000
--- a/src/main/java/com/ibm/cldk/javaee/jdbc/JDBCCRUDFinder.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package com.ibm.cldk.javaee.jdbc;
-
-import com.ibm.cldk.javaee.utils.interfaces.AbstractCRUDFinder;
-import java.util.List;
-import java.util.Optional;
-
-public class JDBCCRUDFinder extends AbstractCRUDFinder {
- /**
- * Detect if the method call is a create operation.
- *
- * @param receiverType
- * @param name
- * @return
- */
- @Override
- public boolean isCreateOperation(String receiverType, String name) {
- return false;
- }
-
- /**
- * Detect if the method call is a delete operation.
- *
- * @param receiverType
- * @param name
- * @return
- */
- @Override
- public boolean isDeleteOperation(String receiverType, String name) {
- return false;
- }
-
- /**
- * Detect if the method call is an update operation.
- *
- * @param receiverType
- * @param name
- * @return
- */
- @Override
- public boolean isUpdateOperation(String receiverType, String name) {
- return false;
- }
-
- /**
- * Detect if the method call is a read operation.
- *
- * @param receiverType
- * @param name
- * @return
- */
- @Override
- public boolean isReadOperation(String receiverType, String name) {
- return false;
- }
-
- /**
- * @param declaringType
- * @param methodName
- * @return
- */
- @Override
- public boolean isCRUDQueryCreation(String declaringType, String methodName) {
- return false;
- }
-
- /**
- * @param declaringType
- * @param nameAsString
- * @param arguments
- * @return
- */
- @Override
- public boolean isReadQuery(String declaringType, String nameAsString, Optional> arguments) {
- return false;
- }
-
- /**
- * @param declaringType
- * @param nameAsString
- * @param arguments
- * @return
- */
- @Override
- public boolean isWriteQuery(String declaringType, String nameAsString, Optional> arguments) {
- return false;
- }
-
- /**
- * @param declaringType
- * @param nameAsString
- * @param arguments
- * @return
- */
- @Override
- public boolean isNamedQuery(String declaringType, String nameAsString, Optional> arguments) {
- return false;
- }
-}
diff --git a/src/main/java/com/ibm/cldk/javaee/spring/SpringCRUDFinder.java b/src/main/java/com/ibm/cldk/javaee/spring/SpringCRUDFinder.java
deleted file mode 100644
index 2338980f..00000000
--- a/src/main/java/com/ibm/cldk/javaee/spring/SpringCRUDFinder.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package com.ibm.cldk.javaee.spring;
-
-import com.ibm.cldk.javaee.utils.interfaces.AbstractCRUDFinder;
-import java.util.List;
-import java.util.Optional;
-
-public class SpringCRUDFinder extends AbstractCRUDFinder {
- /**
- * Detect if the method call is a create operation.
- *
- * @param receiverType The type of the receiver object.
- * @param name The name of the method.
- * @return True if the method call is a create operation, false otherwise.
- */
- @Override
- public boolean isCreateOperation(String receiverType, String name) {
- return false;
- }
-
- /**
- * Detect if the method call is a delete operation.
- *
- * @param receiverType The type of the receiver object.
- * @param name The name of the method.
- * @return True if the method call is a delete operation, false otherwise.
- */
- @Override
- public boolean isDeleteOperation(String receiverType, String name) {
- return false;
- }
-
- /**
- * Detect if the method call is an update operation.
- *
- * @param receiverType The type of the receiver object.
- * @param name The name of the method.
- * @return True if the method call is an update operation, false otherwise.
- */
- @Override
- public boolean isUpdateOperation(String receiverType, String name) {
- return false;
- }
-
- /**
- * Detect if the method call is a read operation.
- *
- * @param receiverType The type of the receiver object.
- * @param name The name of the method.
- * @return True if the method call is a read operation, false otherwise.
- */
- @Override
- public boolean isReadOperation(String receiverType, String name) {
- return false;
- }
-
- /**
- * @param declaringType
- * @param methodName
- * @return
- */
- @Override
- public boolean isCRUDQueryCreation(String declaringType, String methodName) {
- return false;
- }
-
- /**
- * @param declaringType
- * @param nameAsString
- * @param arguments
- * @return
- */
- @Override
- public boolean isReadQuery(String declaringType, String nameAsString, Optional> arguments) {
- return false;
- }
-
- /**
- * @param declaringType
- * @param nameAsString
- * @param arguments
- * @return
- */
- @Override
- public boolean isWriteQuery(String declaringType, String nameAsString, Optional> arguments) {
- return false;
- }
-
- /**
- * @param declaringType
- * @param nameAsString
- * @param arguments
- * @return
- */
- @Override
- public boolean isNamedQuery(String declaringType, String nameAsString, Optional> arguments) {
- return false;
- }
-
-}
diff --git a/src/main/java/com/ibm/cldk/javaee/spring/SpringEntrypointFinder.java b/src/main/java/com/ibm/cldk/javaee/spring/SpringEntrypointFinder.java
deleted file mode 100644
index 4f48ead2..00000000
--- a/src/main/java/com/ibm/cldk/javaee/spring/SpringEntrypointFinder.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.ibm.cldk.javaee.spring;
-
-import com.github.javaparser.ast.body.CallableDeclaration;
-import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
-import com.github.javaparser.ast.body.TypeDeclaration;
-import com.github.javaparser.ast.expr.AnnotationExpr;
-import com.github.javaparser.ast.type.ClassOrInterfaceType;
-import com.ibm.cldk.javaee.utils.interfaces.AbstractEntrypointFinder;
-import java.util.List;
-
-public class SpringEntrypointFinder extends AbstractEntrypointFinder {
- @Override
- public boolean isEntrypointClass(TypeDeclaration typeDeclaration) {
- List annotations = typeDeclaration.getAnnotations();
- for (AnnotationExpr annotation : annotations) {
- // Existing checks
- if (annotation.getNameAsString().contains("RestController")
- || annotation.getNameAsString().contains("Controller")
- || annotation.getNameAsString().contains("HandleInterceptor")
- || annotation.getNameAsString().contains("HandlerInterceptor")) {
- return true;
- }
-
- // Spring Boot specific checks
- if (annotation.getNameAsString().contains("SpringBootApplication")
- || annotation.getNameAsString().contains("Configuration")
- || annotation.getNameAsString().contains("Component")
- || annotation.getNameAsString().contains("Service")
- || annotation.getNameAsString().contains("Repository")) {
- return true;
- }
- }
-
- // Check if class implements CommandLineRunner or ApplicationRunner
- if (typeDeclaration instanceof ClassOrInterfaceDeclaration) {
- ClassOrInterfaceDeclaration classDecl = (ClassOrInterfaceDeclaration) typeDeclaration;
- for (ClassOrInterfaceType implementedType : classDecl.getImplementedTypes()) {
- String typeName = implementedType.getNameAsString();
- if (typeName.equals("CommandLineRunner") || typeName.equals("ApplicationRunner")) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- @Override
- public boolean isEntrypointMethod(CallableDeclaration callableDecl) { return callableDecl.getAnnotations().stream().anyMatch(a -> a.toString().contains("GetMapping") ||
- a.toString().contains("PostMapping") ||
- a.toString().contains("PutMapping") ||
- a.toString().contains("DeleteMapping") ||
- a.toString().contains("PatchMapping") ||
- a.toString().contains("RequestMapping") ||
- a.toString().contains("EventListener") ||
- a.toString().contains("Scheduled") ||
- a.toString().contains("KafkaListener") ||
- a.toString().contains("RabbitListener") ||
- a.toString().contains("JmsListener") ||
- a.toString().contains("PreAuthorize") ||
- a.toString().contains("PostAuthorize") ||
- a.toString().contains("PostConstruct") ||
- a.toString().contains("PreDestroy") ||
- a.toString().contains("Around") ||
- a.toString().contains("Before") ||
- a.toString().contains("After") ||
- a.toString().contains("JobScope") ||
- a.toString().contains("StepScope")); }
-}
diff --git a/src/main/java/com/ibm/cldk/javaee/struts/StrutsEntrypointFinder.java b/src/main/java/com/ibm/cldk/javaee/struts/StrutsEntrypointFinder.java
deleted file mode 100644
index a506364d..00000000
--- a/src/main/java/com/ibm/cldk/javaee/struts/StrutsEntrypointFinder.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.ibm.cldk.javaee.struts;
-
-import com.github.javaparser.ast.Node;
-import com.github.javaparser.ast.body.CallableDeclaration;
-import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
-import com.github.javaparser.ast.body.TypeDeclaration;
-import com.github.javaparser.ast.type.ClassOrInterfaceType;
-import com.github.javaparser.resolution.UnsolvedSymbolException;
-import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
-import com.ibm.cldk.javaee.utils.interfaces.AbstractEntrypointFinder;
-import com.ibm.cldk.utils.Log;
-import java.util.Optional;
-
-public class StrutsEntrypointFinder extends AbstractEntrypointFinder {
- @Override
- public boolean isEntrypointClass(TypeDeclaration typeDeclaration){
- if (!(typeDeclaration instanceof ClassOrInterfaceDeclaration)) {
- return false;
- }
-
- ClassOrInterfaceDeclaration classDecl = (ClassOrInterfaceDeclaration) typeDeclaration;
-
- // Check class-level Struts annotations
- if (classDecl.getAnnotations().stream().anyMatch(a -> a.getNameAsString().contains("Action")
- || a.getNameAsString().contains("Namespace") || a.getNameAsString().contains("InterceptorRef"))) {
- return true;
- }
-
- // Check if extends ActionSupport or implements Interceptor
- try {
- ResolvedReferenceTypeDeclaration resolved = classDecl.resolve();
- return resolved.getAllAncestors().stream().anyMatch(ancestor -> {
- String name = ancestor.getQualifiedName();
- return name.contains("ActionSupport") || name.contains("Interceptor");
- });
- } catch (RuntimeException e) {
- Log.warn("Could not resolve class: " + e.getMessage());
- }
-
- return false;
- }
-
- @Override
- public boolean isEntrypointMethod(CallableDeclaration callableDecl) {
- // First check if this method is in a Struts Action class
- Optional parentNode = callableDecl.getParentNode();
- if (parentNode.isEmpty() || !(parentNode.get() instanceof ClassOrInterfaceDeclaration)) {
- return false;
- }
-
- ClassOrInterfaceDeclaration parentClass = (ClassOrInterfaceDeclaration) parentNode.get();
- if (parentClass.getExtendedTypes().stream()
- .map(ClassOrInterfaceType::asString)
- .noneMatch(type -> type.contains("ActionSupport") || type.contains("Action")))
- return false;
-
- return callableDecl.getAnnotations().stream().anyMatch(a -> a.toString().contains("Action") ||
- a.toString().contains("Actions") ||
- a.toString().contains("ValidationMethod") ||
- a.toString().contains("InputConfig") ||
- a.toString().contains("BeforeResult") ||
- a.toString().contains("After") ||
- a.toString().contains("Before") ||
- a.toString().contains("Result") ||
- a.toString().contains("Results")) || callableDecl.getNameAsString().equals("execute");
- }
-}
diff --git a/src/main/java/com/ibm/cldk/javaee/utils/enums/CRUDOperationType.java b/src/main/java/com/ibm/cldk/javaee/utils/enums/CRUDOperationType.java
deleted file mode 100644
index 45375e73..00000000
--- a/src/main/java/com/ibm/cldk/javaee/utils/enums/CRUDOperationType.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.ibm.cldk.javaee.utils.enums;
-
-public enum CRUDOperationType {
- CREATE,
- READ,
- UPDATE,
- DELETE;
-}
diff --git a/src/main/java/com/ibm/cldk/javaee/utils/enums/CRUDQueryType.java b/src/main/java/com/ibm/cldk/javaee/utils/enums/CRUDQueryType.java
deleted file mode 100644
index 8f95f0f9..00000000
--- a/src/main/java/com/ibm/cldk/javaee/utils/enums/CRUDQueryType.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.ibm.cldk.javaee.utils.enums;
-
-public enum CRUDQueryType {
- READ,
- WRITE,
- NAMED;
-}
diff --git a/src/main/java/com/ibm/cldk/javaee/utils/enums/JPAQueryMethod.java b/src/main/java/com/ibm/cldk/javaee/utils/enums/JPAQueryMethod.java
deleted file mode 100644
index 25d666fb..00000000
--- a/src/main/java/com/ibm/cldk/javaee/utils/enums/JPAQueryMethod.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.ibm.cldk.javaee.utils.enums;
-
-import com.ibm.cldk.utils.annotations.Note;
-import java.util.Optional;
-import lombok.Getter;
-
-@Getter
-public enum JPAQueryMethod {
- // Read Operations
- GET_RESULT_LIST(CRUDOperationType.READ),
- GET_SINGLE_RESULT(CRUDOperationType.READ),
- GET_FIRST_RESULT(CRUDOperationType.READ),
- GET_MAX_RESULTS(CRUDOperationType.READ),
-
- // Write Operations
- @Note("There is a possiblity that the user may execute a delete action using the executeUpdate method. There is no way to differentiate between an update and delete operation without doing a dataflow analysis on the query string because the query string may be defined anywhere in the code. So for now, we are assuming that executeUpdate is an update operation.")
- EXECUTE_UPDATE(CRUDOperationType.UPDATE),
-
- // Non-CRUD Methods (configuration or metadata)
- GET_FLUSH_MODE(null),
- GET_HINTS(null),
- GET_LOCK_MODE(null),
- GET_PARAMETER(null),
- GET_PARAMETERS(null),
- GET_PARAMETER_VALUE(null),
- IS_BOUND(null),
- UNWRAP(null);
-
- private final CRUDOperationType crudOperation;
-
- JPAQueryMethod(CRUDOperationType crudOperation) {
- this.crudOperation = crudOperation;
- }
-
- public static Optional getOperationForMethod(String methodName) {
- try {
- // A small hack to convert camelCase to snake_case and also to uppercase. Basically, we want getSingleResult to get converted to GET_SINGLE_RESULT so as to
- // match the enum values conventions.
- return Optional.ofNullable(JPAQueryMethod.valueOf(methodName.replaceAll("([a-z])([A-Z]+)", "$1_$2").toUpperCase()).getCrudOperation());
- } catch (IllegalArgumentException e) {
- return Optional.empty();
- }
- }
-}
diff --git a/src/main/java/com/ibm/cldk/javaee/utils/interfaces/AbstractCRUDFinder.java b/src/main/java/com/ibm/cldk/javaee/utils/interfaces/AbstractCRUDFinder.java
deleted file mode 100644
index 02a331f4..00000000
--- a/src/main/java/com/ibm/cldk/javaee/utils/interfaces/AbstractCRUDFinder.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.ibm.cldk.javaee.utils.interfaces;
-
-import java.util.List;
-import java.util.Optional;
-
-/**
- * Abstract base class for finding CRUD operations in various frameworks.
- */
-@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
-public abstract class AbstractCRUDFinder {
- public abstract boolean isCreateOperation(String receiverType, String methodName);
-
- public abstract boolean isDeleteOperation(String receiverType, String methodName);
-
- public abstract boolean isUpdateOperation(String receiverType, String methodName);
-
- public abstract boolean isReadOperation(String receiverType, String methodName);
-
- // Detect CRUD Query Creation (Only query definitions, not execution)
- public abstract boolean isCRUDQueryCreation(String declaringType, String methodName);
-
- public abstract boolean isReadQuery(String declaringType, String nameAsString, Optional> arguments);
-
- public abstract boolean isWriteQuery(String declaringType, String nameAsString, Optional> arguments);
-
- public abstract boolean isNamedQuery(String declaringType, String nameAsString, Optional> arguments);
-}
diff --git a/src/main/java/com/ibm/cldk/javaee/utils/interfaces/AbstractEntrypointFinder.java b/src/main/java/com/ibm/cldk/javaee/utils/interfaces/AbstractEntrypointFinder.java
deleted file mode 100644
index 7fca2d2f..00000000
--- a/src/main/java/com/ibm/cldk/javaee/utils/interfaces/AbstractEntrypointFinder.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.ibm.cldk.javaee.utils.interfaces;
-
-import com.github.javaparser.ast.body.CallableDeclaration;
-import com.github.javaparser.ast.body.TypeDeclaration;
-import com.ibm.cldk.utils.annotations.NotImplemented;
-
-@SuppressWarnings({"unchecked", "rawtypes"})
-public abstract class AbstractEntrypointFinder {
- /**
- * Enum for rules.
- */
- enum Rulset{
- }
-
- /**
- * Detect if the method is an entrypoint.
- *
- * @param receiverType The type of the receiver object.
- * @param name The name of the method.
- * @return True if the method is an entrypoint, false otherwise.
- */
- public abstract boolean isEntrypointClass(TypeDeclaration typeDecl);
-
- public abstract boolean isEntrypointMethod(CallableDeclaration callableDecl);
-}
diff --git a/src/main/java/com/ibm/cldk/utils/AnalysisUtils.java b/src/main/java/com/ibm/cldk/utils/AnalysisUtils.java
deleted file mode 100644
index ab4e5285..00000000
--- a/src/main/java/com/ibm/cldk/utils/AnalysisUtils.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
-Copyright IBM Corporation 2023, 2024
-
-Licensed under the Apache Public License 2.0, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-
-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.
- */
-package com.ibm.cldk.utils;
-
-import static com.ibm.cldk.SymbolTable.declaredMethodsAndConstructors;
-
-import com.ibm.cldk.entities.Callable;
-import com.ibm.cldk.entities.Comment;
-import com.ibm.cldk.entities.ParameterInCallable;
-import com.ibm.wala.classLoader.IClass;
-import com.ibm.wala.classLoader.IMethod;
-import com.ibm.wala.ipa.callgraph.CallGraph;
-import com.ibm.wala.ipa.callgraph.Entrypoint;
-import com.ibm.wala.ipa.callgraph.impl.DefaultEntrypoint;
-import com.ibm.wala.ipa.cha.IClassHierarchy;
-import com.ibm.wala.ssa.IR;
-import com.ibm.wala.ssa.ISSABasicBlock;
-import com.ibm.wala.ssa.SSAConditionalBranchInstruction;
-import com.ibm.wala.ssa.SSASwitchInstruction;
-import com.ibm.wala.types.ClassLoaderReference;
-import java.util.*;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-import org.apache.commons.lang3.tuple.Pair;
-import org.objectweb.asm.Type;
-
-/**
- * The type Analysis utils.
- */
-public class AnalysisUtils {
-
- /**
- * The constant classAttr.
- */
- public static Map createAndPutNewCallableInSymbolTable(IMethod method) {
- // Get the class name, with a . representation.
- String declaringClassSignature = method.getDeclaringClass().getName().toString().substring(1).replace("/", ".").replace("$", ".");
-
- // Get the method arguments, use a . notation for types.
- List arguments = Arrays.stream(Type.getMethodType(method.getDescriptor().toString()).getArgumentTypes()).map(Type::getClassName).collect(Collectors.toList());
-
- String methodName = method.getName().toString();
-
- // Get the method signature.
- String methodSignature = String.join("", methodName, "(", String.join(", ", Optional.of(arguments).orElseGet(Collections::emptyList)), ")");
-
- Callable newCallable = new Callable();
- newCallable.setFilePath("");
- newCallable.setImplicit(true);
- newCallable.setConstructor(methodName.contains(""));
- newCallable.setComments(new ArrayList<>());
- newCallable.setModifiers(Stream.of(method.isPublic() ? "public" : null, method.isProtected() ? "protected" : null, method.isPrivate() ? "private" : null, method.isAbstract() ? "abstract" : null, method.isStatic() ? "static" : null, method.isFinal() ? "final" : null, method.isSynchronized() ? "synchronized" : null, method.isNative() ? "native" : null, method.isSynthetic() ? "synthetic" : null, method.isBridge() ? "bridge" : null).filter(Objects::nonNull).collect(Collectors.toList()));
- newCallable.setCode("");
- newCallable.setSignature(methodSignature);
- newCallable.setDeclaration(methodSignature);
- newCallable.setEndLine(-1);
- newCallable.setStartLine(-1);
- newCallable.setParameters(Arrays.stream(Type.getMethodType(method.getDescriptor().toString()).getArgumentTypes()).map(param -> {
- ParameterInCallable parameter = new ParameterInCallable();
- parameter.setType(param.getClassName());
- parameter.setName(null);
- parameter.setModifiers(Collections.emptyList());
- parameter.setAnnotations(Collections.emptyList());
- return parameter;
- }).collect(Collectors.toList()));
- newCallable.setReferencedTypes(Collections.emptyList());
- newCallable.setAnnotations(method.getAnnotations().stream().map(annotation -> annotation.toString().replace("[", "(").replace("]", ")").replace("Annotation type ", "@")).collect(Collectors.toList()));
-
- declaredMethodsAndConstructors.put(declaringClassSignature, methodSignature, newCallable);
- String signature = newCallable.getSignature();
- if (signature.contains("")) {
- signature = signature.replace("", declaringClassSignature.substring(declaringClassSignature.lastIndexOf(".") + 1));
- } else if (signature.contains("")) {
- signature = signature.replace("", declaringClassSignature.substring(declaringClassSignature.lastIndexOf(".") + 1));
- }
- return Map.ofEntries(
- Map.entry("typeDeclaration", declaringClassSignature),
- Map.entry("filePath", "<>"),
- Map.entry("signature", signature),
- Map.entry("callableDeclaration", newCallable.getDeclaration())
- );
- }
-
- /**
- * Computes and returns cyclomatic complexity for the given IR (for a method
- * or constructor).
- *
- * @param ir IR for method or constructor
- * @return int Cyclomatic complexity for method/constructor
- */
- public static int getCyclomaticComplexity(IR ir) {
- if (ir == null) {
- return 0;
- }
- int conditionalBranchCount = (int) Arrays.stream(ir.getInstructions())
- .filter(inst -> inst instanceof SSAConditionalBranchInstruction)
- .count();
- int switchBranchCount = Arrays.stream(ir.getInstructions())
- .filter(inst -> inst instanceof SSASwitchInstruction)
- .map(inst -> ((SSASwitchInstruction) inst).getCasesAndLabels().length).reduce(0, Integer::sum);
- Iterable iterableBasicBlocks = ir::getBlocks;
- int catchBlockCount = (int) StreamSupport.stream(iterableBasicBlocks.spliterator(), false)
- .filter(ISSABasicBlock::isCatchBlock)
- .count();
- return conditionalBranchCount + switchBranchCount + catchBlockCount + 1;
- }
-
- public static Map getCallableFromSymbolTable(IMethod method) {
-
- // Get the class name, with a . representation.
- String declaringClassSignature = method.getDeclaringClass().getName().toString().substring(1).replace("/", ".").replace("$", ".");
-
- // Get the method arguments, use a . notation for types.
- List arguments = Arrays.stream(Type.getMethodType(method.getDescriptor().toString()).getArgumentTypes()).map(Type::getClassName).collect(Collectors.toList());
-
- // Get the method signature.
- String methodSignature = String.join("", method.getName().toString(), "(", String.join(", ", Optional.of(arguments).orElseGet(Collections::emptyList)), ")");
- Callable callable = declaredMethodsAndConstructors.get(declaringClassSignature, methodSignature);
-
- if (callable == null) {
- return null;
- } else {
- String signature = callable.getSignature();
- if (signature.contains("")) {
- signature = signature.replace("", declaringClassSignature.substring(declaringClassSignature.lastIndexOf(".") + 1));
- } else if (signature.contains("")) {
- signature = signature.replace("", declaringClassSignature.substring(declaringClassSignature.lastIndexOf(".") + 1));
- }
- return Map.ofEntries(
- Map.entry("typeDeclaration", declaringClassSignature),
- Map.entry("filePath", callable.getFilePath()),
- Map.entry("signature", signature),
- Map.entry("callableDeclaration", callable.getSignature())
- );
- }
- }
-
- public static Pair getCallableObjectFromSymbolTable(IMethod method) {
-
- // Get the class name, with a . representation.
- String declaringClassSignature = method.getDeclaringClass().getName().toString().substring(1).replace("/", ".").replace("$", ".");
-
- // Get the method arguments, use a . notation for types.
- List arguments = Arrays.stream(Type.getMethodType(method.getDescriptor().toString()).getArgumentTypes()).map(Type::getClassName).collect(Collectors.toList());
-
- // Get the method signature.
- String methodSignature = String.join("", method.getName().toString(), "(", String.join(", ", Optional.of(arguments).orElseGet(Collections::emptyList)), ")");
-
- return Pair.of(declaringClassSignature, declaredMethodsAndConstructors.get(declaringClassSignature, methodSignature));
- }
-
-
- /**
- * Verfy if a class is an application class.
- *
- * @param _class the class
- * @return Boolean boolean
- */
- public static Boolean isApplicationClass(IClass _class) {
- return _class.getClassLoader().getReference().equals(ClassLoaderReference.Application);
- }
-
- /**
- * Gets number of application classes.
- *
- * @param cha the cha
- * @return the number of application classes
- */
- public static long getNumberOfApplicationClasses(IClassHierarchy cha) {
- return StreamSupport.stream(cha.spliterator(), false).filter(AnalysisUtils::isApplicationClass).count();
- }
-
- /**
- * Use all public methods of all application classes as entrypoints.
- *
- * @param cha the cha
- * @return Iterable entry points
- */
- public static Iterable getEntryPoints(IClassHierarchy cha) {
- List entrypoints = StreamSupport.stream(cha.spliterator(), true).filter(AnalysisUtils::isApplicationClass).flatMap(c -> {
- try {
- return c.getDeclaredMethods().stream();
- } catch (NullPointerException nullPointerException) {
- Log.error(c.getSourceFileName());
- System.exit(1);
- return Stream.empty();
- }
- }).map(method -> new DefaultEntrypoint(method, cha)).collect(Collectors.toList());
- // We're assuming that all methods are potential entrypoints. May revisit this later if the assumption is incorrect.
- Log.info("Registered " + entrypoints.size() + " entrypoints.");
- return entrypoints;
- }
-}
diff --git a/src/main/java/com/ibm/cldk/utils/BuildProject.java b/src/main/java/com/ibm/cldk/utils/BuildProject.java
deleted file mode 100644
index 53dcd649..00000000
--- a/src/main/java/com/ibm/cldk/utils/BuildProject.java
+++ /dev/null
@@ -1,304 +0,0 @@
-package com.ibm.cldk.utils;
-
-import static com.ibm.cldk.CodeAnalyzer.includeTestClasses;
-import static com.ibm.cldk.CodeAnalyzer.noCleanDependencies;
-import static com.ibm.cldk.CodeAnalyzer.projectRootPom;
-import static com.ibm.cldk.utils.ProjectDirectoryScanner.classFilesStream;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.text.MessageFormat;
-import java.util.*;
-import java.util.function.Predicate;
-import java.util.stream.Stream;
-
-public class BuildProject {
- public static Path libDownloadPath;
- private static final String LIB_DEPS_DOWNLOAD_DIR = "_library_dependencies";
- private static final String MAVEN_CMD = BuildProject.getMavenCommand();
- private static final String GRADLE_CMD = BuildProject.getGradleCommand();
-
- /**
- * Gets the maven command to be used for building the project.
- *
- * @return the maven command
- */
- public static String getMavenCommand() {
- String mvnSystemCommand = Arrays.stream(System.getenv("PATH").split(File.pathSeparator)).filter(Predicate.not(String::isBlank)).filter(Predicate.not(String::isEmpty)).map(path -> new File(path, System.getProperty("os.name").toLowerCase().contains("windows") ? "mvn.cmd" : "mvn")).filter(File::exists).findFirst().map(File::getAbsolutePath).orElse(null);
- File mvnWrapper = System.getProperty("os.name").toLowerCase().contains("windows") ? new File(projectRootPom, "mvnw.cmd") : new File(projectRootPom, "mvnw");
- return commandExists(mvnWrapper.getAbsoluteFile()).getKey() ? mvnWrapper.getAbsoluteFile().toString() : mvnSystemCommand;
- }
-
- /**
- * Gets the gradle command to be used for building the project.
- *
- * @return the gradle command
- */
- public static String getGradleCommand() {
- String gradleSystemCommand = Arrays.stream(System.getenv("PATH").split(File.pathSeparator)).filter(Predicate.not(String::isBlank)).filter(Predicate.not(String::isEmpty)).map(path -> new File(path, System.getProperty("os.name").toLowerCase().contains("windows") ? "gradle.bat" : "gradle")).filter(File::exists).findFirst().map(File::getAbsolutePath).orElse(null);
- File gradleWrapper = System.getProperty("os.name").toLowerCase().contains("windows") ? new File(projectRootPom, "gradlew.bat") : new File(projectRootPom, "gradlew");
-
- return commandExists(gradleWrapper.getAbsoluteFile()).getKey() ? gradleWrapper.getAbsoluteFile() .toString() : gradleSystemCommand;
- }
-
- public static Path tempInitScript;
-
- static {
- try {
- tempInitScript = Files.createTempFile("gradle-init-", ".gradle");
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- private static final String GRADLE_DEPENDENCIES_TASK = "allprojects { afterEvaluate { project -> task downloadDependencies(type: Copy) { def configs = project.configurations.findAll { it.canBeResolved }; dependsOn configs; from configs; into project.hasProperty('outputDir') ? project.property('outputDir') : \"${project.buildDir}/libs\"; eachFile { fileCopyDetails -> fileCopyDetails.file.setWritable(true) }; doFirst { println \"Downloading dependencies for project ${project.name} to: ${destinationDir}\"; configs.each { config -> println \"Configuration: ${config.name}\"; config.resolvedConfiguration.resolvedArtifacts.each { artifact -> println \"\\t${artifact.moduleVersion.id}:${artifact.extension}\" } } } } } }";
- private static AbstractMap.SimpleEntry commandExists(File command) {
- StringBuilder output = new StringBuilder();
- if (!command.exists()) {
- return new AbstractMap.SimpleEntry<>(false, MessageFormat.format("Command {0} does not exist.", command));
- }
- try {
- Process process = new ProcessBuilder().directory(new File(projectRootPom)).command(String.valueOf(command), "--version").start();
- // Read the output stream
- BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
- String line;
- while ((line = reader.readLine()) != null) {
- output.append(line).append("\n");
- }
-
- // Read the error stream
- BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
- while ((line = errorReader.readLine()) != null) {
- output.append(line).append("\n");
- }
-
- int exitCode = process.waitFor();
- return new AbstractMap.SimpleEntry<>(exitCode == 0, output.toString().trim());
- } catch (IOException | InterruptedException exceptions) {
- Log.error(exceptions.getMessage());
- return new AbstractMap.SimpleEntry<>(false, exceptions.getMessage());
- }
- }
-
- private static boolean buildWithTool(String[] buildCommand) {
- Log.info("Building the project using " + buildCommand[0] + ".");
- ProcessBuilder processBuilder = new ProcessBuilder().directory(new File(projectRootPom)).command(buildCommand);
- try {
- Process process = processBuilder.start();
- BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
- String line;
- while ((line = reader.readLine()) != null) {
- Log.info(line);
- }
- int exitCode = process.waitFor();
- process.getErrorStream().transferTo(System.err);
- Log.info(buildCommand[0].toUpperCase() + " build exited with code " + exitCode);
- return exitCode == 0;
- } catch (IOException | InterruptedException e) {
- e.printStackTrace();
- return false;
- }
- }
-
- /**
- * Checks if Maven is installed in the system.
- *
- * @return true if Maven is installed, false otherwise.
- */
- private static boolean isMavenInstalled() {
- ProcessBuilder processBuilder = new ProcessBuilder().directory(new File(projectRootPom)).command(MAVEN_CMD, "--version");
- try {
- Process process = processBuilder.start();
- BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
- String line = reader.readLine(); // Read the first line of the output
- if (line != null && line.contains("Apache Maven")) {
- return true;
- }
- } catch (IOException e) {
- Log.error("An error occurred while checking if Maven is installed: " + e.getMessage());
- }
- Log.error("Maven is not installed or not properly configured in the system's PATH.");
- return false;
- }
-
- /**
- * Initiates a maven build process for the given project path.
- *
- * @param projectPath is the path to the project to be built.
- * @return true if the build was successful, false otherwise.
- */
- private static boolean mavenBuild(String projectPath) {
- Log.info("Building the project using Maven.");
- if (!isMavenInstalled()) {
- Log.info("Checking if Maven is installed.");
- return false;
- }
-
- String[] mavenCommand;
- if (includeTestClasses) {
- Log.warn("Hidden flag `--include-test-classes` is turned on. We'll including test classes in WALA analysis");
- mavenCommand = new String[]{MAVEN_CMD, "test-compile", "-f", projectPath + "/pom.xml", "-B", "-V", "-e", "-Drat.skip", "-Dfindbugs.skip", "-Dcheckstyle.skip", "-Dpmd.skip=true", "-Dspotbugs.skip", "-Denforcer.skip", "-Dmaven.javadoc.skip", "-DskipTests", "-Dmaven.test.skip.exec", "-Dlicense.skip=true", "-Drat.skip=true", "-Dspotless.check.skip=true"};
- }
- else
- mavenCommand = new String[]{MAVEN_CMD, "compile", "-f", projectPath + "/pom.xml", "-B", "-V", "-e", "-Drat.skip", "-Dfindbugs.skip", "-Dcheckstyle.skip", "-Dpmd.skip=true", "-Dspotbugs.skip", "-Denforcer.skip", "-Dmaven.javadoc.skip", "-DskipTests", "-Dmaven.test.skip.exec", "-Dlicense.skip=true", "-Drat.skip=true", "-Dspotless.check.skip=true"};
-
- return buildWithTool(mavenCommand);
- }
-
- public static boolean gradleBuild(String projectPath) {
- // Adjust Gradle command as needed
- String[] gradleCommand;
- if (GRADLE_CMD.equals("gradlew") || GRADLE_CMD.equals("gradlew.bat")) {
- gradleCommand = new String[]{projectPath + File.separator + GRADLE_CMD, "compileJava", "-p", projectPath};
- } else {
- if (includeTestClasses) {
- Log.warn("Hidden flag `--include-test-classes` is turned on. We'll including test classes in WALA analysis");
- gradleCommand = new String[]{GRADLE_CMD, "compileTestJava", "-p", projectPath};
- }
- else
- gradleCommand = new String[]{GRADLE_CMD, "compileJava", "-p", projectPath};
- }
- return buildWithTool(gradleCommand);
- }
-
- private static boolean buildProject(String projectPath, String build) {
- File pomFile = new File(String.valueOf(Paths.get(projectPath).toAbsolutePath()), "pom.xml");
- if (build == null) {
- return true;
- } else if (build.equals("auto")) {
- if (pomFile.exists()) {
- Log.info("Found pom.xml in the project directory. Using Maven to build the project.");
- return mavenBuild(Paths.get(projectPath).toAbsolutePath().toString()); // Use Maven if pom.xml exists
- } else {
- Log.info("Did not find a pom.xml in the project directory. Using Gradle to build the project.");
- return gradleBuild(projectPath); // Otherwise, use Gradle
- }
- } else {
- // Update command with a project path
- build = build.replace(MAVEN_CMD, MAVEN_CMD + " -f " + projectPath);
- Log.info("Using custom build command: " + build);
- String[] customBuildCommand = build.split(" ");
- return buildWithTool(customBuildCommand);
- }
- }
-
- /**
- * Streams the files in the given project path.
- *
- * @param projectPath is the path to the project to be streamed.
- * @return true if the streaming was successful, false otherwise.
- */
- public static List buildProjectAndStreamClassFiles(String projectPath, String build) throws IOException {
- return buildProject(projectPath, build) ? classFilesStream(projectPath) : new ArrayList<>();
- }
-
- private static boolean mkLibDepDirs(String projectPath) {
- if (!Files.exists(libDownloadPath)) {
- try {
- Files.createDirectories(libDownloadPath);
- } catch (IOException e) {
- Log.error("Error creating library dependency directory for " + projectPath + ": " + e.getMessage());
- return false;
- }
- }
- return true;
- }
- /**
- * Downloads library dependency jars of the given project so that the jars can be used
- * for type resolution during symbol table creation.
- *
- * @param projectPath Path to the project under javaee
- * @return true if dependency download succeeds; false otherwise
- */
- public static boolean downloadLibraryDependencies(String projectPath, String projectRootPom) throws IOException {
- // created download dir if it does not exist
- String projectRoot = projectRootPom != null ? projectRootPom : projectPath;
-
- File pomFile = new File((new File(projectRoot)).getAbsoluteFile(), "pom.xml");
- if (pomFile.exists()) {
- libDownloadPath = Paths.get(projectPath, "target", LIB_DEPS_DOWNLOAD_DIR).toAbsolutePath();
- if (mkLibDepDirs(projectPath))
- Log.debug("Dependencies found/created in " + libDownloadPath);
- else
- throw new IllegalStateException("Error creating library dependency directory in " + libDownloadPath);
-
- if (MAVEN_CMD == null || !commandExists(new File(MAVEN_CMD)).getKey()) {
- String msg = MAVEN_CMD == null ?
- "Could not find Maven or a valid Maven Wrapper" :
- MessageFormat.format("Could not verify that {0} exists", MAVEN_CMD);
- Log.error(msg);
- throw new IllegalStateException("Unable to execute Maven command. " +
- (MAVEN_CMD == null ?
- "Could not find Maven or a valid Maven Wrapper" :
- "Attempt failed with message\n" + commandExists(new File(MAVEN_CMD)).getValue()
- ));
- }
- Log.info("Found pom.xml in the project directory. Using Maven to download dependencies.");
- String[] mavenCommand = {MAVEN_CMD, "--no-transfer-progress", "-f", Paths.get(projectRoot, "pom.xml").toAbsolutePath().toString(), "dependency:copy-dependencies", "-DoutputDirectory=" + libDownloadPath.toString(), "-Doverwrite=true", "--fail-never"};
- return buildWithTool(mavenCommand);
- } else if (new File(projectRoot, "build.gradle").exists() || new File(projectRoot, "build.gradle.kts").exists()) {
- libDownloadPath = Paths.get(projectPath, "build", LIB_DEPS_DOWNLOAD_DIR).toAbsolutePath();
- if (mkLibDepDirs(projectPath))
- Log.debug("Dependencies found/created in " + libDownloadPath);
- else
- throw new IllegalStateException("Error creating library dependency directory in " + libDownloadPath);
-
- if (GRADLE_CMD == null || !commandExists(new File(GRADLE_CMD)).getKey()) {
- String msg = GRADLE_CMD == null ?
- "Could not find Gradle or valid Gradle Wrapper" :
- MessageFormat.format("Could not verify that {0} exists", GRADLE_CMD);
- Log.error(msg);
- throw new IllegalStateException("Unable to execute Gradle command. " +
- (GRADLE_CMD == null ?
- "Could not find Gradle or valid Gradle Wrapper" :
- "Attempt failed with message\n" + commandExists(new File(GRADLE_CMD)).getValue()
- ));
- }
- Log.info("Found build.gradle or build.gradle.kts in the project directory. Using Gradle to download dependencies.");
- tempInitScript = Files.writeString(tempInitScript, GRADLE_DEPENDENCIES_TASK);
- String[] gradleCommand;
- if (GRADLE_CMD.equals("gradlew") || GRADLE_CMD.equals("gradlew.bat")) {
- gradleCommand = new String[]{projectRoot + File.separator + GRADLE_CMD, "--init-script", tempInitScript.toFile().getAbsolutePath(), "downloadDependencies", "-PoutputDir=" + libDownloadPath.toString()};
- } else {
- gradleCommand = new String[]{GRADLE_CMD, "--init-script", tempInitScript.toFile().getAbsolutePath(), "downloadDependencies", "-PoutputDir=" + libDownloadPath.toString()};
- }
- return buildWithTool(gradleCommand);
- }
- return false;
- }
-
- public static void cleanLibraryDependencies() {
- if (noCleanDependencies) {
- return;
- }
- if (libDownloadPath != null) {
- Log.info("Cleaning up library dependency directory: " + libDownloadPath);
- try {
- if (libDownloadPath.toFile().getAbsoluteFile().exists()) {
- try (Stream paths = Files.walk(libDownloadPath)) {
- paths.sorted(Comparator.reverseOrder()) // Delete files first, then directories
- .map(Path::toFile)
- .forEach(file -> {
- if (!file.delete())
- Log.warn("Failed to delete: " + file.getAbsolutePath());
- });
- }
- }
- } catch (IOException e) {
- Log.warn("Unable to fully delete library dependency directory: " + e.getMessage());
- }
- }
- if (tempInitScript != null) {
- try {
- Files.delete(tempInitScript);
- } catch (IOException e) {
- Log.warn("Error deleting temporary Gradle init script: " + e.getMessage());
- }
- }
- }
-}
diff --git a/src/main/java/com/ibm/cldk/utils/Log.java b/src/main/java/com/ibm/cldk/utils/Log.java
deleted file mode 100644
index d0946ee1..00000000
--- a/src/main/java/com/ibm/cldk/utils/Log.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
-Copyright IBM Corporation 2023, 2024
-
-Licensed under the Apache Public License 2.0, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-
-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.
-*/
-
-package com.ibm.cldk.utils;
-
-import java.time.LocalDateTime;
-
-/**
- * The type Log.
- */
-public class Log {
- /**
- * The constant ANSI_RESET.
- */
- public static final String ANSI_RESET = "\u001B[0m";
- /**
- * The constant ANSI_BLACK.
- */
- public static final String ANSI_BLACK = "\u001B[30m";
- /**
- * The constant ANSI_RED.
- */
- public static final String ANSI_RED = "\u001B[31m";
- /**
- * The constant ANSI_GREEN.
- */
- public static final String ANSI_GREEN = "\u001B[32m";
- /**
- * The constant ANSI_YELLOW.
- */
- public static final String ANSI_YELLOW = "\u001B[33m";
- /**
- * The constant ANSI_BLUE.
- */
- public static final String ANSI_BLUE = "\u001B[34m";
- /**
- * The constant ANSI_PURPLE.
- */
- public static final String ANSI_PURPLE = "\u001B[35m";
- /**
- * The constant ANSI_CYAN.
- */
- public static final String ANSI_CYAN = "\u001B[36m";
- /**
- * The constant ANSI_WHITE.
- */
- public static final String ANSI_WHITE = "\u001B[37m";
- private static boolean verbose = true;
-
- /**
- * Set verbose setting to on or off.
- *
- * @param val True or false.
- */
- public static final void setVerbosity(boolean val) {
- verbose = val;
- }
-
- /**
- * Is verbosity turned on/off
- *
- * @return Boolean boolean
- */
- public static final boolean isVerbose() {
- return verbose;
- }
-
- /**
- * Info.
- *
- * @param msg the msg
- */
- public static final void info(String msg) {
- toConsole(msg, ANSI_PURPLE, "INFO");
- }
-
- /**
- * Done.
- *
- * @param msg the msg
- */
- public static final void done(String msg) {
- toConsole(msg, ANSI_GREEN, "DONE");
- }
-
- /**
- * Debug.
- *
- * @param msg the msg
- */
- public static final void debug(String msg) {
- toConsole(msg, ANSI_YELLOW, "DEBUG");
- }
-
- /**
- * Warn.
- *
- * @param msg the msg
- */
- public static final void warn(String msg) {
- toConsole(msg, ANSI_YELLOW, "WARN");
- }
-
- /**
- * Error.
- *
- * @param msg the msg
- */
- public static final void error(String msg) {
- toConsole(msg, ANSI_RED, "ERROR");
- }
-
- /**
- * Print log message to console
- *
- * @param msg to print to console
- */
- private static void toConsole(String msg, String ansi_color, String Level) {
- if (isVerbose()) {
- LocalDateTime localDateTime = LocalDateTime.now();
- System.out.println(
- ANSI_CYAN + localDateTime + ANSI_RESET + ansi_color + "\t[" + Level + "]\t" + ANSI_RESET + msg);
- }
- }
-}
diff --git a/src/main/java/com/ibm/cldk/utils/ProjectDirectoryScanner.java b/src/main/java/com/ibm/cldk/utils/ProjectDirectoryScanner.java
deleted file mode 100644
index 9a64edf7..00000000
--- a/src/main/java/com/ibm/cldk/utils/ProjectDirectoryScanner.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package com.ibm.cldk.utils;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-public class ProjectDirectoryScanner {
- public static List classFilesStream(String projectPath) throws IOException {
- Path projectDir = Paths.get(projectPath).toAbsolutePath();
- Log.info("Finding *.class files in " + projectDir);
- if (Files.exists(projectDir)) {
- try (Stream paths = Files.walk(projectDir)) {
- return paths.filter(file -> !Files.isDirectory(file) && file.toString().endsWith(".class"))
- .collect(Collectors.toList());
- }
- }
- return null;
- }
-
- public static List jarFilesStream(String projectPath) throws IOException {
- Path projectDir = Paths.get(projectPath);
- Log.info("Finding *.jar files in " + projectDir);
- if (Files.exists(projectDir)) {
- try (Stream paths = Files.walk(projectDir)) {
- return paths
- .filter(file -> !Files.isDirectory(file) && file.toString().endsWith(".jar"))
- .collect(Collectors.toList());
- }
- }
- return new ArrayList<>();
- }
-
- public static List sourceFilesStream(String projectPath) throws IOException {
- Path projectDir = Paths.get(projectPath);
- Log.info("Finding *.java files in " + projectDir);
- if (Files.exists(projectDir)) {
- try (Stream paths = Files.walk(projectDir)) {
- return paths
- .filter(file -> !Files.isDirectory(file))
- .filter(file -> file.toString().endsWith(".java"))
- .filter(file -> !file.toAbsolutePath().toString().contains("build/"))
- .filter(file -> !file.toAbsolutePath().toString().contains("target/"))
- .filter(file -> !file.toAbsolutePath().toString().contains("main/resources/"))
- .filter(file -> !file.toAbsolutePath().toString().contains("test/resources/"))
- .collect(Collectors.toList());
- }
- }
- return null;
- }
-
-}
diff --git a/src/main/java/com/ibm/cldk/utils/ScopeUtils.java b/src/main/java/com/ibm/cldk/utils/ScopeUtils.java
deleted file mode 100644
index 25db185c..00000000
--- a/src/main/java/com/ibm/cldk/utils/ScopeUtils.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
-Copyright IBM Corporation 2023, 2024
-
-Licensed under the Apache Public License 2.0, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-
-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.
-*/
-
-package com.ibm.cldk.utils;
-
-import static com.ibm.cldk.utils.ProjectDirectoryScanner.jarFilesStream;
-
-import com.ibm.wala.cast.java.ipa.callgraph.JavaSourceAnalysisScope;
-import com.ibm.wala.ipa.callgraph.AnalysisScope;
-import com.ibm.wala.shrike.shrikeCT.InvalidClassFileException;
-import com.ibm.wala.types.ClassLoaderReference;
-import com.ibm.wala.util.config.FileOfClasses;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.List;
-import java.util.Objects;
-import java.util.UUID;
-import java.util.jar.JarFile;
-import org.apache.commons.io.FileUtils;
-
-public class ScopeUtils {
-
- private static final String EXCLUSIONS = "";
-
- /**
- * The Std libs.
- */
- public static String[] stdLibs;
-
- /**
- * Create an javaee scope base on the input
- *
- * @param projectPath The root directory of the project to be analyzed.
- * @return scope The created javaee scope
- * @throws IOException the io exception
- */
- /**
- * Create an javaee scope base on the input
- *
- * @param projectPath The root directory of the project to be analyzed.
- * @param applicationDeps the application deps
- * @return scope The created javaee scope
- * @throws IOException the io exception
- */
- public static AnalysisScope createScope(String projectPath, String applicationDeps, String build)
- throws IOException {
- Log.info("Create javaee scope.");
- AnalysisScope scope = new JavaSourceAnalysisScope();
- addDefaultExclusions(scope);
-
- Log.info("Loading Java SE standard libs.");
-
- if (System.getenv("JAVA_HOME") == null) {
- Log.error("JAVA_HOME is not set.");
- throw new RuntimeException("JAVA_HOME is not set.");
- }
-
- String[] stdlibs = Files.walk(Paths.get(System.getenv("JAVA_HOME"), "jmods"))
- .filter(path -> path.toString().endsWith(".jmod"))
- .map(path -> path.toAbsolutePath().toString())
- .toArray(String[]::new);
-
- for (String stdlib : stdlibs) {
- scope.addToScope(ClassLoaderReference.Primordial, new JarFile(stdlib));
- }
- setStdLibs(stdlibs);
-
- // -------------------------------------
- // Add extra user provided JARS to scope
- // -------------------------------------
- if (!(applicationDeps == null)) {
- Log.info("Loading user specified extra libs.");
- Objects.requireNonNull(jarFilesStream(applicationDeps)).stream()
- .forEach(
- extraLibJar -> {
- Log.info("-> Adding dependency " + extraLibJar + " to javaee scope.");
- try {
- scope.addToScope(ClassLoaderReference.Extension, new JarFile(extraLibJar.toAbsolutePath().toFile()));
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- });
- } else {
- Log.warn("No extra libraries to process.");
- }
-
- Path path = Paths.get(FileUtils.getTempDirectory().getAbsolutePath(), UUID.randomUUID().toString());
- String tmpDirString = Files.createDirectories(path).toFile().getAbsolutePath();
- Path workDir = Paths.get(tmpDirString);
- FileUtils.cleanDirectory(workDir.toFile());
-
- List applicationClassFiles = BuildProject.buildProjectAndStreamClassFiles(projectPath, build);
- Log.debug("Application class files: " + String.valueOf(applicationClassFiles.size()));
- if (applicationClassFiles == null) {
- Log.error("No application classes found.");
- throw new RuntimeException("No application classes found.");
- }
- Log.info("Adding application classes to scope.");
- applicationClassFiles.forEach(
- applicationClassFile -> {
- try {
- scope.addClassFileToScope(
- ClassLoaderReference.Application, applicationClassFile.toFile());
- } catch (InvalidClassFileException e) {
- throw new RuntimeException(e);
- }
- });
-
- return scope;
- }
-
- private static AnalysisScope addDefaultExclusions(AnalysisScope scope)
- throws IOException {
- Log.info("Add exclusions to scope.");
- scope.setExclusions(new FileOfClasses(new ByteArrayInputStream(EXCLUSIONS.getBytes(StandardCharsets.UTF_8))));
- return scope;
- }
-
- private static void setStdLibs(String[] stdlibs) {
- stdLibs = stdlibs;
- }
-}
diff --git a/src/main/java/com/ibm/cldk/utils/annotations/NotImplemented.java b/src/main/java/com/ibm/cldk/utils/annotations/NotImplemented.java
deleted file mode 100644
index d2d084f6..00000000
--- a/src/main/java/com/ibm/cldk/utils/annotations/NotImplemented.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.ibm.cldk.utils.annotations;
-
-import java.lang.annotation.*;
-
-@Documented
-@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface NotImplemented {
- String value() default "";
- String comment() default "";
-}
diff --git a/src/main/java/com/ibm/cldk/utils/annotations/Note.java b/src/main/java/com/ibm/cldk/utils/annotations/Note.java
deleted file mode 100644
index cf12e6fa..00000000
--- a/src/main/java/com/ibm/cldk/utils/annotations/Note.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.ibm.cldk.utils.annotations;
-
-import java.lang.annotation.*;
-
-@Documented
-@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE, ElementType.PACKAGE, ElementType.TYPE_PARAMETER, ElementType.TYPE_USE, ElementType.MODULE, ElementType.ANNOTATION_TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface Note {
- String value() default "";
-}
diff --git a/src/main/java/com/ibm/cldk/utils/annotations/Todo.java b/src/main/java/com/ibm/cldk/utils/annotations/Todo.java
deleted file mode 100644
index 1cd2f6ad..00000000
--- a/src/main/java/com/ibm/cldk/utils/annotations/Todo.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.ibm.cldk.utils.annotations;
-
-import java.lang.annotation.*;
-
-@Documented
-@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface Todo {
- String value() default "";
- String issue() default "";
- String comment() default "";
-}
diff --git a/src/main/resources/META-INF/native-image-config/jni-config.json b/src/main/resources/META-INF/native-image-config/jni-config.json
deleted file mode 100644
index 8b4e4170..00000000
--- a/src/main/resources/META-INF/native-image-config/jni-config.json
+++ /dev/null
@@ -1,6 +0,0 @@
-[
-{
- "name":"java.lang.Boolean",
- "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }]
-}
-]
diff --git a/src/main/resources/META-INF/native-image-config/predefined-classes-config.json b/src/main/resources/META-INF/native-image-config/predefined-classes-config.json
deleted file mode 100644
index 0e79b2c5..00000000
--- a/src/main/resources/META-INF/native-image-config/predefined-classes-config.json
+++ /dev/null
@@ -1,8 +0,0 @@
-[
- {
- "type":"agent-extracted",
- "classes":[
- ]
- }
-]
-
diff --git a/src/main/resources/META-INF/native-image-config/proxy-config.json b/src/main/resources/META-INF/native-image-config/proxy-config.json
deleted file mode 100644
index 0d4f101c..00000000
--- a/src/main/resources/META-INF/native-image-config/proxy-config.json
+++ /dev/null
@@ -1,2 +0,0 @@
-[
-]
diff --git a/src/main/resources/META-INF/native-image-config/reflect-config.json b/src/main/resources/META-INF/native-image-config/reflect-config.json
deleted file mode 100644
index 2c027f53..00000000
--- a/src/main/resources/META-INF/native-image-config/reflect-config.json
+++ /dev/null
@@ -1,240 +0,0 @@
-[
-{
- "name":"[B"
-},
-{
- "name":"[Ljava.lang.String;"
-},
-{
- "name":"[Lsun.security.pkcs.SignerInfo;"
-},
-{
- "name":"com.ibm.northstar.CodeAnalyzer",
- "allDeclaredFields":true,
- "queryAllDeclaredMethods":true,
- "queryAllPublicMethods":true
-},
-{
- "name":"com.ibm.northstar.entities.Callable",
- "allDeclaredFields":true,
- "methods":[{"name":"","parameterTypes":[] }]
-},
-{
- "name":"com.ibm.northstar.entities.ClassOrInterface",
- "allDeclaredFields":true,
- "methods":[{"name":"","parameterTypes":[] }]
-},
-{
- "name":"com.ibm.northstar.entities.Field",
- "allDeclaredFields":true,
- "methods":[{"name":"","parameterTypes":[] }]
-},
-{
- "name":"com.ibm.northstar.entities.JavaCompilationUnit",
- "allDeclaredFields":true,
- "methods":[{"name":"","parameterTypes":[] }]
-},
-{
- "name":"com.ibm.northstar.entities.ParameterInCallable",
- "allDeclaredFields":true,
- "methods":[{"name":"","parameterTypes":[] }]
-},
-{
- "name":"com.ibm.northstar.entities.Type",
- "allDeclaredFields":true,
- "methods":[{"name":"","parameterTypes":[] }]
-},
-{
- "name":"java.lang.Class",
- "methods":[{"name":"getRecordComponents","parameterTypes":[] }, {"name":"isRecord","parameterTypes":[] }]
-},
-{
- "name":"java.lang.Object",
- "allDeclaredFields":true,
- "queryAllDeclaredMethods":true
-},
-{
- "name":"java.lang.String"
-},
-{
- "name":"java.lang.reflect.RecordComponent",
- "methods":[{"name":"getName","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }]
-},
-{
- "name":"java.nio.file.Path"
-},
-{
- "name":"java.nio.file.Paths",
- "methods":[{"name":"get","parameterTypes":["java.lang.String","java.lang.String[]"] }]
-},
-{
- "name":"java.security.interfaces.RSAPrivateKey"
-},
-{
- "name":"java.security.interfaces.RSAPublicKey"
-},
-{
- "name":"java.sql.Connection"
-},
-{
- "name":"java.sql.Date"
-},
-{
- "name":"java.sql.Driver"
-},
-{
- "name":"java.sql.DriverManager",
- "methods":[{"name":"getConnection","parameterTypes":["java.lang.String"] }, {"name":"getDriver","parameterTypes":["java.lang.String"] }]
-},
-{
- "name":"java.sql.Time",
- "methods":[{"name":"","parameterTypes":["long"] }]
-},
-{
- "name":"java.sql.Timestamp",
- "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
-},
-{
- "name":"java.time.Duration",
- "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
-},
-{
- "name":"java.time.Instant",
- "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
-},
-{
- "name":"java.time.LocalDate",
- "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
-},
-{
- "name":"java.time.LocalDateTime",
- "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
-},
-{
- "name":"java.time.LocalTime",
- "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
-},
-{
- "name":"java.time.MonthDay",
- "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
-},
-{
- "name":"java.time.OffsetDateTime",
- "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
-},
-{
- "name":"java.time.OffsetTime",
- "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
-},
-{
- "name":"java.time.Period",
- "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
-},
-{
- "name":"java.time.Year",
- "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
-},
-{
- "name":"java.time.YearMonth",
- "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
-},
-{
- "name":"java.time.ZoneId",
- "methods":[{"name":"of","parameterTypes":["java.lang.String"] }]
-},
-{
- "name":"java.time.ZoneOffset",
- "methods":[{"name":"of","parameterTypes":["java.lang.String"] }]
-},
-{
- "name":"java.time.ZonedDateTime",
- "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
-},
-{
- "name":"java.util.Date"
-},
-{
- "name":"java.util.HashMap",
- "methods":[{"name":"","parameterTypes":[] }]
-},
-{
- "name":"java.util.LinkedHashMap",
- "methods":[{"name":"","parameterTypes":[] }]
-},
-{
- "name":"java.util.concurrent.atomic.AtomicBoolean",
- "fields":[{"name":"value"}]
-},
-{
- "name":"javax.security.auth.x500.X500Principal",
- "fields":[{"name":"thisX500Name"}],
- "methods":[{"name":"","parameterTypes":["sun.security.x509.X500Name"] }]
-},
-{
- "name":"picocli.CommandLine$AutoHelpMixin",
- "allDeclaredFields":true,
- "queryAllDeclaredMethods":true
-},
-{
- "name":"sun.security.provider.SHA",
- "methods":[{"name":"","parameterTypes":[] }]
-},
-{
- "name":"sun.security.provider.SHA2$SHA256",
- "methods":[{"name":"","parameterTypes":[] }]
-},
-{
- "name":"sun.security.provider.X509Factory",
- "methods":[{"name":"","parameterTypes":[] }]
-},
-{
- "name":"sun.security.rsa.RSAKeyFactory$Legacy",
- "methods":[{"name":"","parameterTypes":[] }]
-},
-{
- "name":"sun.security.rsa.RSASignature$SHA256withRSA",
- "methods":[{"name":"","parameterTypes":[] }]
-},
-{
- "name":"sun.security.util.ObjectIdentifier"
-},
-{
- "name":"sun.security.x509.AuthorityInfoAccessExtension",
- "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
-},
-{
- "name":"sun.security.x509.AuthorityKeyIdentifierExtension",
- "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
-},
-{
- "name":"sun.security.x509.BasicConstraintsExtension",
- "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
-},
-{
- "name":"sun.security.x509.CRLDistributionPointsExtension",
- "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
-},
-{
- "name":"sun.security.x509.CertificateExtensions"
-},
-{
- "name":"sun.security.x509.CertificatePoliciesExtension",
- "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
-},
-{
- "name":"sun.security.x509.ExtendedKeyUsageExtension",
- "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
-},
-{
- "name":"sun.security.x509.KeyUsageExtension",
- "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
-},
-{
- "name":"sun.security.x509.SubjectAlternativeNameExtension",
- "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
-},
-{
- "name":"sun.security.x509.SubjectKeyIdentifierExtension",
- "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
-}
-]
diff --git a/src/main/resources/META-INF/native-image-config/resource-config.json b/src/main/resources/META-INF/native-image-config/resource-config.json
deleted file mode 100644
index 271f2e65..00000000
--- a/src/main/resources/META-INF/native-image-config/resource-config.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "resources":{
- "includes":[{
- "pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt67b/nfkc.nrm\\E"
- }]},
- "bundles":[]
-}
diff --git a/src/main/resources/META-INF/native-image-config/serialization-config.json b/src/main/resources/META-INF/native-image-config/serialization-config.json
deleted file mode 100644
index f3d7e06e..00000000
--- a/src/main/resources/META-INF/native-image-config/serialization-config.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "types":[
- ],
- "lambdaCapturingTypes":[
- ],
- "proxies":[
- ]
-}
diff --git a/src/styles/docs.css b/src/styles/docs.css
new file mode 100644
index 00000000..4d4c12b7
--- /dev/null
+++ b/src/styles/docs.css
@@ -0,0 +1,229 @@
+/* ============================================================================
+ CLDK docs theme — "MCP restraint on IBM Carbon".
+ Near-monochrome neutral surfaces; one IBM-blue accent; code + diagrams carry
+ the color. Typography is IBM Plex; layout tuned for reference density.
+ ========================================================================== */
+
+:root {
+ --sl-font: "Space Grotesk", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
+ --sl-font-mono: "Space Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
+
+ /* Slightly wider content for reference tables / signatures. */
+ --sl-content-width: 50rem;
+
+ /* Brand accent (IBM Carbon blue family). */
+ --cldk-blue: #0f62fe;
+}
+
+/* ---- Dark theme: Carbon gray-100 surfaces ------------------------------- */
+:root,
+:root[data-theme="dark"] {
+ --sl-color-accent-low: #0a2d6e;
+ --sl-color-accent: var(--cldk-blue);
+ --sl-color-accent-high: #a6c8ff;
+
+ --sl-color-bg: #161616; /* Carbon gray-100 */
+ --sl-color-bg-nav: #161616;
+ --sl-color-bg-sidebar: #1a1a1a;
+ --sl-color-bg-inline-code: #262626;
+ --sl-color-bg-accent: var(--cldk-blue);
+
+ --sl-color-hairline: #2a2a2a;
+ --sl-color-hairline-light: #333333;
+ --sl-color-hairline-shade: #202020;
+
+ --cldk-surface: #1f1f1f; /* card / panel surface */
+ --cldk-surface-2: #262626;
+ --cldk-border: #393939; /* Carbon gray-80 */
+}
+
+/* ---- Light theme: white / gray-10 --------------------------------------- */
+:root[data-theme="light"] {
+ --sl-color-accent-low: #d0e2ff;
+ --sl-color-accent: var(--cldk-blue);
+ --sl-color-accent-high: #002d6e;
+
+ --sl-color-bg: #ffffff;
+ --sl-color-bg-nav: #ffffff;
+ --sl-color-bg-sidebar: #f4f4f4; /* Carbon gray-10 */
+ --sl-color-bg-inline-code: #f4f4f4;
+
+ --sl-color-hairline: #e0e0e0;
+ --sl-color-hairline-light: #d0d0d0;
+ --sl-color-hairline-shade: #ececec;
+
+ --cldk-surface: #ffffff;
+ --cldk-surface-2: #f4f4f4;
+ --cldk-border: #d0d0d0; /* Carbon gray-30 */
+}
+
+html {
+ font-family: var(--sl-font);
+}
+
+/* Tighten heading weight for a reference-grade feel. */
+.sl-markdown-content h1,
+.sl-markdown-content h2,
+.sl-markdown-content h3 {
+ letter-spacing: -0.01em;
+}
+
+/* ============================================================================
+ Landing-page building blocks (splash template)
+ ========================================================================== */
+
+/* Badge row (arXiv / PyPI / license / Discord). */
+.cldk-badges {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ margin: 0 0 1.75rem;
+}
+.cldk-badges img {
+ height: 26px;
+}
+
+/* "First analysis" / inline install one-liner emphasis. */
+.cldk-oneliner {
+ font-size: var(--sl-text-lg);
+}
+
+/* scikit-learn-style capability grid: definition + thumbnail + examples. */
+.cldk-capabilities {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
+ gap: 1rem;
+ margin: 1.5rem 0 2rem;
+ padding: 0;
+ list-style: none;
+}
+.cldk-capability {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ padding: 1.1rem 1.2rem;
+ border: 1px solid var(--cldk-border);
+ border-radius: 0.6rem;
+ background: var(--cldk-surface);
+ color: inherit;
+ text-decoration: none;
+ transition: border-color 0.18s ease, transform 0.18s ease;
+}
+.cldk-capability:hover {
+ border-color: var(--sl-color-accent);
+ transform: translateY(-2px);
+}
+.cldk-capability__title {
+ font-weight: 600;
+ font-size: var(--sl-text-lg);
+ margin: 0;
+}
+.cldk-capability__def {
+ margin: 0;
+ color: var(--sl-color-gray-2);
+ font-size: var(--sl-text-sm);
+}
+.cldk-capability__thumb {
+ margin: 0.25rem 0;
+ padding: 0.6rem 0.7rem;
+ border-radius: 0.4rem;
+ background: var(--cldk-surface-2);
+ border: 1px solid var(--sl-color-hairline);
+ font-family: var(--sl-font-mono);
+ font-size: 0.74rem;
+ line-height: 1.45;
+ overflow-x: auto;
+ white-space: pre;
+ color: var(--sl-color-text);
+}
+.cldk-capability__examples {
+ margin: 0;
+ padding-left: 1.1rem;
+ font-size: var(--sl-text-xs);
+ color: var(--sl-color-gray-3);
+}
+
+/* "Agents prefer CLDK" call-out band. */
+.cldk-agent-band {
+ margin: 2rem 0;
+ padding: 1.5rem 1.6rem;
+ border: 1px solid var(--cldk-border);
+ border-left: 3px solid var(--sl-color-accent);
+ border-radius: 0.6rem;
+ background: var(--cldk-surface);
+}
+.cldk-agent-band h2 {
+ margin-top: 0 !important;
+}
+
+/* At-a-glance stat strip. */
+.cldk-stats {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 1rem;
+ margin: 1.5rem 0;
+ padding: 0;
+ list-style: none;
+}
+.cldk-stat {
+ flex: 1 1 8rem;
+ padding: 1rem 1.1rem;
+ border: 1px solid var(--cldk-border);
+ border-radius: 0.6rem;
+ background: var(--cldk-surface);
+ text-align: center;
+}
+.cldk-stat__num {
+ display: block;
+ font-size: 1.7rem;
+ font-weight: 700;
+ line-height: 1.2;
+}
+.cldk-stat__label {
+ font-size: var(--sl-text-xs);
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ color: var(--sl-color-gray-3);
+}
+
+/* Hero wordmark fallbacks (kept for non-splash uses). */
+.cldk-hero {
+ margin: 0.5rem 0 1.5rem;
+ text-align: center;
+}
+.cldk-hero img {
+ display: block;
+ width: min(100%, 620px);
+ height: auto;
+ margin: 0 auto;
+}
+:root[data-theme="dark"] .cldk-hero-light {
+ display: none;
+}
+:root[data-theme="light"] .cldk-hero-dark {
+ display: none;
+}
+
+/* Auto-generated API signatures wrap rather than overflow. */
+.sl-markdown-content pre.cldk-signature {
+ white-space: pre-wrap;
+}
+/* ---- In-content status/tier badges: subtle pastel pills, not loud blocks -- */
+.sl-markdown-content .sl-badge {
+ font-family: var(--sl-font);
+ font-size: 0.75rem;
+ font-weight: 600;
+ letter-spacing: 0.01em;
+ border-radius: 999px;
+ padding: 0.05rem 0.55rem;
+ border-width: 1px;
+ white-space: nowrap;
+ background: transparent;
+}
+
+.sl-markdown-content .sl-badge.success { color: #57b87e; border-color: rgba(36, 161, 72, 0.28); background: rgba(36, 161, 72, 0.08); }
+.sl-markdown-content .sl-badge.tip { color: #44b3b0; border-color: rgba(0, 157, 154, 0.28); background: rgba(0, 157, 154, 0.08); }
+.sl-markdown-content .sl-badge.caution { color: #cba43a; border-color: rgba(178, 134, 0, 0.28); background: rgba(178, 134, 0, 0.09); }
+.sl-markdown-content .sl-badge.note { color: #5b8def; border-color: rgba(15, 98, 254, 0.28); background: rgba(15, 98, 254, 0.08); }
+.sl-markdown-content .sl-badge.default { color: #9a9a9a; border-color: rgba(111, 111, 111, 0.28); background: rgba(111, 111, 111, 0.08); }
+.sl-markdown-content .sl-badge.danger { color: #e8737a; border-color: rgba(218, 30, 40, 0.28); background: rgba(218, 30, 40, 0.08); }
diff --git a/src/test/java/com/ibm/cldk/CodeAnalyzerIntegrationTest.java b/src/test/java/com/ibm/cldk/CodeAnalyzerIntegrationTest.java
deleted file mode 100644
index cd933cc4..00000000
--- a/src/test/java/com/ibm/cldk/CodeAnalyzerIntegrationTest.java
+++ /dev/null
@@ -1,445 +0,0 @@
-package com.ibm.cldk;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.nio.file.Paths;
-import java.util.Map;
-import java.util.Properties;
-import java.util.stream.StreamSupport;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.testcontainers.containers.GenericContainer;
-import org.testcontainers.junit.jupiter.Container;
-import org.testcontainers.junit.jupiter.Testcontainers;
-import org.testcontainers.utility.MountableFile;
-
-
-@Testcontainers
-@SuppressWarnings("resource")
-public class CodeAnalyzerIntegrationTest {
-
- /**
- * Creates a Java 11 test container that mounts the build/libs folder.
- */
- static String codeanalyzerVersion;
- static final String javaVersion = "17";
- static String javaHomePath;
-
- static {
- // Build project first
- try {
- Process process = new ProcessBuilder("./gradlew", "fatJar")
- .directory(new File(System.getProperty("user.dir")))
- .start();
- if (process.waitFor() != 0) {
- throw new RuntimeException("Build failed");
- }
- } catch (IOException | InterruptedException e) {
- throw new RuntimeException("Failed to build codeanalyzer", e);
- }
- }
-
- @Container
- static final GenericContainer> container = new GenericContainer<>("ubuntu:latest")
- .withCreateContainerCmdModifier(cmd -> cmd.withEntrypoint("sh"))
- .withCommand("-c", "while true; do sleep 1; done")
- .withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("build/libs")), "/opt/jars")
- .withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("build/libs")), "/opt/jars")
- .withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/mvnw-corrupt-test")), "/test-applications/mvnw-corrupt-test")
- .withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/plantsbywebsphere")), "/test-applications/plantsbywebsphere")
- .withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/call-graph-test")), "/test-applications/call-graph-test")
- .withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/record-class-test")), "/test-applications/record-class-test")
- .withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/init-blocks-test")), "/test-applications/init-blocks-test")
- .withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/mvnw-working-test")), "/test-applications/mvnw-working-test");
-
- @Container
- static final GenericContainer> mavenContainer = new GenericContainer<>("maven:3.8.3-openjdk-17")
- .withCreateContainerCmdModifier(cmd -> cmd.withEntrypoint("sh"))
- .withCommand("-c", "while true; do sleep 1; done")
- .withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("build/libs")), "/opt/jars")
- .withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/mvnw-corrupt-test")), "/test-applications/mvnw-corrupt-test")
- .withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/mvnw-working-test")), "/test-applications/mvnw-working-test")
- .withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/daytrader8")), "/test-applications/daytrader8");
-
- public CodeAnalyzerIntegrationTest() throws IOException, InterruptedException {
- }
-
- @BeforeAll
- static void setUp() {
- // Install Java 17 in the base container
- try {
- container.execInContainer("apt-get", "update");
- container.execInContainer("apt-get", "install", "-y", "openjdk-17-jdk");
-
- // Get JAVA_HOME dynamically
- var javaHomeResult = container.execInContainer("bash", "-c",
- "dirname $(dirname $(readlink -f $(which java)))"
- );
- javaHomePath = javaHomeResult.getStdout().trim();
- Assertions.assertFalse(javaHomePath.isEmpty(), "Failed to determine JAVA_HOME");
-
- } catch (IOException | InterruptedException e) {
- throw new RuntimeException(e);
- }
-
-
- // Get the version of the codeanalyzer jar
- Properties properties = new Properties();
- try (FileInputStream fis = new FileInputStream(
- Paths.get(System.getProperty("user.dir"), "gradle.properties").toFile())) {
- properties.load(fis);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- codeanalyzerVersion = properties.getProperty("version");
- }
-
- @Test
- void shouldHaveCorrectJavaVersionInstalled() throws Exception {
- var baseContainerresult = container.execInContainer("java", "-version");
- var mvnContainerresult = mavenContainer.execInContainer("java", "-version");
- Assertions.assertTrue(baseContainerresult.getStderr().contains("openjdk version \"" + javaVersion), "Base container Java version should be " + javaVersion);
- Assertions.assertTrue(mvnContainerresult.getStderr().contains("openjdk version \"" + javaVersion), "Maven container Java version should be " + javaVersion);
- }
-
- @Test
- void shouldHaveCodeAnalyzerJar() throws Exception {
- var dirContents = container.execInContainer("ls", "/opt/jars/");
- Assertions.assertTrue(dirContents.getStdout().length() > 0, "Directory listing should not be empty");
- Assertions.assertTrue(dirContents.getStdout().contains("codeanalyzer"), "Codeanalyzer.jar not found in the container.");
- }
-
- @Test
- void shouldBeAbleToRunCodeAnalyzer() throws Exception {
- var runCodeAnalyzerJar = container.execInContainer(
- "bash", "-c",
- String.format("export JAVA_HOME=%s && java -jar /opt/jars/codeanalyzer-%s.jar --help",
- javaHomePath, codeanalyzerVersion
- ));
-
- Assertions.assertEquals(0, runCodeAnalyzerJar.getExitCode(),
- "Command should execute successfully");
- Assertions.assertTrue(runCodeAnalyzerJar.getStdout().length() > 0,
- "Should have some output");
- }
-
- @Test
- void callGraphShouldHaveKnownEdges() throws Exception {
- var runCodeAnalyzerOnCallGraphTest = container.execInContainer(
- "bash", "-c",
- String.format(
- "export JAVA_HOME=%s && java -jar /opt/jars/codeanalyzer-%s.jar --input=/test-applications/call-graph-test --analysis-level=2",
- javaHomePath, codeanalyzerVersion
- )
- );
-
-
- // Read the output JSON
- Gson gson = new Gson();
- JsonObject jsonObject = gson.fromJson(runCodeAnalyzerOnCallGraphTest.getStdout(), JsonObject.class);
- JsonArray callGraph = jsonObject.getAsJsonArray("call_graph");
- Assertions.assertTrue(StreamSupport.stream(callGraph.spliterator(), false)
- .map(JsonElement::getAsJsonObject)
- .anyMatch(entry ->
- "CALL_DEP".equals(entry.get("type").getAsString()) &&
- "1".equals(entry.get("weight").getAsString()) &&
- entry.getAsJsonObject("source").get("signature").getAsString().equals("helloString()") &&
- entry.getAsJsonObject("target").get("signature").getAsString().equals("log()")
- ), "Expected edge not found in the system dependency graph");
- }
-
- @Test
- void corruptMavenShouldNotBuildWithWrapper() throws IOException, InterruptedException {
- // Make executable
- mavenContainer.execInContainer("chmod", "+x", "/test-applications/mvnw-corrupt-test/mvnw");
- // Let's start by building the project by itself
- var mavenProjectBuildWithWrapper = mavenContainer.withWorkingDirectory("/test-applications/mvnw-corrupt-test").execInContainer("/test-applications/mvnw-corrupt-test/mvnw", "clean", "compile");
- Assertions.assertNotEquals(0, mavenProjectBuildWithWrapper.getExitCode());
- }
-
- @Test
- void corruptMavenShouldProduceAnalysisArtifactsWhenMVNCommandIsInPath() throws IOException, InterruptedException {
- // Let's start by building the project by itself
- var corruptMavenProjectBuild = mavenContainer.withWorkingDirectory("/test-applications/mvnw-corrupt-test").execInContainer("mvn", "-f", "/test-applications/mvnw-corrupt-test/pom.xml", "clean", "compile");
- Assertions.assertEquals(0, corruptMavenProjectBuild.getExitCode(), "Failed to build the project with system's default Maven.");
- // NOw run codeanalyzer and assert if analysis.json is generated.
- var runCodeAnalyzer = mavenContainer.execInContainer("java", "-jar", String.format("/opt/jars/codeanalyzer-%s.jar", codeanalyzerVersion), "--input=/test-applications/mvnw-corrupt-test", "--output=/tmp/", "--analysis-level=2", "--verbose", "--no-build");
- var codeAnalyzerOutputDirContents = mavenContainer.execInContainer("ls", "/tmp/analysis.json");
- String codeAnalyzerOutputDirContentsStdOut = codeAnalyzerOutputDirContents.getStdout();
- Assertions.assertTrue(codeAnalyzerOutputDirContentsStdOut.length() > 0, "Could not find 'analysis.json'.");
- // mvnw is corrupt, so we should see an error message in the output.
- Assertions.assertTrue(runCodeAnalyzer.getStdout().contains("[ERROR]\tCannot run program \"/test-applications/mvnw-corrupt-test/mvnw\"") && runCodeAnalyzer.getStdout().contains("/mvn."));
- // We should correctly identify the build tool used in the mvn command from the system path.
- Assertions.assertTrue(runCodeAnalyzer.getStdout().contains("[INFO]\tBuilding the project using /usr/bin/mvn."));
- }
-
- @Test
- void corruptMavenShouldNotTerminateWithErrorWhenMavenIsNotPresentUnlessAnalysisLevel2() throws IOException, InterruptedException {
- // When analysis level 2, we should get a Runtime Exception
- var runCodeAnalyzer = container.execInContainer(
- "bash", "-c",
- String.format(
- "export JAVA_HOME=%s && java -jar /opt/jars/codeanalyzer-%s.jar --input=/test-applications/mvnw-corrupt-test --output=/tmp/ --analysis-level=2",
- javaHomePath, codeanalyzerVersion
- )
- );
-
- Assertions.assertEquals(1, runCodeAnalyzer.getExitCode());
- Assertions.assertTrue(runCodeAnalyzer.getStderr().contains("java.lang.RuntimeException"));
- }
-
- @Test
- void shouldBeAbleToGenerateAnalysisArtifactForDaytrader8() throws Exception {
- var runCodeAnalyzerOnDaytrader8 = mavenContainer.execInContainer(
- "bash", "-c",
- String.format(
- "export JAVA_HOME=%s && java -jar /opt/jars/codeanalyzer-%s.jar --input=/test-applications/daytrader8 --analysis-level=1",
- javaHomePath, codeanalyzerVersion
- )
- );
-
- Assertions.assertTrue(runCodeAnalyzerOnDaytrader8.getStdout().contains("\"is_entrypoint_class\": true"), "No entry point classes found");
- Assertions.assertTrue(runCodeAnalyzerOnDaytrader8.getStdout().contains("\"is_entrypoint\": true"), "No entry point methods found");
- }
-
-
- @Test
- void shouldBeAbleToDetectCRUDOperationsAndQueriesForPlantByWebsphere() throws Exception {
- var runCodeAnalyzerOnPlantsByWebsphere = container.execInContainer(
- "bash", "-c",
- String.format(
- "export JAVA_HOME=%s && java -jar /opt/jars/codeanalyzer-%s.jar --input=/test-applications/plantsbywebsphere --analysis-level=1",
- javaHomePath, codeanalyzerVersion
- )
- );
-
-
- Assertions.assertEquals(0, runCodeAnalyzerOnPlantsByWebsphere.getExitCode(), "CodeAnalyzer command should succeed");
- String output = runCodeAnalyzerOnPlantsByWebsphere.getStdout();
- Gson gson = new Gson();
- JsonObject jsonObject = gson.fromJson(output, JsonObject.class);
- JsonObject symbolTable = jsonObject.getAsJsonObject("symbol_table");
- Assertions.assertNotNull(symbolTable);
- Assertions.assertTrue(symbolTable.size() > 0, "Symbol table should not be empty");
-
- boolean hasReadOperation = false;
- boolean hasCreateOperation = false;
- boolean hasUpdateOperation = false;
- boolean hasNamedQuery = false;
- int crudOperationCount = 0;
- int crudQueryCount = 0;
-
- for (Map.Entry compilationUnitEntry : symbolTable.entrySet()) {
- JsonObject compilationUnit = compilationUnitEntry.getValue().getAsJsonObject();
- if (!compilationUnit.has("type_declarations")) {
- continue;
- }
- JsonObject typeDeclarations = compilationUnit.getAsJsonObject("type_declarations");
- for (Map.Entry typeEntry : typeDeclarations.entrySet()) {
- JsonObject typeDeclaration = typeEntry.getValue().getAsJsonObject();
- if (!typeDeclaration.has("callable_declarations")) {
- continue;
- }
- JsonObject callableDeclarations = typeDeclaration.getAsJsonObject("callable_declarations");
- for (Map.Entry callableEntry : callableDeclarations.entrySet()) {
- JsonObject callable = callableEntry.getValue().getAsJsonObject();
- JsonArray crudOperations = callable.getAsJsonArray("crud_operations");
- if (crudOperations != null) {
- for (JsonElement crudOperationElement : crudOperations) {
- JsonObject crudOperation = crudOperationElement.getAsJsonObject();
- crudOperationCount++;
- Assertions.assertTrue(crudOperation.has("line_number"), "CRUD operation should have line_number");
- Assertions.assertTrue(crudOperation.has("operation_type"), "CRUD operation should have operation_type");
- Assertions.assertTrue(crudOperation.has("target_table"), "CRUD operation should have target_table");
- Assertions.assertTrue(crudOperation.has("involved_columns"), "CRUD operation should have involved_columns");
- Assertions.assertTrue(crudOperation.has("condition"), "CRUD operation should have condition");
- Assertions.assertTrue(crudOperation.has("joined_tables"), "CRUD operation should have joined_tables");
- String operationType = crudOperation.get("operation_type").getAsString();
- int lineNumber = crudOperation.get("line_number").getAsInt();
- Assertions.assertTrue(lineNumber > 0, "CRUD operation should have positive line_number");
- if ("READ".equals(operationType)) {
- hasReadOperation = true;
- }
- if ("CREATE".equals(operationType)) {
- hasCreateOperation = true;
- }
- if ("UPDATE".equals(operationType)) {
- hasUpdateOperation = true;
- }
- }
- }
- JsonArray crudQueries = callable.getAsJsonArray("crud_queries");
- if (crudQueries != null) {
- for (JsonElement crudQueryElement : crudQueries) {
- JsonObject crudQuery = crudQueryElement.getAsJsonObject();
- crudQueryCount++;
- Assertions.assertTrue(crudQuery.has("line_number"), "CRUD query should have line_number");
- Assertions.assertTrue(crudQuery.has("query_type"), "CRUD query should have query_type");
- Assertions.assertTrue(crudQuery.has("query_arguments"), "CRUD query should have query_arguments");
- String queryType = crudQuery.get("query_type").getAsString();
- int lineNumber = crudQuery.get("line_number").getAsInt();
- Assertions.assertTrue(lineNumber > 0, "CRUD query should have positive line_number");
- if ("NAMED".equals(queryType)) {
- hasNamedQuery = true;
- }
- }
- }
- }
- }
- }
-
- Assertions.assertTrue(crudOperationCount > 0, "No CRUD operations found");
- Assertions.assertTrue(crudQueryCount > 0, "No CRUD queries found");
- Assertions.assertTrue(hasNamedQuery, "No NAMED CRUD query found");
- Assertions.assertTrue(hasReadOperation, "No READ CRUD operation found");
- Assertions.assertTrue(hasCreateOperation, "No CREATE CRUD operation found");
- Assertions.assertTrue(hasUpdateOperation, "No UPDATE CRUD operation found");
- }
-
- @Test
- void symbolTableShouldHaveRecords() throws IOException, InterruptedException {
- var runCodeAnalyzerOnCallGraphTest = container.execInContainer(
- "bash", "-c",
- String.format(
- "export JAVA_HOME=%s && java -jar /opt/jars/codeanalyzer-%s.jar --input=/test-applications/record-class-test --analysis-level=1",
- javaHomePath, codeanalyzerVersion
- )
- );
-
- // Read the output JSON
- Gson gson = new Gson();
- JsonObject jsonObject = gson.fromJson(runCodeAnalyzerOnCallGraphTest.getStdout(), JsonObject.class);
- JsonObject symbolTable = jsonObject.getAsJsonObject("symbol_table");
- Assertions.assertEquals(4, symbolTable.size(), "Symbol table should have 4 records");
- }
-
- @Test
- void symbolTableShouldHaveDefaultRecordComponents() throws IOException, InterruptedException {
- var runCodeAnalyzerOnCallGraphTest = container.execInContainer(
- "bash", "-c",
- String.format(
- "export JAVA_HOME=%s && java -jar /opt/jars/codeanalyzer-%s.jar --input=/test-applications/record-class-test --analysis-level=1",
- javaHomePath, codeanalyzerVersion
- )
- );
-
- // Read the output JSON
- Gson gson = new Gson();
- JsonObject jsonObject = gson.fromJson(runCodeAnalyzerOnCallGraphTest.getStdout(), JsonObject.class);
- JsonObject symbolTable = jsonObject.getAsJsonObject("symbol_table");
- for (Map.Entry element : symbolTable.entrySet()) {
- String key = element.getKey();
- if (!key.endsWith("PersonRecord.java")) {
- continue;
- }
- JsonObject type = element.getValue().getAsJsonObject();
- if (type.has("type_declarations")) {
- JsonObject typeDeclarations = type.getAsJsonObject("type_declarations");
- JsonArray recordComponent = typeDeclarations.getAsJsonObject("org.example.PersonRecord").getAsJsonArray("record_components");
- Assertions.assertEquals(2, recordComponent.size(), "Record component should have 2 components");
- JsonObject record = recordComponent.get(1).getAsJsonObject();
- Assertions.assertTrue(record.get("name").getAsString().equals("age") && record.get("default_value").getAsInt() == 18, "Record component should have a name");
- }
- }
- }
-
- @Test
- void parametersInCallableMustHaveStartAndEndLineAndColumns() throws IOException, InterruptedException {
- var runCodeAnalyzerOnCallGraphTest = container.execInContainer(
- "bash", "-c",
- String.format(
- "export JAVA_HOME=%s && java -jar /opt/jars/codeanalyzer-%s.jar --input=/test-applications/record-class-test --analysis-level=1",
- javaHomePath, codeanalyzerVersion
- )
- );
-
- // Read the output JSON
- Gson gson = new Gson();
- JsonObject jsonObject = gson.fromJson(runCodeAnalyzerOnCallGraphTest.getStdout(), JsonObject.class);
- JsonObject symbolTable = jsonObject.getAsJsonObject("symbol_table");
- for (Map.Entry element : symbolTable.entrySet()) {
- String key = element.getKey();
- if (!key.endsWith("App.java")) {
- continue;
- }
- JsonObject type = element.getValue().getAsJsonObject();
- if (type.has("type_declarations")) {
- JsonObject typeDeclarations = type.getAsJsonObject("type_declarations");
- JsonObject mainMethod = typeDeclarations.getAsJsonObject("org.example.App")
- .getAsJsonObject("callable_declarations")
- .getAsJsonObject("main(java.lang.String[])");
- JsonArray parameters = mainMethod.getAsJsonArray("parameters");
- // There should be 1 parameter
- Assertions.assertEquals(1, parameters.size(), "Callable should have 1 parameter");
- JsonObject parameter = parameters.get(0).getAsJsonObject();
- // Start and end line and column should not be -1
- Assertions.assertTrue(parameter.get("start_line").getAsInt() == 7 && parameter.get("end_line").getAsInt() == 7 && parameter.get("start_column").getAsInt() == 29 && parameter.get("end_column").getAsInt() == 41, "Parameter should have start and end line and columns");
- }
- }
- }
-
- @Test
- void mustBeAbleToResolveInitializationBlocks() throws IOException, InterruptedException {
- var runCodeAnalyzerOnCallGraphTest = container.execInContainer(
- "bash", "-c",
- String.format(
- "export JAVA_HOME=%s && java -jar /opt/jars/codeanalyzer-%s.jar --input=/test-applications/init-blocks-test --analysis-level=1",
- javaHomePath, codeanalyzerVersion
- )
- );
-
- // Read the output JSON
- Gson gson = new Gson();
- JsonObject jsonObject = gson.fromJson(runCodeAnalyzerOnCallGraphTest.getStdout(), JsonObject.class);
- JsonObject symbolTable = jsonObject.getAsJsonObject("symbol_table");
- for (Map.Entry element : symbolTable.entrySet()) {
- String key = element.getKey();
- if (!key.endsWith("App.java")) {
- continue;
- }
- JsonObject type = element.getValue().getAsJsonObject();
- if (type.has("type_declarations")) {
- JsonObject typeDeclarations = type.getAsJsonObject("type_declarations");
- JsonArray initializationBlocks = typeDeclarations.getAsJsonObject("org.example.App").getAsJsonArray("initialization_blocks");
- // There should be 2 blocks
- Assertions.assertEquals(2, initializationBlocks.size(), "Callable should have 1 parameter");
- Assertions.assertTrue(initializationBlocks.get(0).getAsJsonObject().get("is_static").getAsBoolean(), "Static block should be marked as static");
- Assertions.assertFalse(initializationBlocks.get(1).getAsJsonObject().get("is_static").getAsBoolean(), "Instance block should be marked as not static");
- }
- }
- }
-
- @Test
- void mustBeAbleToExtractCommentBlocks() throws IOException, InterruptedException {
- var runCodeAnalyzerOnCallGraphTest = container.execInContainer(
- "bash", "-c",
- String.format(
- "export JAVA_HOME=%s && java -jar /opt/jars/codeanalyzer-%s.jar --input=/test-applications/init-blocks-test --analysis-level=1",
- javaHomePath, codeanalyzerVersion
- )
- );
-
- // Read the output JSON
- Gson gson = new Gson();
- JsonObject jsonObject = gson.fromJson(runCodeAnalyzerOnCallGraphTest.getStdout(), JsonObject.class);
- JsonObject symbolTable = jsonObject.getAsJsonObject("symbol_table");
- for (Map.Entry element : symbolTable.entrySet()) {
- String key = element.getKey();
- if (!key.endsWith("App.java")) {
- continue;
- }
- JsonObject type = element.getValue().getAsJsonObject();
- JsonArray comments = type.getAsJsonArray("comments");
- Assertions.assertEquals(16, comments.size(), "Should have 15 comments");
- Assertions.assertTrue(StreamSupport.stream(comments.spliterator(), false)
- .map(JsonElement::getAsJsonObject)
- .anyMatch(comment -> comment.get("is_javadoc").getAsBoolean()), "Single line comment not found");
- }
- }
-}
diff --git a/src/test/java/com/ibm/cldk/CodeAnalyzerTest.java b/src/test/java/com/ibm/cldk/CodeAnalyzerTest.java
deleted file mode 100644
index d3aed26c..00000000
--- a/src/test/java/com/ibm/cldk/CodeAnalyzerTest.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package com.ibm.cldk;
-
-import com.ibm.cldk.entities.Import;
-import com.ibm.cldk.entities.JavaCompilationUnit;
-import java.io.File;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.List;
-import java.util.Map;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-
-public class CodeAnalyzerTest {
-
- @TempDir
- Path tempDir;
-
- @SuppressWarnings("unchecked")
- private Map invokeReadSymbolTableFromFile(Path analysisFilePath) throws Exception {
- Method readSymbolTableMethod = CodeAnalyzer.class.getDeclaredMethod("readSymbolTableFromFile", File.class);
- readSymbolTableMethod.setAccessible(true);
- try {
- return (Map) readSymbolTableMethod.invoke(null, analysisFilePath.toFile());
- } catch (InvocationTargetException invocationTargetException) {
- Throwable targetException = invocationTargetException.getTargetException();
- if (targetException instanceof Exception) {
- throw (Exception) targetException;
- }
- throw new RuntimeException(targetException);
- }
- }
-
- private Path writeAnalysisFile(String jsonContent) throws Exception {
- Path analysisFilePath = tempDir.resolve("analysis.json");
- Files.writeString(analysisFilePath, jsonContent, StandardCharsets.UTF_8);
- return analysisFilePath;
- }
-
- @Test
- public void testReadSymbolTableFromFileRejectsLegacyImportSchema() throws Exception {
- String jsonContent = "{\n"
- + " \"symbol_table\": {\n"
- + " \"/tmp/T.java\": {\n"
- + " \"file_path\": \"/tmp/T.java\",\n"
- + " \"package_name\": \"\",\n"
- + " \"comments\": [],\n"
- + " \"imports\": [\"java.util.List\"],\n"
- + " \"type_declarations\": {},\n"
- + " \"is_modified\": false\n"
- + " }\n"
- + " }\n"
- + "}\n";
- Path analysisFilePath = writeAnalysisFile(jsonContent);
-
- IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class,
- () -> invokeReadSymbolTableFromFile(analysisFilePath));
- Assertions.assertTrue(exception.getMessage().contains("legacy import schema"));
- }
-
- @Test
- public void testReadSymbolTableFromFileParsesExplicitImportSchema() throws Exception {
- String jsonContent = "{\n"
- + " \"symbol_table\": {\n"
- + " \"/tmp/T.java\": {\n"
- + " \"file_path\": \"/tmp/T.java\",\n"
- + " \"package_name\": \"\",\n"
- + " \"comments\": [],\n"
- + " \"imports\": [\n"
- + " {\n"
- + " \"path\": \"java.util.List\",\n"
- + " \"is_static\": false,\n"
- + " \"is_wildcard\": false\n"
- + " },\n"
- + " {\n"
- + " \"path\": \"java.util.Collections\",\n"
- + " \"is_static\": true,\n"
- + " \"is_wildcard\": true\n"
- + " }\n"
- + " ],\n"
- + " \"type_declarations\": {},\n"
- + " \"is_modified\": false\n"
- + " }\n"
- + " }\n"
- + "}\n";
- Path analysisFilePath = writeAnalysisFile(jsonContent);
-
- Map symbolTable = invokeReadSymbolTableFromFile(analysisFilePath);
- Assertions.assertNotNull(symbolTable);
- Assertions.assertEquals(1, symbolTable.size());
-
- JavaCompilationUnit compilationUnit = symbolTable.get("/tmp/T.java");
- Assertions.assertNotNull(compilationUnit);
- List imports = compilationUnit.getImports();
- Assertions.assertNotNull(imports);
- Assertions.assertEquals(2, imports.size());
-
- Import defaultImport = imports.stream()
- .filter(imp -> "java.util.List".equals(imp.getPath()))
- .findFirst()
- .orElse(null);
- Assertions.assertNotNull(defaultImport);
- Assertions.assertFalse(defaultImport.isStatic());
- Assertions.assertFalse(defaultImport.isWildcard());
-
- Import staticWildcardImport = imports.stream()
- .filter(imp -> "java.util.Collections".equals(imp.getPath()))
- .findFirst()
- .orElse(null);
- Assertions.assertNotNull(staticWildcardImport);
- Assertions.assertTrue(staticWildcardImport.isStatic());
- Assertions.assertTrue(staticWildcardImport.isWildcard());
- }
-}
diff --git a/src/test/java/com/ibm/cldk/SymbolTableTest.java b/src/test/java/com/ibm/cldk/SymbolTableTest.java
deleted file mode 100644
index 2dcc6697..00000000
--- a/src/test/java/com/ibm/cldk/SymbolTableTest.java
+++ /dev/null
@@ -1,134 +0,0 @@
-package com.ibm.cldk;
-
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import com.ibm.cldk.entities.CallSite;
-import com.ibm.cldk.entities.Callable;
-import com.ibm.cldk.entities.Import;
-import com.ibm.cldk.entities.JavaCompilationUnit;
-import com.ibm.cldk.entities.Type;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-public class SymbolTableTest {
-
- private String getJavaCodeForTestResource(String resourcePath) {
- InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourcePath);
- assert inputStream != null;
- return new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))
- .lines()
- .collect(Collectors.joining("\n"));
- }
-
- @Test
- public void testExtractSingleGenricsDuplicateSignature_Validate() throws IOException {
- String javaCode = getJavaCodeForTestResource("test-applications/generics-varargs-duplicate-signature-test/Validate.java");
- Map symbolTable = SymbolTable.extractSingle(javaCode).getLeft();
- Assertions.assertEquals(1, symbolTable.size());
- Map typeDeclaration = symbolTable.values().iterator().next().getTypeDeclarations();
- Assertions.assertEquals(1, typeDeclaration.size());
- Map callables = typeDeclaration.values().iterator().next().getCallableDeclarations();
- Assertions.assertEquals(17, callables.size());
- }
-
- @Test
- public void testExtractSingleGenricsDuplicateSignature_FunctorUtils() throws IOException {
- String javaCode = getJavaCodeForTestResource("test-applications/generics-varargs-duplicate-signature-test/FunctorUtils.java");
- Map symbolTable = SymbolTable.extractSingle(javaCode).getLeft();
- Assertions.assertEquals(1, symbolTable.size());
- Map typeDeclaration = symbolTable.values().iterator().next().getTypeDeclarations();
- Assertions.assertEquals(1, typeDeclaration.size());
- Map callables = typeDeclaration.values().iterator().next().getCallableDeclarations();
- Assertions.assertEquals(10, callables.size());
- }
-
- @Test
- public void testExtractSingleMissingNodeRange() throws IOException {
- String javaCode = getJavaCodeForTestResource("test-applications/missing-node-range-test/WeakHashtableTestCase.java");
- Map symbolTable = SymbolTable.extractSingle(javaCode).getLeft();
- Assertions.assertEquals(1, symbolTable.size());
- Map typeDeclaration = symbolTable.values().iterator().next().getTypeDeclarations();
- Assertions.assertEquals(2, typeDeclaration.size());
- }
-
- @Test
- public void testExtractSingleDefaultKeywordMethodDecl() throws IOException {
- String javaCode = getJavaCodeForTestResource("test-applications/default-keyword-method-decl/IndexExtractor.java");
- Map symbolTable = SymbolTable.extractSingle(javaCode).getLeft();
- Assertions.assertEquals(1, symbolTable.size());
- Map typeDeclaration = symbolTable.values().iterator().next().getTypeDeclarations();
- Assertions.assertEquals(1, typeDeclaration.size());
- Map callables = typeDeclaration.values().iterator().next().getCallableDeclarations();
- Assertions.assertEquals(5, callables.size());
- }
-
- @Test
- public void testCallSiteArgumentExpression() throws IOException {
- String javaCode = getJavaCodeForTestResource("test-applications/generics-varargs-duplicate-signature-test/Validate.java");
- Map typeDeclaration = SymbolTable.extractSingle(javaCode).getLeft()
- .values().iterator().next().getTypeDeclarations();
- Callable callable = typeDeclaration.values().iterator().next().getCallableDeclarations()
- .get("notEmpty(java.util.Collection>, java.lang.String, java.lang.Object[])");
- Assertions.assertNotNull(callable);
- for (CallSite callSite : callable.getCallSites()) {
- if (callSite.getMethodName().equals("requireNonNull")) {
- String[] expectedArgumentExpr = {"collection", "toSupplier(message, values)"};
- List argumentExpr = callSite.getArgumentExpr();
- Assertions.assertArrayEquals(expectedArgumentExpr, argumentExpr.toArray(new String[0]));
- break;
- }
- }
- }
-
- @Test
- public void testExtractSingleImportMetadata() throws IOException {
- String javaCode = String.join("\n",
- "import java.util.List;",
- "import java.util.Map.*;",
- "import static java.util.Collections.emptyList;",
- "import static java.util.Collections.*;",
- "class T {}");
- Map symbolTable = SymbolTable.extractSingle(javaCode).getLeft();
- Assertions.assertEquals(1, symbolTable.size());
- List imports = symbolTable.values().iterator().next().getImports();
- Assertions.assertNotNull(imports);
- Assertions.assertEquals(4, imports.size());
-
- assertImport(imports, "java.util.List", false, false);
- assertImport(imports, "java.util.Map", false, true);
- assertImport(imports, "java.util.Collections.emptyList", true, false);
- assertImport(imports, "java.util.Collections", true, true);
-
- JsonArray serializedImports = JsonParser.parseString(CodeAnalyzer.gson.toJson(imports)).getAsJsonArray();
- Assertions.assertEquals(4, serializedImports.size());
- for (JsonElement serializedImport : serializedImports) {
- Assertions.assertTrue(serializedImport.isJsonObject());
- JsonObject serializedImportObject = serializedImport.getAsJsonObject();
- Assertions.assertTrue(serializedImportObject.has("path"));
- Assertions.assertTrue(serializedImportObject.has("is_static"));
- Assertions.assertTrue(serializedImportObject.has("is_wildcard"));
- }
- }
-
- private static void assertImport(List imports, String path, boolean isStatic, boolean isWildcard) {
- Import matchingImport = imports.stream()
- .filter(imp -> path.equals(imp.getPath())
- && imp.isStatic() == isStatic
- && imp.isWildcard() == isWildcard)
- .findFirst()
- .orElse(null);
- Assertions.assertNotNull(matchingImport,
- String.format("Expected import '%s' with isStatic=%s and isWildcard=%s", path, isStatic, isWildcard));
- }
-
-}
diff --git a/src/test/java/com/ibm/cldk/utils/BuildProjectTest.java b/src/test/java/com/ibm/cldk/utils/BuildProjectTest.java
deleted file mode 100644
index 48e3aae0..00000000
--- a/src/test/java/com/ibm/cldk/utils/BuildProjectTest.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package com.ibm.cldk.utils;
-
-public class BuildProjectTest {
-}
diff --git a/src/test/resources/generated/.gitignore b/src/test/resources/generated/.gitignore
deleted file mode 100644
index 94a2dd14..00000000
--- a/src/test/resources/generated/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*.json
\ No newline at end of file
diff --git a/src/test/resources/test-applications/.gitignore b/src/test/resources/test-applications/.gitignore
deleted file mode 100644
index 11609223..00000000
--- a/src/test/resources/test-applications/.gitignore
+++ /dev/null
@@ -1,57 +0,0 @@
-# Compiled class file
-*.class
-
-# Log file
-*.log
-
-# BlueJ files
-*.ctxt
-
-# Mobile Tools for Java (J2ME)
-.mtj.tmp/
-
-# Package Files #
-*.jar
-*.war
-*.ear
-*.zip
-*.tar.gz
-*.rar
-
-# Don't ignore jar files in any level of binary and dependencies
-!**/binaries/**/*.jar
-!**/libs/**/*.jar
-
-# virtual machine crash logs
-hs_err_pid*
-
-# Ignore Gradle files
-.gradle/
-build/
-
-# Don't ignore Gradle wrapper jar file
-!gradle-wrapper.jar
-
-# Ignore Maven target folder
-target/
-
-# Ignore IntelliJ IDEA files
-.idea/
-*.iml
-*.iws
-*.ipr
-
-# Ignore Eclipse files
-.settings/
-*.classpath
-*.project
-
-# Ignore VS Code files
-.vscode/
-
-# Ignore everything in codeql-db except the directory itself
-codeql-db/*
-!codeql-db/.keep
-
-# Ignore hidden apps
-hidden-apps/
\ No newline at end of file
diff --git a/src/test/resources/test-applications/call-graph-test/.gitattributes b/src/test/resources/test-applications/call-graph-test/.gitattributes
deleted file mode 100644
index 097f9f98..00000000
--- a/src/test/resources/test-applications/call-graph-test/.gitattributes
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# https://help.github.com/articles/dealing-with-line-endings/
-#
-# Linux start script should use lf
-/gradlew text eol=lf
-
-# These are Windows script files and should use crlf
-*.bat text eol=crlf
-
diff --git a/src/test/resources/test-applications/call-graph-test/.gitignore b/src/test/resources/test-applications/call-graph-test/.gitignore
deleted file mode 100644
index 1b6985c0..00000000
--- a/src/test/resources/test-applications/call-graph-test/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-# Ignore Gradle project-specific cache directory
-.gradle
-
-# Ignore Gradle build output directory
-build
diff --git a/src/test/resources/test-applications/call-graph-test/build.gradle b/src/test/resources/test-applications/call-graph-test/build.gradle
deleted file mode 100644
index 709e182c..00000000
--- a/src/test/resources/test-applications/call-graph-test/build.gradle
+++ /dev/null
@@ -1,30 +0,0 @@
-plugins {
- id 'application'
-}
-
-repositories {
- mavenCentral()
-}
-
-java {
- sourceCompatibility = JavaVersion.VERSION_11
- targetCompatibility = JavaVersion.VERSION_11
-}
-
-if (project.hasProperty('mainClass')) {
- mainClassName = project.getProperty('mainClass')
-} else {
- // use a default
- mainClassName =("org.example.User")
-}
-
-sourceSets {
- main {
- java {
- srcDirs = ["src/main/java"]
- }
- resources {
- srcDirs = ["src/main/resources"]
- }
- }
-}
\ No newline at end of file
diff --git a/src/test/resources/test-applications/call-graph-test/gradle/wrapper/gradle-wrapper.jar b/src/test/resources/test-applications/call-graph-test/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index a4b76b95..00000000
Binary files a/src/test/resources/test-applications/call-graph-test/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/src/test/resources/test-applications/call-graph-test/gradle/wrapper/gradle-wrapper.properties b/src/test/resources/test-applications/call-graph-test/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index e18bc253..00000000
--- a/src/test/resources/test-applications/call-graph-test/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
-networkTimeout=10000
-validateDistributionUrl=true
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
diff --git a/src/test/resources/test-applications/call-graph-test/gradlew b/src/test/resources/test-applications/call-graph-test/gradlew
deleted file mode 100755
index f3b75f3b..00000000
--- a/src/test/resources/test-applications/call-graph-test/gradlew
+++ /dev/null
@@ -1,251 +0,0 @@
-#!/bin/sh
-
-#
-# Copyright © 2015-2021 the original authors.
-#
-# Licensed 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
-#
-# https://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.
-#
-# SPDX-License-Identifier: Apache-2.0
-#
-
-##############################################################################
-#
-# Gradle start up script for POSIX generated by Gradle.
-#
-# Important for running:
-#
-# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
-# noncompliant, but you have some other compliant shell such as ksh or
-# bash, then to run this script, type that shell name before the whole
-# command line, like:
-#
-# ksh Gradle
-#
-# Busybox and similar reduced shells will NOT work, because this script
-# requires all of these POSIX shell features:
-# * functions;
-# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
-# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
-# * compound commands having a testable exit status, especially «case»;
-# * various built-in commands including «command», «set», and «ulimit».
-#
-# Important for patching:
-#
-# (2) This script targets any POSIX shell, so it avoids extensions provided
-# by Bash, Ksh, etc; in particular arrays are avoided.
-#
-# The "traditional" practice of packing multiple parameters into a
-# space-separated string is a well documented source of bugs and security
-# problems, so this is (mostly) avoided, by progressively accumulating
-# options in "$@", and eventually passing that to Java.
-#
-# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
-# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
-# see the in-line comments for details.
-#
-# There are tweaks for specific operating systems such as AIX, CygWin,
-# Darwin, MinGW, and NonStop.
-#
-# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
-# within the Gradle project.
-#
-# You can find Gradle at https://github.com/gradle/gradle/.
-#
-##############################################################################
-
-# Attempt to set APP_HOME
-
-# Resolve links: $0 may be a link
-app_path=$0
-
-# Need this for daisy-chained symlinks.
-while
- APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
- [ -h "$app_path" ]
-do
- ls=$( ls -ld "$app_path" )
- link=${ls#*' -> '}
- case $link in #(
- /*) app_path=$link ;; #(
- *) app_path=$APP_HOME$link ;;
- esac
-done
-
-# This is normally unused
-# shellcheck disable=SC2034
-APP_BASE_NAME=${0##*/}
-# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
-APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD=maximum
-
-warn () {
- echo "$*"
-} >&2
-
-die () {
- echo
- echo "$*"
- echo
- exit 1
-} >&2
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "$( uname )" in #(
- CYGWIN* ) cygwin=true ;; #(
- Darwin* ) darwin=true ;; #(
- MSYS* | MINGW* ) msys=true ;; #(
- NONSTOP* ) nonstop=true ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD=$JAVA_HOME/jre/sh/java
- else
- JAVACMD=$JAVA_HOME/bin/java
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD=java
- if ! command -v java >/dev/null 2>&1
- then
- die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-fi
-
-# Increase the maximum file descriptors if we can.
-if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
- case $MAX_FD in #(
- max*)
- # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC2039,SC3045
- MAX_FD=$( ulimit -H -n ) ||
- warn "Could not query maximum file descriptor limit"
- esac
- case $MAX_FD in #(
- '' | soft) :;; #(
- *)
- # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC2039,SC3045
- ulimit -n "$MAX_FD" ||
- warn "Could not set maximum file descriptor limit to $MAX_FD"
- esac
-fi
-
-# Collect all arguments for the java command, stacking in reverse order:
-# * args from the command line
-# * the main class name
-# * -classpath
-# * -D...appname settings
-# * --module-path (only if needed)
-# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
-
-# For Cygwin or MSYS, switch paths to Windows format before running java
-if "$cygwin" || "$msys" ; then
- APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
- CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
-
- JAVACMD=$( cygpath --unix "$JAVACMD" )
-
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- for arg do
- if
- case $arg in #(
- -*) false ;; # don't mess with options #(
- /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
- [ -e "$t" ] ;; #(
- *) false ;;
- esac
- then
- arg=$( cygpath --path --ignore --mixed "$arg" )
- fi
- # Roll the args list around exactly as many times as the number of
- # args, so each arg winds up back in the position where it started, but
- # possibly modified.
- #
- # NB: a `for` loop captures its iteration list before it begins, so
- # changing the positional parameters here affects neither the number of
- # iterations, nor the values presented in `arg`.
- shift # remove old arg
- set -- "$@" "$arg" # push replacement arg
- done
-fi
-
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-
-# Collect all arguments for the java command:
-# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
-# and any embedded shellness will be escaped.
-# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
-# treated as '${Hostname}' itself on the command line.
-
-set -- \
- "-Dorg.gradle.appname=$APP_BASE_NAME" \
- -classpath "$CLASSPATH" \
- org.gradle.wrapper.GradleWrapperMain \
- "$@"
-
-# Stop when "xargs" is not available.
-if ! command -v xargs >/dev/null 2>&1
-then
- die "xargs is not available"
-fi
-
-# Use "xargs" to parse quoted args.
-#
-# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
-#
-# In Bash we could simply go:
-#
-# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
-# set -- "${ARGS[@]}" "$@"
-#
-# but POSIX shell has neither arrays nor command substitution, so instead we
-# post-process each arg (as a line of input to sed) to backslash-escape any
-# character that might be a shell metacharacter, then use eval to reverse
-# that process (while maintaining the separation between arguments), and wrap
-# the whole thing up as a single "set" statement.
-#
-# This will of course break if any of these variables contains a newline or
-# an unmatched quote.
-#
-
-eval "set -- $(
- printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
- xargs -n1 |
- sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
- tr '\n' ' '
- )" '"$@"'
-
-exec "$JAVACMD" "$@"
diff --git a/src/test/resources/test-applications/call-graph-test/gradlew.bat b/src/test/resources/test-applications/call-graph-test/gradlew.bat
deleted file mode 100644
index 9d21a218..00000000
--- a/src/test/resources/test-applications/call-graph-test/gradlew.bat
+++ /dev/null
@@ -1,94 +0,0 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-@rem SPDX-License-Identifier: Apache-2.0
-@rem
-
-@if "%DEBUG%"=="" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%"=="" set DIRNAME=.
-@rem This is normally unused
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if %ERRORLEVEL% equ 0 goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-goto fail
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if %ERRORLEVEL% equ 0 goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-set EXIT_CODE=%ERRORLEVEL%
-if %EXIT_CODE% equ 0 set EXIT_CODE=1
-if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
-exit /b %EXIT_CODE%
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/src/test/resources/test-applications/call-graph-test/settings.gradle b/src/test/resources/test-applications/call-graph-test/settings.gradle
deleted file mode 100644
index 8a57a6e2..00000000
--- a/src/test/resources/test-applications/call-graph-test/settings.gradle
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * This file was generated by the Gradle 'init' task.
- *
- * The settings file is used to specify which projects to include in your build.
- *
- * Detailed information about configuring a multi-project build in Gradle can be found
- * in the user manual at https://docs.gradle.org/7.6.4/userguide/multi_project_builds.html
- * This project uses @Incubating APIs which are subject to change.
- */
-
-rootProject.name = 'call-graph-test'
diff --git a/src/test/resources/test-applications/call-graph-test/src/main/java/org/example/User.java b/src/test/resources/test-applications/call-graph-test/src/main/java/org/example/User.java
deleted file mode 100644
index ff1e1a6b..00000000
--- a/src/test/resources/test-applications/call-graph-test/src/main/java/org/example/User.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.example;
-
-public class User {
-
- private String name;
- public User(String name)
- {
- this.name = name;
- }
-
- private void loglog()
- {
- this.name += " (logged in x2)";
- }
-
- private void log()
- {
- this.name += " (logged in)";
- loglog();
- }
-
- public String getName()
- {
- return this.name;
- }
-
- String helloString()
- {
- log();
- return "Hello, " + this.getName();
- }
-}
diff --git a/src/test/resources/test-applications/daytrader8/.gitignore b/src/test/resources/test-applications/daytrader8/.gitignore
deleted file mode 100644
index cd2aeaff..00000000
--- a/src/test/resources/test-applications/daytrader8/.gitignore
+++ /dev/null
@@ -1,11 +0,0 @@
-/.apt_generated/
-/target/
-/build/
-/bin/
-.classpath
-.project
-/.settings/
-/wlp/
-/openliberty/
-.factorypath
-.DS_Store
\ No newline at end of file
diff --git a/src/test/resources/test-applications/daytrader8/Dockerfile b/src/test/resources/test-applications/daytrader8/Dockerfile
deleted file mode 100644
index bce5ad82..00000000
--- a/src/test/resources/test-applications/daytrader8/Dockerfile
+++ /dev/null
@@ -1,14 +0,0 @@
-FROM open-liberty:full
-
-COPY --chown=1001:0 src/main/liberty/config/server.xml /config/server.xml
-COPY --chown=1001:0 src/main/liberty/config/bootstrap.properties /config/bootstrap.properties
-COPY --chown=1001:0 target/io.openliberty.sample.daytrader8.war /config/apps/
-
-#Derby
-COPY --chown=1001:0 target/liberty/wlp/usr/shared/resources/DerbyLibs/derby-10.14.2.0.jar /opt/ol/wlp/usr/shared/resources/DerbyLibs/derby-10.14.2.0.jar
-COPY --chown=1001:0 target/liberty/wlp/usr/shared/resources/data /opt/ol/wlp/usr/shared/resources/data
-
-ENV MAX_USERS=1000
-ENV MAX_QUOTES=500
-
-#RUN configure.sh
diff --git a/src/test/resources/test-applications/daytrader8/Dockerfile-db2 b/src/test/resources/test-applications/daytrader8/Dockerfile-db2
deleted file mode 100644
index c32a54e8..00000000
--- a/src/test/resources/test-applications/daytrader8/Dockerfile-db2
+++ /dev/null
@@ -1,21 +0,0 @@
-# Create folder db2jars/ and copy db2jcc4.jar and db2jcc_license_cu.jar to it.
-# Set Env below
-
-FROM open-liberty:full
-
-COPY --chown=1001:0 src/main/liberty/config/server.xml_db2 /config/server.xml
-COPY --chown=1001:0 src/main/liberty/config/bootstrap.properties /config/bootstrap.properties
-COPY --chown=1001:0 target/io.openliberty.sample.daytrader8.war /config/apps/
-
-# DB2 JARS
-COPY --chown=1001:0 /db2jars /opt/ol/wlp/usr/shared/resources/db2jars
-
-ENV contextRoot=daytrader
-ENV dbUser=
-ENV dbPass=
-ENV tradeDbHost=
-ENV tradeDbPort=
-ENV tradeDbName=
-
-
-#RUN configure.sh
diff --git a/src/test/resources/test-applications/daytrader8/LICENSE b/src/test/resources/test-applications/daytrader8/LICENSE
deleted file mode 100644
index 8f71f43f..00000000
--- a/src/test/resources/test-applications/daytrader8/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "{}"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright {yyyy} {name of copyright owner}
-
- Licensed 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.
-
diff --git a/src/test/resources/test-applications/daytrader8/README.md b/src/test/resources/test-applications/daytrader8/README.md
deleted file mode 100644
index 595a96da..00000000
--- a/src/test/resources/test-applications/daytrader8/README.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# Java EE8: DayTrader8 Sample
-
-This sample contains the DayTrader 8 benchmark, which is an application built around the paradigm of an online stock trading system. The application allows users to login, view their portfolio, lookup stock quotes, and buy or sell stock shares. With the aid of a Web-based load driver such as Apache JMeter, the real-world workload provided by DayTrader can be used to measure and compare the performance of Java Platform, Enterprise Edition (Java EE) application servers offered by a variety of vendors. In addition to the full workload, the application also contains a set of primitives used for functional and performance testing of various Java EE components and common design patterns.
-
-DayTrader is an end-to-end benchmark and performance sample application. It provides a real world Java EE workload. DayTrader's new design spans Java EE 8.
-
-This sample can be installed onto Liberty runtime versions 18.0.0.2 and later. A prebuilt derby database is provided in resources/data
-
-
-To run this sample, first [download](https://github.com/OpenLiberty/sample.daytrader8/archive/master.zip) or clone this repo - to clone:
-```
-git clone git@github.com:OpenLiberty/sample.daytrader8.git
-```
-
-From inside the sample.daytrader8 directory, build and start the application in Open Liberty with the following command:
-```
-mvn clean package liberty:run
-```
-
-The server will listen on port 9080 by default. You can change the port (for example, to port 9081) by adding `mvn clean package liberty:run -DtestServerHttpPort=9081` to the end of the Maven command.
-
-Once the server is started, you should be able to access the application at:
-http://localhost:9080/daytrader
-
-
-
-## Notice
-
-© Copyright IBM Corporation 2019.
-
-## License
-
-```text
-Licensed 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.
-````
diff --git a/src/test/resources/test-applications/daytrader8/README_LOAD_TEST.md b/src/test/resources/test-applications/daytrader8/README_LOAD_TEST.md
deleted file mode 100644
index 23d468e1..00000000
--- a/src/test/resources/test-applications/daytrader8/README_LOAD_TEST.md
+++ /dev/null
@@ -1,151 +0,0 @@
-# Daytrader8: Load Testing
-This readme explains how to setup DB2 and load test the Daytrader 8 application with Open Liberty.
-
-## Prerequisites
-
-1. Open Liberty Machine (Server with Open Liberty unzipped at , and a default profile created)
-2. DB2 Machine (Server running DB2)
-3. Driver Machine (Server running JMeter with jmeter files copied to /bin, and the WebSocket plugin copied to /lib/ext, see jmeter_files)
-
-## Setup Open Liberty
-
-Build Daytrader8;
-[Download](https://github.com/OpenLiberty/sample.daytrader8/archive/master.zip) or clone this repo - to clone:
-```
-git clone git@github.com:OpenLiberty/sample.daytrader8.git
-```
-
-From inside the sample.daytrader8 directory, build the application:
-```
-mvn clean package
-```
-* Copy target/io.openliberty.sample.daytrader8.war to /usr/servers/defaultServer/apps
-* Copy src/main/liberty/config/server.xml_db2 to /usr/servers/defaultServer/server.xml (overwrite)
-* Copy db2 jars from the DB2 Machine to /usr/shared/db2jars
-```
-db2jcc4.jar
-db2jcc_license_cu.jar
-```
-
-Create /usr/servers/defaultServer/jvm.options and add any JVM Arguments desired.
-```
--Xms1024m
--Xmx1024m
-```
-
-Set these environment variables. matching your environment (or hard code them in the server.xml):
-```
-contextRoot=daytrader
-dbUser
-dbPass
-tradeDbHost
-tradeDbPort
-tradeDbName
-```
-
-## Set up DB2
-Sign in to DB2 machine as db2 user and create tradedb database
-```
-db2 create db tradedb
-```
-
-Note: When creating the database, DB2_APM_PERFORMANCE needs to be off, or you'll get: SQL1803N The requested operation cannot be executed in "No Package Lock"
-```
-db2set DB2_APM_PERFORMANCE=
-db2stop
-db2start
-```
-
-## Load Database
-
-Start OpenLiberty:
-```
-/bin/server start --clean
-```
-
-With a web browser go to http://openliberty-hostname:9080/daytrader/configure.html
-```
-Click (Re)-create DayTrader Database Tables and Indexes
-Click (Re)-populate DayTrader Database
-```
-
-Stop Liberty Server
-```
-/bin/server stop
-```
-
-On the DB2 Server, Put the following into a script (backupTradeDB.sh) and run as the db2 user
-```
-DB=tradedb
-mkdir -p ~/backups/${DB}
-db2 update dbm cfg using notifylevel 0
-db2 update dbm cfg using diaglevel 1
-db2 update dbm cfg using NUM_POOLAGENTS 500 automatic MAX_COORDAGENTS 500 automatic MAX_CONNECTIONS 500 automatic
-
-db2 -v update db cfg for ${DB} using MAXLOCKS 100 LOCKLIST 100000
-
-db2 connect to ${DB}
-db2 update db cfg for ${DB} using maxappls 500 automatic
-db2 update db cfg for ${DB} using logfilsiz 8000
-db2 update db cfg for ${DB} using logprimary 32
-db2 update db cfg for ${DB} using dft_queryopt 0
-
-db2 update db cfg for ${DB} using softmax 3000
-db2 update db cfg for ${DB} using chngpgs_thresh 99
-
-db2 -v alter bufferpool IBMDEFAULTBP size -1
-db2 -v connect reset
-db2 -v update db cfg for ${DB} using BUFFPAGE 262144
-
-db2set DB2_APM_PERFORMANCE=ON
-db2set DB2_KEEPTABLELOCK=CONNECTION
-db2set DB2_USE_ALTERNATE_PAGE_CLEANING=ON
-db2set DB2_MINIMIZE_LISTPREFETCH=YES
-db2set DB2_LOGGER_NON_BUFFERED_IO=OFF
-
-db2 connect reset
-db2 terminate
-
-db2stop force
-db2start
-
-db2 connect to ${DB}
-db2 reorgchk update statistics
-db2 connect reset
-
-db2 terminate
-db2 backup db tradedb to ~/backups/${DB}
-```
-
-Put the following into a script (restoreTradeDB.sh) to run before each server restart. (To make sure the database is in the same state every time)
-```
-db2stop force
-db2start
-db2 restore db tradedb from ~/backups/tradedb replace existing
-```
-
-Note: If disk writing/reading becomes a bottleneck, you may need to create a ramdisk and restore the database to the ramdisk.
-
-## Apply Load
-On the DB2 Server, restore the database (as db2 user)
-```
-restoreTradeDB.sh
-```
-
-Start Liberty
-```
-/bin/server start
-```
-
-On the JMETER Server, Start JMeter:
-```
-cd /bin
-./jmeter -n -t daytrader8.jmx -JHOST=openliberty-hostname -JDURATION=180
-```
-
-Preferably, do three 180 second warm up runs and then three 180 second measurement runs with a 30 second break in between each.
-
-Also, a best practice is to reset the database between each run, which can be done on the configuration tab of the application.
-```
-http://openliberty-hostname:9080/daytrader/config?action=resetTrade
-```
diff --git a/src/test/resources/test-applications/daytrader8/jmeter_files/README.txt b/src/test/resources/test-applications/daytrader8/jmeter_files/README.txt
deleted file mode 100644
index 50566ce0..00000000
--- a/src/test/resources/test-applications/daytrader8/jmeter_files/README.txt
+++ /dev/null
@@ -1,54 +0,0 @@
-# (C) Copyright IBM Corporation 2019, 2021.
- #
- # Licensed 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.
-
-
-daytrader8.jmx is an Apache JMeter script that may be used for running the DayTrader8 benchmark.
-
-Jmeter version 3.3 or later is highly recommended.
-To use the script, you will need to put the the WebSocket Sampler (and dependencies) from WebSocket Samplers by Peter Doornbosch into lib/ext.
-Use the Jmeter plugin manager or download via https://bitbucket.org/pjtr/jmeter-websocket-samplers.
-
-
-The script has the following options:
- -JHOST The name of the machine running the DayTrader Application. The default is localhost.
- -JPORT The HTTP port of the server running the DayTrader Application. The default is 9080.
- -JPROTOCOL The transport either http or https
- -JTHREADS The number of jmeter threads to start. The default is 50.
- -JRAMP The ramp up time for starting the threads. Set this to the same value as -JTHREADS for a smoother startup. The default is 0.
- -JDURATION The time (in seconds) to run jmeter.
- -JMAXTHINKTIME The time in milliseconds to wait between each call. The default is 0 ms
- -JSTOCKS The total amount of stocks/quotes in the database, minus one. The default is 9999, which assumes there are 10,000 stocks in the database.
- -JBOTUID The lowest user id. The default is 0.
- -JTOPUID The highest user id. The default is 14999, which assumes there are 15,000 users in the database.
-
-Example: ./jmeter -n -t daytrader8.jmx -JHOST=myserver -JPORT=9080 -JPROTOCOL=http -JMAXTHINKTIME=100 -JDURATION=300
-
-To see output every five seconds from JMeter, edit the following section in /bin/jmeter.properties
-
-#---------------------------------------------------------------------------
-# Summariser - Generate Summary Results - configuration (mainly applies to non-GUI mode)
-#---------------------------------------------------------------------------
-#
-# Define the following property to automatically start a summariser with that name
-# (applies to non-GUI mode only)
-summariser.name=summary
-#
-# interval between summaries (in seconds) default 30 seconds
-summariser.interval=5
-#
-# Write messages to log file
-summariser.log=true
-#
-# Write messages to System.out
-summariser.out=true
diff --git a/src/test/resources/test-applications/daytrader8/jmeter_files/daytrader8.jmx b/src/test/resources/test-applications/daytrader8/jmeter_files/daytrader8.jmx
deleted file mode 100644
index cfe98684..00000000
--- a/src/test/resources/test-applications/daytrader8/jmeter_files/daytrader8.jmx
+++ /dev/null
@@ -1,2607 +0,0 @@
-
-
-
-
-
- false
- false
-
-
-
-
-
-
-
-
- false
- -1
-
- ${__P(THREADS,50)}
- ${__P(RAMP,0)}
- 1355173676000
- 1355173676000
- true
- continue
- ${__P(DURATION, 180)}
-
- true
-
-
-
-
- false
- false
- rfc2109
-
-
-
-
-
- User-Agent
- Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322)
-
-
- Accept
- image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
-
-
- Accept-Language
- en-us
-
-
-
-
-
-
-
- minimumuid
- ${__P(BOTUID,0)}
- =
-
-
- maximumuid
- ${__P(TOPUID,14999)}
- =
-
-
- hostname
- ${__P(HOST,localhost)}
- =
-
-
- port
- ${__P(PORT,9080)}
- =
-
-
- maxthinkingtime
- ${__P(MAXTHINKTIME,0)}
- =
-
-
- maximumsid
- ${__P(STOCKS,9999)}
- =
-
-
- protocol
- ${__P(PROTOCOL,http)}
- =
- http | https
-
-
-
-
-
- ${minimumuid}
- ${maximumuid}
- 1
- logincounter
-
- false
-
-
-
- 1
-
-
-
- 1
- true
- 50
-
- ThroughputController.percentThroughput
- 10.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/welcome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- xxx
- =
- true
- login:password
-
-
- false
- Log in
- =
- true
- login:submit
-
-
- false
- uid:${logincounter}
- =
- true
- login:uid
-
-
- false
- 1
- =
- true
- login_SUBMIT
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/welcome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- loop
-
-
-
- true
-
-
- true
-
-
-
-
- Ready to Trade
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- ${loop}
-
-
-
- 1
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 36.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- s:${__Random(0,${maximumsid},)}
- =
- true
- quotes:symbols
-
-
- false
- quotes
- =
- true
- quotes:submit2
-
-
- false
- 1
- =
- true
- quotes_SUBMIT
-
-
- false
- 100
- =
- true
- quotes:quotes:0:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:1:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:2:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:3:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:4:quantity
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- DayTrader Quotes
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 16.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 15.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/portfolio.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 10.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/account.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 8.0
- 0.0
-
-
-
-
- ${__jexl3("${protocol}"== "http",)}
- false
- true
-
-
-
- true
- false
- ${hostname}
- ${port}
- /daytrader/marketsummary
- false
- {"action":"updateMarketSummary"}
- 20000
- open and close
- 20000
- false
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- ${__jexl3("${protocol}"== "https",)}
- false
- true
-
-
-
- true
- true
- ${hostname}
- ${port}
- /daytrader/marketsummary
- false
- {"action":"updateMarketSummary"}
- 20000
- open and close
- 20000
- false
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- s:${__Random(0,${maximumsid},)}
- =
- true
- quotes:symbols
-
-
- false
- quotes
- =
- true
- quotes:submit2
-
-
- false
- 1
- =
- true
- quotes_SUBMIT
-
-
- false
- 100
- =
- true
- quotes:quotes:0:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:1:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:2:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:3:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:4:quantity
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- DayTrader Quotes
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
- false
- tobuy
- s:([0-9]+)
- $1$
- 0
- 1
- all
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- s:${tobuy}
- =
- true
- quotes:symbols
-
-
- false
- ${__Random(1,200)}
- =
- true
- quotes:quotes:0:quantity
-
-
- false
- buy
- =
- true
- quotes:quotes:0:buy
-
-
- false
- 1
- =
- true
- quotes_SUBMIT
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- been submitted
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/portfolio.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- false
- numHoldings
- of Holdings: </b>([1-9][0-9]*)</td>
- $1$
- 0
- 0
- all
-
-
-
- ${maxthinkingtime}
-
-
-
-
- ${__jexl("${numHoldings}" != "0")}
- false
- true
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- portfolio:holdings:0:sell
- =
- true
- portfolio:_idcl
-
-
- false
- 1
- =
- true
- portfolio_SUBMIT
-
-
- false
- s:0,s:1,s:2,s:3,s:4
- =
- true
- portfolio:symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/portfolio.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- been submitted
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 2.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/account.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- uid:${logincounter}
- =
- true
- updateProfile:uid
-
-
- false
- rnd${__threadNum}${logincounter}
- =
- true
- updateProfile:fullname
-
-
- false
- xxx
- =
- true
- updateProfile:password
-
-
- false
- rndAddress
- =
- true
- updateProfile:address
-
-
- false
- xxx
- =
- true
- updateProfile:cpassword
-
-
- false
- rndCC
- =
- true
- updateProfile:ccn
-
-
- false
- rndEmail@email.com
- =
- true
- updateProfile:email
-
-
- false
- 1
- =
- true
- updateProfile_SUBMIT
-
-
- false
- Update Profile
- =
- true
- updateProfile:submit
-
-
- false
- s:0,s:1,s:2,s:3,s:4
- =
- true
- updateProfile:symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/account.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 1.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- tradeHome:logoff
- =
- true
- tradeHome:_idcl
-
-
- false
- s:0,s:1,s:2,s:3,s:4
- =
- true
- tradeHome:symbols
-
-
- false
- 1
- =
- true
- tradeHome_SUBMIT
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- DayTrader Login
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/register.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- first:${__Random(0,999,)} last:${__Random(0,4999,)}
- =
- true
- register:fullname
-
-
- false
- first:${__Random(0,999,)} last:${__Random(0,4999,)}
- =
- true
- register:address
-
-
- false
- uid${logincounter}@${__Random(0,100,)}.com
- =
- true
- register:email
-
-
- false
- ru:${logincounter}${__threadNum}:${__time(HMS)}${__Random(0,999,)}
- =
- true
- register:uid
-
-
- false
- yyy
- =
- true
- register:password
-
-
- false
- yyy
- =
- true
- register:cpassword
-
-
- false
- 1000000
- =
- true
- register:money
-
-
- false
- 123-fake-ccnum-456
- =
- true
- register:ccn
-
-
- false
- 1
- =
- true
- register_SUBMIT
-
-
- false
- Register
- =
- true
- register:submit
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/register.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- Registration operation succeeded
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- tradeHome:logoff
- =
- true
- tradeHome:_idcl
-
-
- false
- s:0,s:1,s:2,s:3,s:4
- =
- true
- tradeHome:symbols
-
-
- false
- 1
- =
- true
- tradeHome_SUBMIT
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- DayTrader Login
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
- loop
-
-
-
- false
-
-
- false
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- tradeHome:logoff
- =
- true
- tradeHome:_idcl
-
-
- false
- s:0,s:1,s:2,s:3,s:4
- =
- true
- tradeHome:symbols
-
-
- false
- 1
- =
- true
- tradeHome_SUBMIT
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- DayTrader Login
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
- loop
-
-
-
- false
-
-
- false
-
-
-
-
-
-
- false
- jsfViewState
- <input type="hidden" name="javax\.faces\.ViewState" id="j_id__v_0:javax\.faces\.ViewState:1" value="([^"]+)".*/>
- $1$
-
- 0
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 70.0
- 0.0
-
-
-
-
-
-
-
- true
- uid:${logincounter}
- =
- true
- uid
-
-
- true
- xxx
- =
- true
- passwd
-
-
- true
- login
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- loop
-
-
-
- true
-
-
- true
-
-
-
-
- Welcome to DayTrader
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- ${__jexl3("${protocol}"== "http",)}
- false
- true
-
-
-
- false
- ${hostname}
- ${port}
- /daytrader/marketsummary
- 20000
- 20000
-
-
-
-
- ${__jexl3("${protocol}"== "https",)}
- false
- true
-
-
-
- true
- ${hostname}
- ${port}
- /daytrader/marketsummary
- 20000
- 20000
-
-
-
-
- ${loop}
-
-
-
- 1
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 36.0
- 0.0
-
-
-
-
-
-
-
- true
- quotes
- =
- true
- action
-
-
- true
- s:${__Random(0,${maximumsid},)}
- =
- true
- symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- DayTrader: Quotes and Trading
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 16.0
- 0.0
-
-
-
-
-
-
-
- true
- home
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 15.0
- 0.0
-
-
-
-
-
-
-
- true
- portfolio
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 10.0
- 0.0
-
-
-
-
-
-
-
- true
- account
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 8.0
- 0.0
-
-
-
-
- false
- false
- ${hostname}
- ${port}
- /daytrader/marketsummary
- false
- {"action":"updateMarketSummary"}
- 20000
- use existing open connection
- 20000
- false
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- true
- quotes
- =
- true
- action
-
-
- true
- s:${__Random(0,${maximumsid},tobuy)}
- =
- true
- symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- buy
- =
- true
- action
-
-
- true
- s:${tobuy}
- =
- true
- symbol
-
-
- true
- ${__Random(1,200)}
- =
- true
- quantity
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- has been submitted
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- true
- portfolio
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- false
- firstHoldingID
- holdingID=([0-9]+)
- $1$
- NotFound
- 0
- all
-
-
-
- false
- firstHoldingIDBool
- holdingID=([0-9]+)
- true
- false
- 1
- all
-
-
-
- ${maxthinkingtime}
-
-
-
-
- ${firstHoldingIDBool}
- false
- true
-
-
-
-
-
-
- true
- sell
- =
- true
- action
-
-
- true
- ${firstHoldingID}
- =
- true
- holdingID
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- has been submitted
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 2.0
- 0.0
-
-
-
-
-
-
-
- true
- account
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- update_profile
- =
- true
- action
-
-
- true
- uid:${logincounter}
- =
- true
- userID
-
-
- true
- rnd${__threadNum}${logincounter}
- =
- true
- fullname
-
-
- true
- xxx
- =
- true
- password
-
-
- true
- rndAddress
- =
- true
- address
-
-
- true
- xxx
- =
- true
- cpassword
-
-
- true
- rndCC
- =
- true
- creditcard
-
-
- true
- rndEmail@email.com
- =
- true
- email
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 1.0
- 0.0
-
-
-
-
-
-
-
- true
- logout
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/register.jsp
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- register
- =
- true
- action
-
-
- true
- first:${__Random(0,999,)} last:${__Random(0,4999,)}
- =
- true
- Full Name
-
-
- true
- first:${__Random(0,999,)} last:${__Random(0,4999,)}
- =
- true
- snail mail
-
-
- true
- uid${logincounter}@${__Random(0,100,)}.com
- =
- true
- email
-
-
- true
- ru:${logincounter}${__threadNum}:${__time(HMS)}${__Random(0,999,)}
- =
- true
- user id
-
-
- true
- yyy
- =
- true
- passwd
-
-
- true
- yyy
- =
- true
- confirm passwd
-
-
- true
- 1000000
- =
- true
- money
-
-
- true
- 123-fake-ccnum-456
- =
- true
- Credit Card Number
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- logout
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- loop
-
-
-
- false
-
-
- false
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- true
- logout
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- DayTrader Login
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
- loop
-
-
-
- false
-
-
- false
-
-
-
-
-
-
- 4000
- 6000
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 20.0
- 0.0
-
-
-
-
-
-
-
- true
- s:${__Random(0,${maximumsid},)}
- =
- true
- symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- daytrader/rest/quotes
- POST
- false
- true
- true
- false
-
-
-
-
-
-
-
- 200
-
- Assertion.response_code
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- false
-
- saveConfig
-
- true
- true
- true
- true
- true
- true
- true
- true
- true
- false
- true
- true
- false
- false
- true
- false
- false
- false
- false
- false
- 0
- true
- true
- true
- true
-
-
- daytrader8.aggregateReport.csv
-
-
-
- true
-
- saveConfig
-
- true
- true
- true
- true
- true
- true
- true
- true
- true
- false
- true
- true
- false
- false
- false
- false
- false
- false
- false
- false
- 0
- true
- true
- true
- true
-
-
- daytrader8.resultsTree.csv
-
-
-
- false
-
- saveConfig
-
- true
- true
- true
- true
- true
- true
- true
- true
- true
- false
- true
- true
- false
- false
- true
- false
- false
- false
- false
- false
- 0
- true
- true
- true
- true
-
-
- daytrader8.resultsTable.csv
-
-
-
-
-
diff --git a/src/test/resources/test-applications/daytrader8/jmeter_files/daytrader8_mojarra.jmx b/src/test/resources/test-applications/daytrader8/jmeter_files/daytrader8_mojarra.jmx
deleted file mode 100755
index bcb3d4fa..00000000
--- a/src/test/resources/test-applications/daytrader8/jmeter_files/daytrader8_mojarra.jmx
+++ /dev/null
@@ -1,2600 +0,0 @@
-
-
-
-
-
- false
- false
-
-
-
-
-
-
-
-
- false
- -1
-
- ${__P(THREADS,50)}
- ${__P(RAMP,0)}
- 1355173676000
- 1355173676000
- true
- continue
- ${__P(DURATION, 180)}
-
- true
-
-
-
-
- false
- false
- rfc2109
-
-
-
-
-
- User-Agent
- Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322)
-
-
- Accept
- image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
-
-
- Accept-Language
- en-us
-
-
-
-
-
-
-
- minimumuid
- ${__P(BOTUID,0)}
- =
-
-
- maximumuid
- ${__P(TOPUID,14999)}
- =
-
-
- hostname
- ${__P(HOST,localhost)}
- =
-
-
- port
- ${__P(PORT,9080)}
- =
-
-
- maxthinkingtime
- ${__P(MAXTHINKTIME,0)}
- =
-
-
- maximumsid
- ${__P(STOCKS,9999)}
- =
-
-
- protocol
- ${__P(PROTOCOL,http)}
- =
- http | https
-
-
-
-
-
- ${minimumuid}
- ${maximumuid}
- 1
- logincounter
-
- false
-
-
-
- 1
-
-
-
- 1
- true
- 50
-
- ThroughputController.percentThroughput
- 10.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/welcome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- false
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- xxx
- =
- true
- login:password
-
-
- false
- Log in
- =
- true
- login:submit
-
-
- false
- uid:${logincounter}
- =
- true
- login:uid
-
-
- false
- login
- =
- true
- login
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/welcome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- loop
-
-
-
- true
-
-
- true
-
-
-
-
- Ready to Trade
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- ${loop}
-
-
-
- 1
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 36.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- s:${__Random(0,${maximumsid},)}
- =
- true
- quotes:symbols
-
-
- false
- quotes
- =
- true
- quotes:submit
-
-
- false
- quotes
- =
- true
- quotes
-
-
- false
- 100
- =
- true
- quotes:quotes:0:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:1:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:2:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:3:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:4:quantity
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- DayTrader Quotes
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 16.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 15.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/portfolio.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 10.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/account.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 8.0
- 0.0
-
-
-
-
- ${__jexl3("${protocol}"== "http",)}
- false
- true
-
-
-
- true
- false
- ${hostname}
- ${port}
- /daytrader/marketsummary
- false
- {"action":"updateMarketSummary"}
- 20000
- open and close
- 20000
- false
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- ${__jexl3("${protocol}"== "https",)}
- false
- true
-
-
-
- true
- true
- ${hostname}
- ${port}
- /daytrader/marketsummary
- false
- {"action":"updateMarketSummary"}
- 20000
- open and close
- 20000
- false
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- s:${__Random(0,${maximumsid},)}
- =
- true
- quotes:symbols
-
-
- false
- quotes
- =
- true
- quotes:submit
-
-
- false
- quotes
- =
- true
- quotes
-
-
- false
- 100
- =
- true
- quotes:quotes:0:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:1:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:2:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:3:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:4:quantity
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- DayTrader Quotes
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
- false
- tobuy
- s:([0-9]+)
- $1$
- 0
- 1
- all
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- s:${tobuy}
- =
- true
- quotes:symbols
-
-
- false
- ${__Random(1,200)}
- =
- true
- quotes:quotes:0:quantity
-
-
- false
- buy
- =
- true
- quotes:quotes:0:buy
-
-
- false
- quotes
- =
- true
- quotes
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- been submitted
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/portfolio.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- false
- numHoldings
- of Holdings: </b>([1-9][0-9]*)</td>
- $1$
- 0
- 0
- all
-
-
-
- ${maxthinkingtime}
-
-
-
-
- ${__jexl("${numHoldings}" != "0")}
- false
- true
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- portfolio:holdings:0:sell
- =
- true
- portfolio:holdings:0:sell
-
-
- false
- portfolio
- =
- true
- portfolio
-
-
- false
- s:0,s:1,s:2,s:3,s:4
- =
- true
- portfolio:symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/portfolio.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- been submitted
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 2.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/account.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- uid:${logincounter}
- =
- true
- updateProfile:uid
-
-
- false
- rnd${__threadNum}${logincounter}
- =
- true
- updateProfile:fullname
-
-
- false
- xxx
- =
- true
- updateProfile:password
-
-
- false
- rndAddress
- =
- true
- updateProfile:address
-
-
- false
- xxx
- =
- true
- updateProfile:cpassword
-
-
- false
- rndCC
- =
- true
- updateProfile:ccn
-
-
- false
- rndEmail@email.com
- =
- true
- updateProfile:email
-
-
- false
- updateProfile
- =
- true
- updateProfile
-
-
- false
- Update Profile
- =
- true
- updateProfile:submit
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/account.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 1.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- tradeHome:logoff
- =
- true
- tradeHome:logoff
-
-
- false
- s:1,s:2,s:3,s:4
- =
- true
- tradeHome:symbols
-
-
- false
- tradeHome
- =
- true
- tradeHome
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- DayTrader Login
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/register.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- first:${__Random(0,999,)} last:${__Random(0,4999,)}
- =
- true
- register:fullname
-
-
- false
- first:${__Random(0,999,)} last:${__Random(0,4999,)}
- =
- true
- register:address
-
-
- false
- uid${logincounter}@${__Random(0,100,)}.com
- =
- true
- register:email
-
-
- false
- ru:${logincounter}${__threadNum}:${__time(HMS)}${__Random(0,999,)}
- =
- true
- register:uid
-
-
- false
- yyy
- =
- true
- register:password
-
-
- false
- yyy
- =
- true
- register:cpassword
-
-
- false
- 1000000
- =
- true
- register:money
-
-
- false
- 123-fake-ccnum-456
- =
- true
- register:ccn
-
-
- false
- register
- =
- true
- register
-
-
- false
- Register
- =
- true
- register:submit
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/register.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- Registration operation succeeded
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- tradeHome:logoff
- =
- true
- tradeHome:logoff
-
-
- false
- s:1,s:2,s:3,s:4
- =
- true
- tradeHome:symbols
-
-
- false
- tradeHome
- =
- true
- tradeHome
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- DayTrader Login
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
- loop
-
-
-
- false
-
-
- false
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- tradeHome:logoff
- =
- true
- tradeHome:logoff
-
-
- false
- s:0,s:1,s:2,s:3,s:4
- =
- true
- tradeHome:symbols
-
-
- false
- tradeHome
- =
- true
- tradeHome
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- DayTrader Login
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
- loop
-
-
-
- false
-
-
- false
-
-
-
-
-
-
- false
- jsfViewState
- <input type="hidden" name="javax\.faces\.ViewState" .* value="([^"]+)".*/>
- $1$
-
- 0
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 70.0
- 0.0
-
-
-
-
-
-
-
- true
- uid:${logincounter}
- =
- true
- uid
-
-
- true
- xxx
- =
- true
- passwd
-
-
- true
- login
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- loop
-
-
-
- true
-
-
- true
-
-
-
-
- Welcome to DayTrader
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- ${__jexl3("${protocol}"== "http",)}
- false
- true
-
-
-
- false
- ${hostname}
- ${port}
- /daytrader/marketsummary
- 20000
- 20000
-
-
-
-
- ${__jexl3("${protocol}"== "https",)}
- false
- true
-
-
-
- true
- ${hostname}
- ${port}
- /daytrader/marketsummary
- 20000
- 20000
-
-
-
-
- ${loop}
-
-
-
- 1
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 36.0
- 0.0
-
-
-
-
-
-
-
- true
- quotes
- =
- true
- action
-
-
- true
- s:${__Random(0,${maximumsid},)}
- =
- true
- symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- DayTrader: Quotes and Trading
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 16.0
- 0.0
-
-
-
-
-
-
-
- true
- home
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 15.0
- 0.0
-
-
-
-
-
-
-
- true
- portfolio
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 10.0
- 0.0
-
-
-
-
-
-
-
- true
- account
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 8.0
- 0.0
-
-
-
-
- false
- false
- ${hostname}
- ${port}
- /daytrader/marketsummary
- false
- {"action":"updateMarketSummary"}
- 20000
- use existing open connection
- 20000
- false
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- true
- quotes
- =
- true
- action
-
-
- true
- s:${__Random(0,${maximumsid},tobuy)}
- =
- true
- symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- buy
- =
- true
- action
-
-
- true
- s:${tobuy}
- =
- true
- symbol
-
-
- true
- ${__Random(1,200)}
- =
- true
- quantity
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- has been submitted
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- true
- portfolio
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- false
- firstHoldingID
- holdingID=([0-9]+)
- $1$
- NotFound
- 0
- all
-
-
-
- false
- firstHoldingIDBool
- holdingID=([0-9]+)
- true
- false
- 1
- all
-
-
-
- ${maxthinkingtime}
-
-
-
-
- ${firstHoldingIDBool}
- false
- true
-
-
-
-
-
-
- true
- sell
- =
- true
- action
-
-
- true
- ${firstHoldingID}
- =
- true
- holdingID
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- has been submitted
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 2.0
- 0.0
-
-
-
-
-
-
-
- true
- account
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- update_profile
- =
- true
- action
-
-
- true
- uid:${logincounter}
- =
- true
- userID
-
-
- true
- rnd${__threadNum}${logincounter}
- =
- true
- fullname
-
-
- true
- xxx
- =
- true
- password
-
-
- true
- rndAddress
- =
- true
- address
-
-
- true
- xxx
- =
- true
- cpassword
-
-
- true
- rndCC
- =
- true
- creditcard
-
-
- true
- rndEmail@email.com
- =
- true
- email
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 1.0
- 0.0
-
-
-
-
-
-
-
- true
- logout
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/register.jsp
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- register
- =
- true
- action
-
-
- true
- first:${__Random(0,999,)} last:${__Random(0,4999,)}
- =
- true
- Full Name
-
-
- true
- first:${__Random(0,999,)} last:${__Random(0,4999,)}
- =
- true
- snail mail
-
-
- true
- uid${logincounter}@${__Random(0,100,)}.com
- =
- true
- email
-
-
- true
- ru:${logincounter}${__threadNum}:${__time(HMS)}${__Random(0,999,)}
- =
- true
- user id
-
-
- true
- yyy
- =
- true
- passwd
-
-
- true
- yyy
- =
- true
- confirm passwd
-
-
- true
- 1000000
- =
- true
- money
-
-
- true
- 123-fake-ccnum-456
- =
- true
- Credit Card Number
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- logout
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- loop
-
-
-
- false
-
-
- false
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- true
- logout
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- DayTrader Login
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
- loop
-
-
-
- false
-
-
- false
-
-
-
-
-
-
- 4000
- 6000
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 20.0
- 0.0
-
-
-
-
-
-
-
- true
- s:${__Random(0,${maximumsid},)}
- =
- true
- symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- daytrader/rest/quotes
- POST
- false
- true
- true
- false
-
-
-
-
-
-
-
- 200
-
- Assertion.response_code
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- false
-
- saveConfig
-
- true
- true
- true
- true
- true
- true
- true
- true
- true
- false
- true
- true
- false
- false
- true
- false
- false
- false
- false
- false
- 0
- true
- true
- true
- true
-
-
- daytrader8.aggregateReport.csv
-
-
-
- true
-
- saveConfig
-
- true
- true
- true
- true
- true
- true
- true
- true
- true
- false
- true
- true
- false
- false
- false
- false
- false
- false
- false
- false
- 0
- true
- true
- true
- true
-
-
- daytrader8.resultsTree.csv
-
-
-
- false
-
- saveConfig
-
- true
- true
- true
- true
- true
- true
- true
- true
- true
- false
- true
- true
- false
- false
- true
- false
- false
- false
- false
- false
- 0
- true
- true
- true
- true
-
-
- daytrader8.resultsTable.csv
-
-
-
-
-
diff --git a/src/test/resources/test-applications/daytrader8/jmeter_files/daytrader8_mojarra_no_ws.jmx b/src/test/resources/test-applications/daytrader8/jmeter_files/daytrader8_mojarra_no_ws.jmx
deleted file mode 100644
index d294427d..00000000
--- a/src/test/resources/test-applications/daytrader8/jmeter_files/daytrader8_mojarra_no_ws.jmx
+++ /dev/null
@@ -1,2600 +0,0 @@
-
-
-
-
-
- false
- false
-
-
-
-
-
-
-
-
- false
- -1
-
- ${__P(THREADS,50)}
- ${__P(RAMP,0)}
- 1355173676000
- 1355173676000
- true
- continue
- ${__P(DURATION, 180)}
-
- true
-
-
-
-
- false
- false
- rfc2109
-
-
-
-
-
- User-Agent
- Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322)
-
-
- Accept
- image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
-
-
- Accept-Language
- en-us
-
-
-
-
-
-
-
- minimumuid
- ${__P(BOTUID,0)}
- =
-
-
- maximumuid
- ${__P(TOPUID,14999)}
- =
-
-
- hostname
- ${__P(HOST,localhost)}
- =
-
-
- port
- ${__P(PORT,9080)}
- =
-
-
- maxthinkingtime
- ${__P(MAXTHINKTIME,0)}
- =
-
-
- maximumsid
- ${__P(STOCKS,9999)}
- =
-
-
- protocol
- ${__P(PROTOCOL,http)}
- =
- http | https
-
-
-
-
-
- ${minimumuid}
- ${maximumuid}
- 1
- logincounter
-
- false
-
-
-
- 1
-
-
-
- 1
- true
- 50
-
- ThroughputController.percentThroughput
- 10.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/welcome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- false
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- xxx
- =
- true
- login:password
-
-
- false
- Log in
- =
- true
- login:submit
-
-
- false
- uid:${logincounter}
- =
- true
- login:uid
-
-
- false
- login
- =
- true
- login
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/welcome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- loop
-
-
-
- true
-
-
- true
-
-
-
-
- Ready to Trade
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- ${loop}
-
-
-
- 1
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 36.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- s:${__Random(0,${maximumsid},)}
- =
- true
- quotes:symbols
-
-
- false
- quotes
- =
- true
- quotes:submit
-
-
- false
- quotes
- =
- true
- quotes
-
-
- false
- 100
- =
- true
- quotes:quotes:0:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:1:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:2:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:3:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:4:quantity
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- DayTrader Quotes
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 16.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 15.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/portfolio.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 10.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/account.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 8.0
- 0.0
-
-
-
-
- ${__jexl3("${protocol}"== "http",)}
- false
- true
-
-
-
- true
- false
- ${hostname}
- ${port}
- /daytrader/marketsummary
- false
- {"action":"updateMarketSummary"}
- 20000
- open and close
- 20000
- false
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- ${__jexl3("${protocol}"== "https",)}
- false
- true
-
-
-
- true
- true
- ${hostname}
- ${port}
- /daytrader/marketsummary
- false
- {"action":"updateMarketSummary"}
- 20000
- open and close
- 20000
- false
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- s:${__Random(0,${maximumsid},)}
- =
- true
- quotes:symbols
-
-
- false
- quotes
- =
- true
- quotes:submit
-
-
- false
- quotes
- =
- true
- quotes
-
-
- false
- 100
- =
- true
- quotes:quotes:0:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:1:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:2:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:3:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:4:quantity
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- DayTrader Quotes
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
- false
- tobuy
- s:([0-9]+)
- $1$
- 0
- 1
- all
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- s:${tobuy}
- =
- true
- quotes:symbols
-
-
- false
- ${__Random(1,200)}
- =
- true
- quotes:quotes:0:quantity
-
-
- false
- buy
- =
- true
- quotes:quotes:0:buy
-
-
- false
- quotes
- =
- true
- quotes
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- been submitted
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/portfolio.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- false
- numHoldings
- of Holdings: </b>([1-9][0-9]*)</td>
- $1$
- 0
- 0
- all
-
-
-
- ${maxthinkingtime}
-
-
-
-
- ${__jexl("${numHoldings}" != "0")}
- false
- true
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- portfolio:holdings:0:sell
- =
- true
- portfolio:holdings:0:sell
-
-
- false
- portfolio
- =
- true
- portfolio
-
-
- false
- s:0,s:1,s:2,s:3,s:4
- =
- true
- portfolio:symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/portfolio.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- been submitted
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 2.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/account.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- uid:${logincounter}
- =
- true
- updateProfile:uid
-
-
- false
- rnd${__threadNum}${logincounter}
- =
- true
- updateProfile:fullname
-
-
- false
- xxx
- =
- true
- updateProfile:password
-
-
- false
- rndAddress
- =
- true
- updateProfile:address
-
-
- false
- xxx
- =
- true
- updateProfile:cpassword
-
-
- false
- rndCC
- =
- true
- updateProfile:ccn
-
-
- false
- rndEmail@email.com
- =
- true
- updateProfile:email
-
-
- false
- updateProfile
- =
- true
- updateProfile
-
-
- false
- Update Profile
- =
- true
- updateProfile:submit
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/account.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 1.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- tradeHome:logoff
- =
- true
- tradeHome:logoff
-
-
- false
- s:1,s:2,s:3,s:4
- =
- true
- tradeHome:symbols
-
-
- false
- tradeHome
- =
- true
- tradeHome
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- DayTrader Login
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/register.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- first:${__Random(0,999,)} last:${__Random(0,4999,)}
- =
- true
- register:fullname
-
-
- false
- first:${__Random(0,999,)} last:${__Random(0,4999,)}
- =
- true
- register:address
-
-
- false
- uid${logincounter}@${__Random(0,100,)}.com
- =
- true
- register:email
-
-
- false
- ru:${logincounter}${__threadNum}:${__time(HMS)}${__Random(0,999,)}
- =
- true
- register:uid
-
-
- false
- yyy
- =
- true
- register:password
-
-
- false
- yyy
- =
- true
- register:cpassword
-
-
- false
- 1000000
- =
- true
- register:money
-
-
- false
- 123-fake-ccnum-456
- =
- true
- register:ccn
-
-
- false
- register
- =
- true
- register
-
-
- false
- Register
- =
- true
- register:submit
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/register.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- Registration operation succeeded
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- tradeHome:logoff
- =
- true
- tradeHome:logoff
-
-
- false
- s:1,s:2,s:3,s:4
- =
- true
- tradeHome:symbols
-
-
- false
- tradeHome
- =
- true
- tradeHome
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- DayTrader Login
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
- loop
-
-
-
- false
-
-
- false
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- tradeHome:logoff
- =
- true
- tradeHome:logoff
-
-
- false
- s:0,s:1,s:2,s:3,s:4
- =
- true
- tradeHome:symbols
-
-
- false
- tradeHome
- =
- true
- tradeHome
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- DayTrader Login
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
- loop
-
-
-
- false
-
-
- false
-
-
-
-
-
-
- false
- jsfViewState
- <input type="hidden" name="javax\.faces\.ViewState" .* value="([^"]+)".*/>
- $1$
-
- 0
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 70.0
- 0.0
-
-
-
-
-
-
-
- true
- uid:${logincounter}
- =
- true
- uid
-
-
- true
- xxx
- =
- true
- passwd
-
-
- true
- login
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- loop
-
-
-
- true
-
-
- true
-
-
-
-
- Welcome to DayTrader
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- ${__jexl3("${protocol}"== "http",)}
- false
- true
-
-
-
- false
- ${hostname}
- ${port}
- /daytrader/marketsummary
- 20000
- 20000
-
-
-
-
- ${__jexl3("${protocol}"== "https",)}
- false
- true
-
-
-
- true
- ${hostname}
- ${port}
- /daytrader/marketsummary
- 20000
- 20000
-
-
-
-
- ${loop}
-
-
-
- 1
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 36.0
- 0.0
-
-
-
-
-
-
-
- true
- quotes
- =
- true
- action
-
-
- true
- s:${__Random(0,${maximumsid},)}
- =
- true
- symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- DayTrader: Quotes and Trading
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 16.0
- 0.0
-
-
-
-
-
-
-
- true
- home
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 15.0
- 0.0
-
-
-
-
-
-
-
- true
- portfolio
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 10.0
- 0.0
-
-
-
-
-
-
-
- true
- account
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 8.0
- 0.0
-
-
-
-
- false
- false
- ${hostname}
- ${port}
- /daytrader/marketsummary
- false
- {"action":"updateMarketSummary"}
- 20000
- use existing open connection
- 20000
- false
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- true
- quotes
- =
- true
- action
-
-
- true
- s:${__Random(0,${maximumsid},tobuy)}
- =
- true
- symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- buy
- =
- true
- action
-
-
- true
- s:${tobuy}
- =
- true
- symbol
-
-
- true
- ${__Random(1,200)}
- =
- true
- quantity
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- has been submitted
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- true
- portfolio
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- false
- firstHoldingID
- holdingID=([0-9]+)
- $1$
- NotFound
- 0
- all
-
-
-
- false
- firstHoldingIDBool
- holdingID=([0-9]+)
- true
- false
- 1
- all
-
-
-
- ${maxthinkingtime}
-
-
-
-
- ${firstHoldingIDBool}
- false
- true
-
-
-
-
-
-
- true
- sell
- =
- true
- action
-
-
- true
- ${firstHoldingID}
- =
- true
- holdingID
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- has been submitted
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 2.0
- 0.0
-
-
-
-
-
-
-
- true
- account
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- update_profile
- =
- true
- action
-
-
- true
- uid:${logincounter}
- =
- true
- userID
-
-
- true
- rnd${__threadNum}${logincounter}
- =
- true
- fullname
-
-
- true
- xxx
- =
- true
- password
-
-
- true
- rndAddress
- =
- true
- address
-
-
- true
- xxx
- =
- true
- cpassword
-
-
- true
- rndCC
- =
- true
- creditcard
-
-
- true
- rndEmail@email.com
- =
- true
- email
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 1.0
- 0.0
-
-
-
-
-
-
-
- true
- logout
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/register.jsp
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- register
- =
- true
- action
-
-
- true
- first:${__Random(0,999,)} last:${__Random(0,4999,)}
- =
- true
- Full Name
-
-
- true
- first:${__Random(0,999,)} last:${__Random(0,4999,)}
- =
- true
- snail mail
-
-
- true
- uid${logincounter}@${__Random(0,100,)}.com
- =
- true
- email
-
-
- true
- ru:${logincounter}${__threadNum}:${__time(HMS)}${__Random(0,999,)}
- =
- true
- user id
-
-
- true
- yyy
- =
- true
- passwd
-
-
- true
- yyy
- =
- true
- confirm passwd
-
-
- true
- 1000000
- =
- true
- money
-
-
- true
- 123-fake-ccnum-456
- =
- true
- Credit Card Number
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- logout
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- loop
-
-
-
- false
-
-
- false
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- true
- logout
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- DayTrader Login
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
- loop
-
-
-
- false
-
-
- false
-
-
-
-
-
-
- 4000
- 6000
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 20.0
- 0.0
-
-
-
-
-
-
-
- true
- s:${__Random(0,${maximumsid},)}
- =
- true
- symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- daytrader/rest/quotes
- POST
- false
- true
- true
- false
-
-
-
-
-
-
-
- 200
-
- Assertion.response_code
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- false
-
- saveConfig
-
- true
- true
- true
- true
- true
- true
- true
- true
- true
- false
- true
- true
- false
- false
- true
- false
- false
- false
- false
- false
- 0
- true
- true
- true
- true
-
-
- daytrader8.aggregateReport.csv
-
-
-
- true
-
- saveConfig
-
- true
- true
- true
- true
- true
- true
- true
- true
- true
- false
- true
- true
- false
- false
- false
- false
- false
- false
- false
- false
- 0
- true
- true
- true
- true
-
-
- daytrader8.resultsTree.csv
-
-
-
- false
-
- saveConfig
-
- true
- true
- true
- true
- true
- true
- true
- true
- true
- false
- true
- true
- false
- false
- true
- false
- false
- false
- false
- false
- 0
- true
- true
- true
- true
-
-
- daytrader8.resultsTable.csv
-
-
-
-
-
diff --git a/src/test/resources/test-applications/daytrader8/jmeter_files/daytrader8_no_ws.jmx b/src/test/resources/test-applications/daytrader8/jmeter_files/daytrader8_no_ws.jmx
deleted file mode 100644
index 026ca71e..00000000
--- a/src/test/resources/test-applications/daytrader8/jmeter_files/daytrader8_no_ws.jmx
+++ /dev/null
@@ -1,2607 +0,0 @@
-
-
-
-
-
- false
- false
-
-
-
-
-
-
-
-
- false
- -1
-
- ${__P(THREADS,50)}
- ${__P(RAMP,0)}
- 1355173676000
- 1355173676000
- true
- continue
- ${__P(DURATION, 180)}
-
- true
-
-
-
-
- false
- false
- rfc2109
-
-
-
-
-
- User-Agent
- Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322)
-
-
- Accept
- image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
-
-
- Accept-Language
- en-us
-
-
-
-
-
-
-
- minimumuid
- ${__P(BOTUID,0)}
- =
-
-
- maximumuid
- ${__P(TOPUID,14999)}
- =
-
-
- hostname
- ${__P(HOST,localhost)}
- =
-
-
- port
- ${__P(PORT,9080)}
- =
-
-
- maxthinkingtime
- ${__P(MAXTHINKTIME,0)}
- =
-
-
- maximumsid
- ${__P(STOCKS,9999)}
- =
-
-
- protocol
- ${__P(PROTOCOL,http)}
- =
- http | https
-
-
-
-
-
- ${minimumuid}
- ${maximumuid}
- 1
- logincounter
-
- false
-
-
-
- 1
-
-
-
- 1
- true
- 50
-
- ThroughputController.percentThroughput
- 10.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/welcome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- xxx
- =
- true
- login:password
-
-
- false
- Log in
- =
- true
- login:submit
-
-
- false
- uid:${logincounter}
- =
- true
- login:uid
-
-
- false
- 1
- =
- true
- login_SUBMIT
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/welcome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- loop
-
-
-
- true
-
-
- true
-
-
-
-
- Ready to Trade
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- ${loop}
-
-
-
- 1
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 36.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- s:${__Random(0,${maximumsid},)}
- =
- true
- quotes:symbols
-
-
- false
- quotes
- =
- true
- quotes:submit2
-
-
- false
- 1
- =
- true
- quotes_SUBMIT
-
-
- false
- 100
- =
- true
- quotes:quotes:0:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:1:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:2:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:3:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:4:quantity
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- DayTrader Quotes
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 16.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 15.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/portfolio.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 10.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/account.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 8.0
- 0.0
-
-
-
-
- ${__jexl3("${protocol}"== "http",)}
- false
- true
-
-
-
- true
- false
- ${hostname}
- ${port}
- /daytrader/marketsummary
- false
- {"action":"updateMarketSummary"}
- 20000
- open and close
- 20000
- false
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- ${__jexl3("${protocol}"== "https",)}
- false
- true
-
-
-
- true
- true
- ${hostname}
- ${port}
- /daytrader/marketsummary
- false
- {"action":"updateMarketSummary"}
- 20000
- open and close
- 20000
- false
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- s:${__Random(0,${maximumsid},)}
- =
- true
- quotes:symbols
-
-
- false
- quotes
- =
- true
- quotes:submit2
-
-
- false
- 1
- =
- true
- quotes_SUBMIT
-
-
- false
- 100
- =
- true
- quotes:quotes:0:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:1:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:2:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:3:quantity
-
-
- false
- 100
- =
- true
- quotes:quotes:4:quantity
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- DayTrader Quotes
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
- false
- tobuy
- s:([0-9]+)
- $1$
- 0
- 1
- all
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- s:${tobuy}
- =
- true
- quotes:symbols
-
-
- false
- ${__Random(1,200)}
- =
- true
- quotes:quotes:0:quantity
-
-
- false
- buy
- =
- true
- quotes:quotes:0:buy
-
-
- false
- 1
- =
- true
- quotes_SUBMIT
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/quote.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- been submitted
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/portfolio.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- false
- numHoldings
- of Holdings: </b>([1-9][0-9]*)</td>
- $1$
- 0
- 0
- all
-
-
-
- ${maxthinkingtime}
-
-
-
-
- ${__jexl("${numHoldings}" != "0")}
- false
- true
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- portfolio:holdings:0:sell
- =
- true
- portfolio:_idcl
-
-
- false
- 1
- =
- true
- portfolio_SUBMIT
-
-
- false
- s:0,s:1,s:2,s:3,s:4
- =
- true
- portfolio:symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/portfolio.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- been submitted
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 2.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/account.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- uid:${logincounter}
- =
- true
- updateProfile:uid
-
-
- false
- rnd${__threadNum}${logincounter}
- =
- true
- updateProfile:fullname
-
-
- false
- xxx
- =
- true
- updateProfile:password
-
-
- false
- rndAddress
- =
- true
- updateProfile:address
-
-
- false
- xxx
- =
- true
- updateProfile:cpassword
-
-
- false
- rndCC
- =
- true
- updateProfile:ccn
-
-
- false
- rndEmail@email.com
- =
- true
- updateProfile:email
-
-
- false
- 1
- =
- true
- updateProfile_SUBMIT
-
-
- false
- Update Profile
- =
- true
- updateProfile:submit
-
-
- false
- s:0,s:1,s:2,s:3,s:4
- =
- true
- updateProfile:symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/account.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 1.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- tradeHome:logoff
- =
- true
- tradeHome:_idcl
-
-
- false
- s:0,s:1,s:2,s:3,s:4
- =
- true
- tradeHome:symbols
-
-
- false
- 1
- =
- true
- tradeHome_SUBMIT
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- DayTrader Login
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/register.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- first:${__Random(0,999,)} last:${__Random(0,4999,)}
- =
- true
- register:fullname
-
-
- false
- first:${__Random(0,999,)} last:${__Random(0,4999,)}
- =
- true
- register:address
-
-
- false
- uid${logincounter}@${__Random(0,100,)}.com
- =
- true
- register:email
-
-
- false
- ru:${logincounter}${__threadNum}:${__time(HMS)}${__Random(0,999,)}
- =
- true
- register:uid
-
-
- false
- yyy
- =
- true
- register:password
-
-
- false
- yyy
- =
- true
- register:cpassword
-
-
- false
- 1000000
- =
- true
- register:money
-
-
- false
- 123-fake-ccnum-456
- =
- true
- register:ccn
-
-
- false
- 1
- =
- true
- register_SUBMIT
-
-
- false
- Register
- =
- true
- register:submit
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/register.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- Registration operation succeeded
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- tradeHome:logoff
- =
- true
- tradeHome:_idcl
-
-
- false
- s:0,s:1,s:2,s:3,s:4
- =
- true
- tradeHome:symbols
-
-
- false
- 1
- =
- true
- tradeHome_SUBMIT
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- DayTrader Login
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
- loop
-
-
-
- false
-
-
- false
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
-
-
- true
- ${jsfViewState}
- =
- true
- javax.faces.ViewState
-
-
- false
- tradeHome:logoff
- =
- true
- tradeHome:_idcl
-
-
- false
- s:0,s:1,s:2,s:3,s:4
- =
- true
- tradeHome:symbols
-
-
- false
- 1
- =
- true
- tradeHome_SUBMIT
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/tradehome.faces
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- DayTrader Login
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
- loop
-
-
-
- false
-
-
- false
-
-
-
-
-
-
- false
- jsfViewState
- <input type="hidden" name="javax\.faces\.ViewState" id="j_id__v_0:javax\.faces\.ViewState:1" value="([^"]+)".*/>
- $1$
-
- 0
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 70.0
- 0.0
-
-
-
-
-
-
-
- true
- uid:${logincounter}
- =
- true
- uid
-
-
- true
- xxx
- =
- true
- passwd
-
-
- true
- login
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- POST
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- loop
-
-
-
- true
-
-
- true
-
-
-
-
- Welcome to DayTrader
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- ${__jexl3("${protocol}"== "http",)}
- false
- true
-
-
-
- false
- ${hostname}
- ${port}
- /daytrader/marketsummary
- 20000
- 20000
-
-
-
-
- ${__jexl3("${protocol}"== "https",)}
- false
- true
-
-
-
- true
- ${hostname}
- ${port}
- /daytrader/marketsummary
- 20000
- 20000
-
-
-
-
- ${loop}
-
-
-
- 1
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 36.0
- 0.0
-
-
-
-
-
-
-
- true
- quotes
- =
- true
- action
-
-
- true
- s:${__Random(0,${maximumsid},)}
- =
- true
- symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- DayTrader: Quotes and Trading
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 16.0
- 0.0
-
-
-
-
-
-
-
- true
- home
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 15.0
- 0.0
-
-
-
-
-
-
-
- true
- portfolio
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 10.0
- 0.0
-
-
-
-
-
-
-
- true
- account
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 8.0
- 0.0
-
-
-
-
- false
- false
- ${hostname}
- ${port}
- /daytrader/marketsummary
- false
- {"action":"updateMarketSummary"}
- 20000
- use existing open connection
- 20000
- false
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- true
- quotes
- =
- true
- action
-
-
- true
- s:${__Random(0,${maximumsid},tobuy)}
- =
- true
- symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- buy
- =
- true
- action
-
-
- true
- s:${tobuy}
- =
- true
- symbol
-
-
- true
- ${__Random(1,200)}
- =
- true
- quantity
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- has been submitted
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- true
- portfolio
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- false
- firstHoldingID
- holdingID=([0-9]+)
- $1$
- NotFound
- 0
- all
-
-
-
- false
- firstHoldingIDBool
- holdingID=([0-9]+)
- true
- false
- 1
- all
-
-
-
- ${maxthinkingtime}
-
-
-
-
- ${firstHoldingIDBool}
- false
- true
-
-
-
-
-
-
- true
- sell
- =
- true
- action
-
-
- true
- ${firstHoldingID}
- =
- true
- holdingID
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
-
- has been submitted
-
- Assertion.response_data
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 2.0
- 0.0
-
-
-
-
-
-
-
- true
- account
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- update_profile
- =
- true
- action
-
-
- true
- uid:${logincounter}
- =
- true
- userID
-
-
- true
- rnd${__threadNum}${logincounter}
- =
- true
- fullname
-
-
- true
- xxx
- =
- true
- password
-
-
- true
- rndAddress
- =
- true
- address
-
-
- true
- xxx
- =
- true
- cpassword
-
-
- true
- rndCC
- =
- true
- creditcard
-
-
- true
- rndEmail@email.com
- =
- true
- email
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 1.0
- 0.0
-
-
-
-
-
-
-
- true
- logout
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/register.jsp
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- register
- =
- true
- action
-
-
- true
- first:${__Random(0,999,)} last:${__Random(0,4999,)}
- =
- true
- Full Name
-
-
- true
- first:${__Random(0,999,)} last:${__Random(0,4999,)}
- =
- true
- snail mail
-
-
- true
- uid${logincounter}@${__Random(0,100,)}.com
- =
- true
- email
-
-
- true
- ru:${logincounter}${__threadNum}:${__time(HMS)}${__Random(0,999,)}
- =
- true
- user id
-
-
- true
- yyy
- =
- true
- passwd
-
-
- true
- yyy
- =
- true
- confirm passwd
-
-
- true
- 1000000
- =
- true
- money
-
-
- true
- 123-fake-ccnum-456
- =
- true
- Credit Card Number
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- true
- logout
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
- loop
-
-
-
- false
-
-
- false
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 4.0
- 0.0
-
-
-
-
-
-
-
- true
- logout
- =
- true
- action
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- /daytrader/app
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
- DayTrader Login
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
- loop
-
-
-
- false
-
-
- false
-
-
-
-
-
-
- 4000
- 6000
-
-
-
-
- 1
- true
- 1
-
- ThroughputController.percentThroughput
- 20.0
- 0.0
-
-
-
-
-
-
-
- true
- s:${__Random(0,${maximumsid},)}
- =
- true
- symbols
-
-
-
- ${hostname}
- ${port}
- ${protocol}
-
- daytrader/rest/quotes
- POST
- false
- true
- true
- false
-
-
-
-
-
-
-
- 200
-
- Assertion.response_code
- false
- 2
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
-
- false
-
- saveConfig
-
- true
- true
- true
- true
- true
- true
- true
- true
- true
- false
- true
- true
- false
- false
- true
- false
- false
- false
- false
- false
- 0
- true
- true
- true
- true
-
-
- daytrader8.aggregateReport.csv
-
-
-
- true
-
- saveConfig
-
- true
- true
- true
- true
- true
- true
- true
- true
- true
- false
- true
- true
- false
- false
- false
- false
- false
- false
- false
- false
- 0
- true
- true
- true
- true
-
-
- daytrader8.resultsTree.csv
-
-
-
- false
-
- saveConfig
-
- true
- true
- true
- true
- true
- true
- true
- true
- true
- false
- true
- true
- false
- false
- true
- false
- false
- false
- false
- false
- 0
- true
- true
- true
- true
-
-
- daytrader7.resultsTable.csv
-
-
-
-
-
diff --git a/src/test/resources/test-applications/daytrader8/jmeter_files/daytrader_primitive.jmx b/src/test/resources/test-applications/daytrader8/jmeter_files/daytrader_primitive.jmx
deleted file mode 100644
index 29ccdf4c..00000000
--- a/src/test/resources/test-applications/daytrader8/jmeter_files/daytrader_primitive.jmx
+++ /dev/null
@@ -1,203 +0,0 @@
-
-
-
-
-
- false
- false
-
-
-
-
-
-
-
-
- false
- -1
-
- ${__P(THREADS, 50)}
- 0
- 1355173676000
- 1355173676000
- true
- continue
- ${__P(DURATION, 180)}
-
- true
-
-
-
-
- false
- false
- rfc2109
-
-
-
-
-
- User-Agent
- Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322)
-
-
- Accept
- image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
-
-
- Accept-Language
- en-us
-
-
-
-
-
-
-
- VIEWSTATE
-
- =
-
-
- jsessionid
-
- =
-
-
- minimumuid
- ${__P(BOTUID,0)}
- =
-
-
- maximumuid
- ${__P(TOPUID,14999)}
- =
-
-
- hostname
- ${__P(HOST,)}
- =
-
-
- maxthinkingtime
- 0
- =
-
-
- maximumsid
- ${__P(STOCKS,9999)}
- =
-
-
- url
- ${__P(PRIMITIVE_URL,servlet/PingServlet)}
- =
-
-
-
-
-
- true
- -1
-
-
-
-
-
-
- ${hostname}
- 9080
- http
-
- /daytrader/${url}
- GET
- false
- true
- true
- false
-
- HttpClient4
-
-
-
-
-
- ${maxthinkingtime}
-
-
-
-
-
-
- false
-
- saveConfig
-
- true
- true
- true
- true
- true
- true
- true
- true
- true
- false
- true
- true
- false
- false
- true
- false
- false
- false
- false
- false
- 0
- true
- true
- true
- true
-
-
-
-
-
-
- true
-
- saveConfig
-
- true
- true
- true
- true
- true
- true
- true
- true
- true
- false
- true
- true
- false
- false
- true
- false
- false
- false
- false
- false
- 0
- true
- true
- true
- true
-
-
- C:\jmeter\jmeter_script\report.csv
-
-
-
-
-
diff --git a/src/test/resources/test-applications/daytrader8/pom.xml b/src/test/resources/test-applications/daytrader8/pom.xml
deleted file mode 100644
index cd631847..00000000
--- a/src/test/resources/test-applications/daytrader8/pom.xml
+++ /dev/null
@@ -1,104 +0,0 @@
-
-
- 4.0.0
- io.openliberty.samples
- io.openliberty.sample.daytrader8
- 1.0-SNAPSHOT
- war
-
- UTF-8
- UTF-8
- 1.8
- 1.8
-
- 10.14.2.0
- ${user.home}/.m2/repository/org/apache/derby/derby
-
- 9080
- 9443
-
-
-
- javax
- javaee-api
- 8.0
- provided
-
-
- taglibs
- standard
- 1.1.1
- compile
-
-
- javax.xml.bind
- jaxb-api
- 2.3.0
- provided
-
-
-
- org.apache.derby
- derby
- ${version.derby}
- test
-
-
-
- ${project.artifactId}
-
-
-
- io.openliberty.tools
- liberty-maven-plugin
- 3.3-M4
-
-
- ${testServerHttpPort}
- ${testServerHttpsPort}
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
- 3.1.2
-
-
- copy-derby-dependency
- package
-
- copy-dependencies
-
-
- derby
- ${project.build.directory}/liberty/wlp/usr/shared/resources/DerbyLibs/
-
-
-
-
-
- maven-resources-plugin
- 2.6
-
-
- copy-resources
- package
-
- copy-resources
-
-
- ${project.build.directory}/liberty/wlp/usr/shared/resources/data
-
-
- resources/data
- false
-
-
-
-
-
-
-
-
-
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/README_DO_NOT_TOUCH_FILES.txt b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/README_DO_NOT_TOUCH_FILES.txt
deleted file mode 100644
index a4bc1452..00000000
--- a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/README_DO_NOT_TOUCH_FILES.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-
-# *************************************************************************
-# *** DO NOT TOUCH FILES IN THIS DIRECTORY! ***
-# *** FILES IN THIS DIRECTORY AND SUBDIRECTORIES CONSTITUTE A DERBY ***
-# *** DATABASE, WHICH INCLUDES THE DATA (USER AND SYSTEM) AND THE ***
-# *** FILES NECESSARY FOR DATABASE RECOVERY. ***
-# *** EDITING, ADDING, OR DELETING ANY OF THESE FILES MAY CAUSE DATA ***
-# *** CORRUPTION AND LEAVE THE DATABASE IN A NON-RECOVERABLE STATE. ***
-# *************************************************************************
\ No newline at end of file
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/log/README_DO_NOT_TOUCH_FILES.txt b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/log/README_DO_NOT_TOUCH_FILES.txt
deleted file mode 100644
index 56df292f..00000000
--- a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/log/README_DO_NOT_TOUCH_FILES.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-
-# *************************************************************************
-# *** DO NOT TOUCH FILES IN THIS DIRECTORY! ***
-# *** FILES IN THIS DIRECTORY ARE USED BY THE DERBY DATABASE RECOVERY ***
-# *** SYSTEM. EDITING, ADDING, OR DELETING FILES IN THIS DIRECTORY ***
-# *** WILL CAUSE THE DERBY RECOVERY SYSTEM TO FAIL, LEADING TO ***
-# *** NON-RECOVERABLE CORRUPT DATABASES. ***
-# *************************************************************************
\ No newline at end of file
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/log/log.ctrl b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/log/log.ctrl
deleted file mode 100644
index 5ae5e491..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/log/log.ctrl and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/log/log286.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/log/log286.dat
deleted file mode 100644
index 114bef65..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/log/log286.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/log/logmirror.ctrl b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/log/logmirror.ctrl
deleted file mode 100644
index 5ae5e491..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/log/logmirror.ctrl and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/README_DO_NOT_TOUCH_FILES.txt b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/README_DO_NOT_TOUCH_FILES.txt
deleted file mode 100644
index 2bdad061..00000000
--- a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/README_DO_NOT_TOUCH_FILES.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-
-# *************************************************************************
-# *** DO NOT TOUCH FILES IN THIS DIRECTORY! ***
-# *** FILES IN THIS DIRECTORY ARE USED BY THE DERBY DATABASE TO STORE ***
-# *** USER AND SYSTEM DATA. EDITING, ADDING, OR DELETING FILES IN THIS ***
-# *** DIRECTORY WILL CORRUPT THE ASSOCIATED DERBY DATABASE AND MAKE ***
-# *** IT NON-RECOVERABLE. ***
-# *************************************************************************
\ No newline at end of file
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c10.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c10.dat
deleted file mode 100644
index ca061266..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c10.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c101.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c101.dat
deleted file mode 100644
index 7c9fc8a0..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c101.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c111.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c111.dat
deleted file mode 100644
index 8e4371b3..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c111.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c121.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c121.dat
deleted file mode 100644
index 5f7789fe..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c121.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c130.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c130.dat
deleted file mode 100644
index f9de0051..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c130.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c141.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c141.dat
deleted file mode 100644
index 2b9408a3..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c141.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c150.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c150.dat
deleted file mode 100644
index db2ff892..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c150.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c161.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c161.dat
deleted file mode 100644
index a2af9876..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c161.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c171.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c171.dat
deleted file mode 100644
index b3e1217b..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c171.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c180.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c180.dat
deleted file mode 100644
index e7b80ed0..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c180.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c191.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c191.dat
deleted file mode 100644
index 5e31e3be..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c191.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1a1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1a1.dat
deleted file mode 100644
index e578b73b..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1a1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1b1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1b1.dat
deleted file mode 100644
index 2e068140..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1b1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1c0.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1c0.dat
deleted file mode 100644
index c5b91e2c..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1c0.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1d1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1d1.dat
deleted file mode 100644
index 451f02f4..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1d1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1e0.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1e0.dat
deleted file mode 100644
index 761408d3..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1e0.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1f1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1f1.dat
deleted file mode 100644
index 78d701f4..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c1f1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c20.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c20.dat
deleted file mode 100644
index 81d623f2..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c20.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c200.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c200.dat
deleted file mode 100644
index c3a7808d..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c200.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c211.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c211.dat
deleted file mode 100644
index 54e15869..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c211.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c221.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c221.dat
deleted file mode 100644
index 59900bc0..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c221.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c230.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c230.dat
deleted file mode 100644
index 207264a3..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c230.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c241.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c241.dat
deleted file mode 100644
index 4433404a..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c241.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c251.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c251.dat
deleted file mode 100644
index c6fab1e7..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c251.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c260.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c260.dat
deleted file mode 100644
index 25f81fde..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c260.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c271.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c271.dat
deleted file mode 100644
index 51cde573..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c271.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c281.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c281.dat
deleted file mode 100644
index cfed875d..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c281.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c290.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c290.dat
deleted file mode 100644
index a85589e5..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c290.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2a1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2a1.dat
deleted file mode 100644
index 8e2ed6af..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2a1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2b1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2b1.dat
deleted file mode 100644
index 2a296924..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2b1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2c1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2c1.dat
deleted file mode 100644
index 5511575f..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2c1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2d0.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2d0.dat
deleted file mode 100644
index c9063637..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2d0.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2e1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2e1.dat
deleted file mode 100644
index fccdbd67..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2e1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2f0.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2f0.dat
deleted file mode 100644
index d854b4b4..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c2f0.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c300.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c300.dat
deleted file mode 100644
index 2053e010..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c300.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c31.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c31.dat
deleted file mode 100644
index ec081434..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c31.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c311.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c311.dat
deleted file mode 100644
index f60c260f..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c311.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c321.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c321.dat
deleted file mode 100644
index a9d74536..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c321.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c331.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c331.dat
deleted file mode 100644
index 85ee72b3..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c331.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c340.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c340.dat
deleted file mode 100644
index d99b11a3..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c340.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c351.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c351.dat
deleted file mode 100644
index f822f4cb..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c351.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c361.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c361.dat
deleted file mode 100644
index b5c8f259..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c361.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c371.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c371.dat
deleted file mode 100644
index ad11f01b..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c371.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c380.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c380.dat
deleted file mode 100644
index 26b6dd66..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c380.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c391.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c391.dat
deleted file mode 100644
index 38ea5620..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c391.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3a1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3a1.dat
deleted file mode 100644
index fe7a67b1..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3a1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3b1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3b1.dat
deleted file mode 100644
index 3a73f40c..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3b1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3c0.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3c0.dat
deleted file mode 100644
index 4d061cf0..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3c0.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3d1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3d1.dat
deleted file mode 100644
index 45c9fa24..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3d1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3e1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3e1.dat
deleted file mode 100644
index 48f53e68..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3e1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3f1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3f1.dat
deleted file mode 100644
index 08acdcee..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c3f1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c400.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c400.dat
deleted file mode 100644
index a23e287b..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c400.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c41.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c41.dat
deleted file mode 100644
index 6889bcb3..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c41.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c411.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c411.dat
deleted file mode 100644
index 22d5ab93..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c411.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c421.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c421.dat
deleted file mode 100644
index c5274a22..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c421.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c430.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c430.dat
deleted file mode 100644
index 55c948db..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c430.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c441.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c441.dat
deleted file mode 100644
index 3948b2a3..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c441.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c451.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c451.dat
deleted file mode 100644
index fe1ab73e..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c451.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c461.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c461.dat
deleted file mode 100644
index e6d98541..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c461.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c470.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c470.dat
deleted file mode 100644
index c9f2eb1c..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c470.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c481.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c481.dat
deleted file mode 100644
index 397b2917..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c481.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c490.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c490.dat
deleted file mode 100644
index 63e4bc67..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c490.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4a1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4a1.dat
deleted file mode 100644
index f0f0ecf8..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4a1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4b0.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4b0.dat
deleted file mode 100644
index a8fed1f8..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4b0.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4c1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4c1.dat
deleted file mode 100644
index 6e1de5e6..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4c1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4d0.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4d0.dat
deleted file mode 100644
index f33c61a9..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4d0.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4e1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4e1.dat
deleted file mode 100644
index 02b55f6d..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4e1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4f1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4f1.dat
deleted file mode 100644
index 5be3e313..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c4f1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c51.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c51.dat
deleted file mode 100644
index 0c3b53eb..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c51.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c60.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c60.dat
deleted file mode 100644
index 4896921f..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c60.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c71.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c71.dat
deleted file mode 100644
index 9df02c15..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c71.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c720.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c720.dat
deleted file mode 100644
index c3794777..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c720.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c731.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c731.dat
deleted file mode 100644
index 756b908f..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c731.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c740.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c740.dat
deleted file mode 100644
index 3f2ad6e6..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c740.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c751.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c751.dat
deleted file mode 100644
index fe2bb77c..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c751.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c760.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c760.dat
deleted file mode 100644
index 98b96abf..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c760.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c771.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c771.dat
deleted file mode 100644
index 789d9ae4..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c771.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c780.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c780.dat
deleted file mode 100644
index c24c67e9..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c780.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c791.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c791.dat
deleted file mode 100644
index ff867857..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c791.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7a0.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7a0.dat
deleted file mode 100644
index 0593ae3e..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7a0.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7b1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7b1.dat
deleted file mode 100644
index 7354db2e..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7b1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7c0.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7c0.dat
deleted file mode 100644
index 907f038f..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7c0.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7d1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7d1.dat
deleted file mode 100644
index 40100e61..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7d1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7e1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7e1.dat
deleted file mode 100644
index 85a139e4..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7e1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7f1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7f1.dat
deleted file mode 100644
index 2e558d6e..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c7f1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c801.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c801.dat
deleted file mode 100644
index eb06862f..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c801.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c81.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c81.dat
deleted file mode 100644
index ab21c74c..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c81.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c811.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c811.dat
deleted file mode 100644
index e502c064..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c811.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c821.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c821.dat
deleted file mode 100644
index f426d4f7..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c821.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c90.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c90.dat
deleted file mode 100644
index 72619ed7..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/c90.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/ca1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/ca1.dat
deleted file mode 100644
index 981a2c58..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/ca1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/cb1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/cb1.dat
deleted file mode 100644
index a4fb8f44..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/cb1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/cc0.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/cc0.dat
deleted file mode 100644
index d6b3b0f4..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/cc0.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/cd1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/cd1.dat
deleted file mode 100644
index 3002ab5d..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/cd1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/ce1.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/ce1.dat
deleted file mode 100644
index 24edcc74..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/ce1.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/cf0.dat b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/cf0.dat
deleted file mode 100644
index b943f01d..00000000
Binary files a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/seg0/cf0.dat and /dev/null differ
diff --git a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/service.properties b/src/test/resources/test-applications/daytrader8/resources/data/tradedb/service.properties
deleted file mode 100644
index 420cab1f..00000000
--- a/src/test/resources/test-applications/daytrader8/resources/data/tradedb/service.properties
+++ /dev/null
@@ -1,23 +0,0 @@
-#/Users/jdmcclur/git/sample.daytrader8/target/liberty/wlp/usr/shared/resources/data/tradedb
-# ********************************************************************
-# *** Please do NOT edit this file. ***
-# *** CHANGING THE CONTENT OF THIS FILE MAY CAUSE DATA CORRUPTION. ***
-# ********************************************************************
-#Fri Jan 11 09:46:30 CST 2019
-SysschemasIndex2Identifier=225
-SyscolumnsIdentifier=144
-SysconglomeratesIndex1Identifier=49
-SysconglomeratesIdentifier=32
-SyscolumnsIndex2Identifier=177
-SysschemasIndex1Identifier=209
-SysconglomeratesIndex3Identifier=81
-SystablesIndex2Identifier=129
-SyscolumnsIndex1Identifier=161
-derby.serviceProtocol=org.apache.derby.database.Database
-SysschemasIdentifier=192
-derby.storage.propertiesId=16
-SysconglomeratesIndex2Identifier=65
-derby.serviceLocale=en_US
-SystablesIdentifier=96
-SystablesIndex1Identifier=113
-#--- last line, don't put anything after this line ---
diff --git a/src/test/resources/test-applications/daytrader8/scripts/buildAll.sh b/src/test/resources/test-applications/daytrader8/scripts/buildAll.sh
deleted file mode 100755
index f57b6890..00000000
--- a/src/test/resources/test-applications/daytrader8/scripts/buildAll.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-cd "$(dirname "$0")"
-cd ..
-
-mvn clean package
-cp target/io.openliberty.sample.daytrader8.war scripts/io.openliberty.sample.daytrader8.war
-
-cd scripts
-./switchToWF.sh
-cd ..
-mvn clean package
-cp target/io.openliberty.sample.daytrader8.war scripts/io.openliberty.sample.daytrader8-WF.war
-cd scripts
-./switchFromWF.sh
-
-./switchToPayara.sh
-cd ..
-mvn clean package
-cp target/io.openliberty.sample.daytrader8.war scripts/io.openliberty.sample.daytrader8-Payara.war
-cd scripts
-./switchFromPayara.sh
diff --git a/src/test/resources/test-applications/daytrader8/scripts/switchFromPayara.sh b/src/test/resources/test-applications/daytrader8/scripts/switchFromPayara.sh
deleted file mode 100755
index 870c5695..00000000
--- a/src/test/resources/test-applications/daytrader8/scripts/switchFromPayara.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/bash
-cd "$(dirname "$0")"
-
-transform () {
- sed -i.bak "s#@ActivationConfigProperty(propertyName = \"destination\", propertyValue = \"T#//@ActivationConfigProperty(propertyName = \"destination\", propertyValue = \"T#" $1
- sed -i.bak "s#//@ActivationConfigProperty(propertyName = \"destination\", propertyValue = \"j#@ActivationConfigProperty(propertyName = \"destination\", propertyValue = \"j#" $1
- rm $1.bak
-}
-
-transform "../src/main/java/com/ibm/websphere/samples/daytrader/mdb/DTBroker3MDB.java"
-transform "../src/main/java/com/ibm/websphere/samples/daytrader/mdb/DTStreamer3MDB.java"
-
-mv ../src/main/java/com/ibm/websphere/samples/daytrader/web/prims/ejb3/PingServlet2MDBQueue.java_bak ../src/main/java/com/ibm/websphere/samples/daytrader/web/prims/ejb3/PingServlet2MDBQueue.java
-mv ../src/main/java/com/ibm/websphere/samples/daytrader/web/prims/ejb3/PingServlet2MDBTopic.java_bak ../src/main/java/com/ibm/websphere/samples/daytrader/web/prims/ejb3/PingServlet2MDBTopic.java
-
-
diff --git a/src/test/resources/test-applications/daytrader8/scripts/switchFromWF.sh b/src/test/resources/test-applications/daytrader8/scripts/switchFromWF.sh
deleted file mode 100755
index 4215abe3..00000000
--- a/src/test/resources/test-applications/daytrader8/scripts/switchFromWF.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-cd "$(dirname "$0")"
-
-transform () {
- sed -i.bak "s#@Resource(name = \"java#//@Resource(name = \"java#" $1
- sed -i.bak "s#@Resource(lookup = \"java#//@Resource(lookup = \"java#" $1
- sed -i.bak "s#//@Resource(name = \"jm#@Resource(name = \"jm#" $1
- sed -i.bak "s#//@Resource(lookup = \"jm#@Resource(lookup = \"jm#" $1
- sed -i.bak "s#//@Resource(lookup = \"jd#@Resource(lookup = \"jd#" $1
- rm $1.bak
-}
-
-transform "../src/main/java/com/ibm/websphere/samples/daytrader/impl/ejb3/TradeSLSBBean.java"
-transform "../src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/TradeDirect.java"
-transform "../src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/TradeDirectDBUtils.java"
-
-
diff --git a/src/test/resources/test-applications/daytrader8/scripts/switchToPayara.sh b/src/test/resources/test-applications/daytrader8/scripts/switchToPayara.sh
deleted file mode 100755
index c8b7f34b..00000000
--- a/src/test/resources/test-applications/daytrader8/scripts/switchToPayara.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/bash
-cd "$(dirname "$0")"
-
-transform () {
- sed -i.bak "s#//@ActivationConfigProperty(propertyName = \"destination\", propertyValue = \"T#@ActivationConfigProperty(propertyName = \"destination\", propertyValue = \"T#" $1
- sed -i.bak "s#@ActivationConfigProperty(propertyName = \"destination\", propertyValue = \"j#//@ActivationConfigProperty(propertyName = \"destination\", propertyValue = \"j#" $1
- rm $1.bak
-}
-
-transform "../src/main/java/com/ibm/websphere/samples/daytrader/mdb/DTBroker3MDB.java"
-transform "../src/main/java/com/ibm/websphere/samples/daytrader/mdb/DTStreamer3MDB.java"
-
-mv ../src/main/java/com/ibm/websphere/samples/daytrader/web/prims/ejb3/PingServlet2MDBQueue.java ../src/main/java/com/ibm/websphere/samples/daytrader/web/prims/ejb3/PingServlet2MDBQueue.java_bak
-mv ../src/main/java/com/ibm/websphere/samples/daytrader/web/prims/ejb3/PingServlet2MDBTopic.java ../src/main/java/com/ibm/websphere/samples/daytrader/web/prims/ejb3/PingServlet2MDBTopic.java_bak
-
-
diff --git a/src/test/resources/test-applications/daytrader8/scripts/switchToWF.sh b/src/test/resources/test-applications/daytrader8/scripts/switchToWF.sh
deleted file mode 100755
index 79415a57..00000000
--- a/src/test/resources/test-applications/daytrader8/scripts/switchToWF.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-cd "$(dirname "$0")"
-
-transform () {
- sed -i.bak "s#//@Resource(name = \"java#@Resource(name = \"java#" $1
- sed -i.bak "s#//@Resource(lookup = \"java#@Resource(lookup = \"java#" $1
- sed -i.bak "s#@Resource(name = \"jm#//@Resource(name = \"jm#" $1
- sed -i.bak "s#@Resource(lookup = \"jm#//@Resource(lookup = \"jm#" $1
- sed -i.bak "s#@Resource(lookup = \"jd#//@Resource(lookup = \"jd#" $1
- rm $1.bak
-}
-
-transform "../src/main/java/com/ibm/websphere/samples/daytrader/impl/ejb3/TradeSLSBBean.java"
-transform "../src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/TradeDirect.java"
-transform "../src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/TradeDirectDBUtils.java"
-
-
diff --git a/src/test/resources/test-applications/daytrader8/src/main/java/META-INF/DEPENDENCIES b/src/test/resources/test-applications/daytrader8/src/main/java/META-INF/DEPENDENCIES
deleted file mode 100644
index cb8878a9..00000000
--- a/src/test/resources/test-applications/daytrader8/src/main/java/META-INF/DEPENDENCIES
+++ /dev/null
@@ -1,15 +0,0 @@
-// ------------------------------------------------------------------
-// Transitive dependencies of this project determined from the
-// maven pom organized by organization.
-// ------------------------------------------------------------------
-
-DayTrader :: Web Application
-
-
-From: 'an unknown organization'
- - Unnamed - taglibs:standard:jar:1.1.1 taglibs:standard:jar:1.1.1
-
-
-
-
-
diff --git a/src/test/resources/test-applications/daytrader8/src/main/java/META-INF/LICENSE b/src/test/resources/test-applications/daytrader8/src/main/java/META-INF/LICENSE
deleted file mode 100644
index d6456956..00000000
--- a/src/test/resources/test-applications/daytrader8/src/main/java/META-INF/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed 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.
diff --git a/src/test/resources/test-applications/daytrader8/src/main/java/build.properties b/src/test/resources/test-applications/daytrader8/src/main/java/build.properties
deleted file mode 100644
index de47f1c7..00000000
--- a/src/test/resources/test-applications/daytrader8/src/main/java/build.properties
+++ /dev/null
@@ -1,17 +0,0 @@
-##
-## (C) Copyright IBM Corporation 2015.
-##
-## Licensed 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.
-##
-
-ejb_version=${pom.version}
diff --git a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/beans/MarketSummaryDataBean.java b/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/beans/MarketSummaryDataBean.java
deleted file mode 100644
index f96b34c9..00000000
--- a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/beans/MarketSummaryDataBean.java
+++ /dev/null
@@ -1,285 +0,0 @@
-/**
- * (C) Copyright IBM Corporation 2015.
- *
- * Licensed 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.
- */
-package com.ibm.websphere.samples.daytrader.beans;
-
-import com.ibm.websphere.samples.daytrader.entities.QuoteDataBean;
-import com.ibm.websphere.samples.daytrader.util.FinancialUtils;
-import com.ibm.websphere.samples.daytrader.util.Log;
-import com.ibm.websphere.samples.daytrader.util.TradeConfig;
-import java.io.Serializable;
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-import java.util.Iterator;
-import javax.json.Json;
-import javax.json.JsonObject;
-import javax.json.JsonObjectBuilder;
-
-public class MarketSummaryDataBean implements Serializable {
-
- private static final long serialVersionUID = 650652242288745600L;
- private BigDecimal TSIA; /* Trade Stock Index Average */
- private BigDecimal openTSIA; /* Trade Stock Index Average at the open */
- private double volume; /* volume of shares traded */
- private Collection topGainers; /*
- * Collection of top gaining
- * stocks
- */
- private Collection topLosers; /*
- * Collection of top losing
- * stocks
- */
- // FUTURE private Collection topVolume; /* Collection of top stocks by
- // volume */
- private Date summaryDate; /* Date this summary was taken */
-
- // cache the gainPercent once computed for this bean
- private BigDecimal gainPercent = null;
-
- public MarketSummaryDataBean() {
- }
-
- public MarketSummaryDataBean(BigDecimal TSIA, BigDecimal openTSIA, double volume, Collection topGainers, Collection topLosers// , Collection topVolume
- ) {
- setTSIA(TSIA);
- setOpenTSIA(openTSIA);
- setVolume(volume);
- setTopGainers(topGainers);
- setTopLosers(topLosers);
- setSummaryDate(new java.sql.Date(System.currentTimeMillis()));
- gainPercent = FinancialUtils.computeGainPercent(getTSIA(), getOpenTSIA());
-
- }
-
- public static MarketSummaryDataBean getRandomInstance() {
- Collection gain = new ArrayList();
- Collection lose = new ArrayList();
-
- for (int ii = 0; ii < 5; ii++) {
- QuoteDataBean quote1 = QuoteDataBean.getRandomInstance();
- QuoteDataBean quote2 = QuoteDataBean.getRandomInstance();
-
- gain.add(quote1);
- lose.add(quote2);
- }
-
- return new MarketSummaryDataBean(TradeConfig.rndBigDecimal(1000000.0f), TradeConfig.rndBigDecimal(1000000.0f), TradeConfig.rndQuantity(), gain, lose);
- }
-
- @Override
- public String toString() {
- String ret = "\n\tMarket Summary at: " + getSummaryDate() + "\n\t\t TSIA:" + getTSIA() + "\n\t\t openTSIA:" + getOpenTSIA()
- + "\n\t\t gain:" + getGainPercent() + "\n\t\t volume:" + getVolume();
-
- if ((getTopGainers() == null) || (getTopLosers() == null)) {
- return ret;
- }
- ret += "\n\t\t Current Top Gainers:";
- Iterator it = getTopGainers().iterator();
- while (it.hasNext()) {
- QuoteDataBean quoteData = it.next();
- ret += ("\n\t\t\t" + quoteData.toString());
- }
- ret += "\n\t\t Current Top Losers:";
- it = getTopLosers().iterator();
- while (it.hasNext()) {
- QuoteDataBean quoteData = it.next();
- ret += ("\n\t\t\t" + quoteData.toString());
- }
- return ret;
- }
-
- public String toHTML() {
- String ret = " Market Summary at: " + getSummaryDate() + " TSIA:" + getTSIA() + " " + " openTSIA:" + getOpenTSIA() + " "
- + " volume:" + getVolume() + " ";
- if ((getTopGainers() == null) || (getTopLosers() == null)) {
- return ret;
- }
- ret += " Current Top Gainers:";
- Iterator it = getTopGainers().iterator();
-
- while (it.hasNext()) {
- QuoteDataBean quoteData = it.next();
- ret += ("" + quoteData.toString() + " ");
- }
- ret += " Current Top Losers:";
- it = getTopLosers().iterator();
- while (it.hasNext()) {
- QuoteDataBean quoteData = it.next();
- ret += ("" + quoteData.toString() + " ");
- }
- return ret;
- }
-
- public JsonObject toJSON() {
-
- JsonObjectBuilder jObjectBuilder = Json.createObjectBuilder();
-
- int i = 1;
- for (Iterator iterator = topGainers.iterator(); iterator.hasNext();) {
- QuoteDataBean quote = iterator.next();
-
- jObjectBuilder.add("gainer" + i + "_stock",quote.getSymbol());
- jObjectBuilder.add("gainer" + i + "_price","$" + quote.getPrice());
- jObjectBuilder.add("gainer" + i + "_change",quote.getChange());
- i++;
- }
-
- i = 1;
- for (Iterator iterator = topLosers.iterator(); iterator.hasNext();) {
- QuoteDataBean quote = iterator.next();
-
- jObjectBuilder.add("loser" + i + "_stock",quote.getSymbol());
- jObjectBuilder.add("loser" + i + "_price","$" + quote.getPrice());
- jObjectBuilder.add("loser" + i + "_change",quote.getChange());
- i++;
- }
-
- jObjectBuilder.add("tsia", TSIA);
- jObjectBuilder.add("volume",volume);
- jObjectBuilder.add("date", summaryDate.toString());
-
- return jObjectBuilder.build();
-
- }
-
- public void print() {
- Log.log(this.toString());
- }
-
- public BigDecimal getGainPercent() {
- if (gainPercent == null) {
- gainPercent = FinancialUtils.computeGainPercent(getTSIA(), getOpenTSIA());
- }
- return gainPercent;
- }
-
- /**
- * Gets the tSIA
- *
- * @return Returns a BigDecimal
- */
- public BigDecimal getTSIA() {
- return TSIA;
- }
-
- /**
- * Sets the tSIA
- *
- * @param tSIA
- * The tSIA to set
- */
- public void setTSIA(BigDecimal tSIA) {
- TSIA = tSIA;
- }
-
- /**
- * Gets the openTSIA
- *
- * @return Returns a BigDecimal
- */
- public BigDecimal getOpenTSIA() {
- return openTSIA;
- }
-
- /**
- * Sets the openTSIA
- *
- * @param openTSIA
- * The openTSIA to set
- */
- public void setOpenTSIA(BigDecimal openTSIA) {
- this.openTSIA = openTSIA;
- }
-
- /**
- * Gets the volume
- *
- * @return Returns a BigDecimal
- */
- public double getVolume() {
- return volume;
- }
-
- /**
- * Sets the volume
- *
- * @param volume
- * The volume to set
- */
- public void setVolume(double volume) {
- this.volume = volume;
- }
-
- /**
- * Gets the topGainers
- *
- * @return Returns a Collection
- */
- public Collection getTopGainers() {
- return topGainers;
- }
-
- /**
- * Sets the topGainers
- *
- * @param topGainers
- * The topGainers to set
- */
- public void setTopGainers(Collection topGainers) {
- this.topGainers = topGainers;
- }
-
- /**
- * Gets the topLosers
- *
- * @return Returns a Collection
- */
- public Collection getTopLosers() {
- return topLosers;
- }
-
- /**
- * Sets the topLosers
- *
- * @param topLosers
- * The topLosers to set
- */
- public void setTopLosers(Collection topLosers) {
- this.topLosers = topLosers;
- }
-
- /**
- * Gets the summaryDate
- *
- * @return Returns a Date
- */
- public Date getSummaryDate() {
- return summaryDate;
- }
-
- /**
- * Sets the summaryDate
- *
- * @param summaryDate
- * The summaryDate to set
- */
- public void setSummaryDate(Date summaryDate) {
- this.summaryDate = summaryDate;
- }
-
-}
diff --git a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/beans/RunStatsDataBean.java b/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/beans/RunStatsDataBean.java
deleted file mode 100644
index 1016b6cf..00000000
--- a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/beans/RunStatsDataBean.java
+++ /dev/null
@@ -1,294 +0,0 @@
-/**
- * (C) Copyright IBM Corporation 2015.
- *
- * Licensed 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.
- */
-package com.ibm.websphere.samples.daytrader.beans;
-
-import java.io.Serializable;
-
-public class RunStatsDataBean implements Serializable {
- private static final long serialVersionUID = 4017778674103242167L;
-
- // Constructors
- public RunStatsDataBean() {
- }
-
- // count of trade users in the database (users w/ userID like 'uid:%')
- private int tradeUserCount;
- // count of trade stocks in the database (stocks w/ symbol like 's:%')
- private int tradeStockCount;
-
- // count of new registered users in this run (users w/ userID like 'ru:%')
- // -- random user
- private int newUserCount;
-
- // sum of logins by trade users
- private int sumLoginCount;
- // sum of logouts by trade users
- private int sumLogoutCount;
-
- // count of holdings of trade users
- private int holdingCount;
-
- // count of orders of trade users
- private int orderCount;
- // count of buy orders of trade users
- private int buyOrderCount;
- // count of sell orders of trade users
- private int sellOrderCount;
- // count of cancelled orders of trade users
- private int cancelledOrderCount;
- // count of open orders of trade users
- private int openOrderCount;
- // count of orders deleted during this trade Reset
- private int deletedOrderCount;
-
- @Override
- public String toString() {
- return "\n\tRunStatsData for reset at " + new java.util.Date() + "\n\t\t tradeUserCount: " + getTradeUserCount() + "\n\t\t newUserCount: "
- + getNewUserCount() + "\n\t\t sumLoginCount: " + getSumLoginCount() + "\n\t\t sumLogoutCount: " + getSumLogoutCount()
- + "\n\t\t holdingCount: " + getHoldingCount() + "\n\t\t orderCount: " + getOrderCount() + "\n\t\t buyOrderCount: "
- + getBuyOrderCount() + "\n\t\t sellOrderCount: " + getSellOrderCount() + "\n\t\t cancelledOrderCount: " + getCancelledOrderCount()
- + "\n\t\t openOrderCount: " + getOpenOrderCount() + "\n\t\t deletedOrderCount: " + getDeletedOrderCount();
- }
-
- /**
- * Gets the tradeUserCount
- *
- * @return Returns a int
- */
- public int getTradeUserCount() {
- return tradeUserCount;
- }
-
- /**
- * Sets the tradeUserCount
- *
- * @param tradeUserCount
- * The tradeUserCount to set
- */
- public void setTradeUserCount(int tradeUserCount) {
- this.tradeUserCount = tradeUserCount;
- }
-
- /**
- * Gets the newUserCount
- *
- * @return Returns a int
- */
- public int getNewUserCount() {
- return newUserCount;
- }
-
- /**
- * Sets the newUserCount
- *
- * @param newUserCount
- * The newUserCount to set
- */
- public void setNewUserCount(int newUserCount) {
- this.newUserCount = newUserCount;
- }
-
- /**
- * Gets the sumLoginCount
- *
- * @return Returns a int
- */
- public int getSumLoginCount() {
- return sumLoginCount;
- }
-
- /**
- * Sets the sumLoginCount
- *
- * @param sumLoginCount
- * The sumLoginCount to set
- */
- public void setSumLoginCount(int sumLoginCount) {
- this.sumLoginCount = sumLoginCount;
- }
-
- /**
- * Gets the sumLogoutCount
- *
- * @return Returns a int
- */
- public int getSumLogoutCount() {
- return sumLogoutCount;
- }
-
- /**
- * Sets the sumLogoutCount
- *
- * @param sumLogoutCount
- * The sumLogoutCount to set
- */
- public void setSumLogoutCount(int sumLogoutCount) {
- this.sumLogoutCount = sumLogoutCount;
- }
-
- /**
- * Gets the holdingCount
- *
- * @return Returns a int
- */
- public int getHoldingCount() {
- return holdingCount;
- }
-
- /**
- * Sets the holdingCount
- *
- * @param holdingCount
- * The holdingCount to set
- */
- public void setHoldingCount(int holdingCount) {
- this.holdingCount = holdingCount;
- }
-
- /**
- * Gets the buyOrderCount
- *
- * @return Returns a int
- */
- public int getBuyOrderCount() {
- return buyOrderCount;
- }
-
- /**
- * Sets the buyOrderCount
- *
- * @param buyOrderCount
- * The buyOrderCount to set
- */
- public void setBuyOrderCount(int buyOrderCount) {
- this.buyOrderCount = buyOrderCount;
- }
-
- /**
- * Gets the sellOrderCount
- *
- * @return Returns a int
- */
- public int getSellOrderCount() {
- return sellOrderCount;
- }
-
- /**
- * Sets the sellOrderCount
- *
- * @param sellOrderCount
- * The sellOrderCount to set
- */
- public void setSellOrderCount(int sellOrderCount) {
- this.sellOrderCount = sellOrderCount;
- }
-
- /**
- * Gets the cancelledOrderCount
- *
- * @return Returns a int
- */
- public int getCancelledOrderCount() {
- return cancelledOrderCount;
- }
-
- /**
- * Sets the cancelledOrderCount
- *
- * @param cancelledOrderCount
- * The cancelledOrderCount to set
- */
- public void setCancelledOrderCount(int cancelledOrderCount) {
- this.cancelledOrderCount = cancelledOrderCount;
- }
-
- /**
- * Gets the openOrderCount
- *
- * @return Returns a int
- */
- public int getOpenOrderCount() {
- return openOrderCount;
- }
-
- /**
- * Sets the openOrderCount
- *
- * @param openOrderCount
- * The openOrderCount to set
- */
- public void setOpenOrderCount(int openOrderCount) {
- this.openOrderCount = openOrderCount;
- }
-
- /**
- * Gets the deletedOrderCount
- *
- * @return Returns a int
- */
- public int getDeletedOrderCount() {
- return deletedOrderCount;
- }
-
- /**
- * Sets the deletedOrderCount
- *
- * @param deletedOrderCount
- * The deletedOrderCount to set
- */
- public void setDeletedOrderCount(int deletedOrderCount) {
- this.deletedOrderCount = deletedOrderCount;
- }
-
- /**
- * Gets the orderCount
- *
- * @return Returns a int
- */
- public int getOrderCount() {
- return orderCount;
- }
-
- /**
- * Sets the orderCount
- *
- * @param orderCount
- * The orderCount to set
- */
- public void setOrderCount(int orderCount) {
- this.orderCount = orderCount;
- }
-
- /**
- * Gets the tradeStockCount
- *
- * @return Returns a int
- */
- public int getTradeStockCount() {
- return tradeStockCount;
- }
-
- /**
- * Sets the tradeStockCount
- *
- * @param tradeStockCount
- * The tradeStockCount to set
- */
- public void setTradeStockCount(int tradeStockCount) {
- this.tradeStockCount = tradeStockCount;
- }
-
-}
diff --git a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/entities/AccountDataBean.java b/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/entities/AccountDataBean.java
deleted file mode 100644
index 00c96fd3..00000000
--- a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/entities/AccountDataBean.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/**
- * (C) Copyright IBM Corporation 2015.
- *
- * Licensed 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.
- */
-package com.ibm.websphere.samples.daytrader.entities;
-
-import com.ibm.websphere.samples.daytrader.util.Log;
-import com.ibm.websphere.samples.daytrader.util.TradeConfig;
-import java.io.Serializable;
-import java.math.BigDecimal;
-import java.sql.Timestamp;
-import java.util.Collection;
-import java.util.Date;
-import javax.ejb.EJBException;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.JoinColumn;
-import javax.persistence.OneToMany;
-import javax.persistence.OneToOne;
-import javax.persistence.Table;
-import javax.persistence.TableGenerator;
-import javax.persistence.Temporal;
-import javax.persistence.TemporalType;
-import javax.persistence.Transient;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.PastOrPresent;
-import javax.validation.constraints.PositiveOrZero;
-
-@Entity(name = "accountejb")
-@Table(name = "accountejb")
-public class AccountDataBean implements Serializable {
-
- private static final long serialVersionUID = 8437841265136840545L;
-
- /* Accessor methods for persistent fields */
- @TableGenerator(name = "accountIdGen", table = "KEYGENEJB", pkColumnName = "KEYNAME", valueColumnName = "KEYVAL", pkColumnValue = "account", allocationSize = 1000)
- @Id
- @GeneratedValue(strategy = GenerationType.TABLE, generator = "accountIdGen")
- @Column(name = "ACCOUNTID", nullable = false)
- private Integer accountID; /* accountID */
-
- @NotNull
- @PositiveOrZero
- @Column(name = "LOGINCOUNT", nullable = false)
- private int loginCount; /* loginCount */
-
- @NotNull
- @PositiveOrZero
- @Column(name = "LOGOUTCOUNT", nullable = false)
- private int logoutCount; /* logoutCount */
-
- @Column(name = "LASTLOGIN")
- @Temporal(TemporalType.TIMESTAMP)
- @PastOrPresent
- private Date lastLogin; /* lastLogin Date */
-
- @Column(name = "CREATIONDATE")
- @Temporal(TemporalType.TIMESTAMP)
- @PastOrPresent
- private Date creationDate; /* creationDate */
-
- @Column(name = "BALANCE")
- private BigDecimal balance; /* balance */
-
- @Column(name = "OPENBALANCE")
- private BigDecimal openBalance; /* open balance */
-
- @OneToMany(mappedBy = "account", fetch = FetchType.LAZY)
- private Collection orders;
-
- @OneToMany(mappedBy = "account", fetch = FetchType.LAZY)
- private Collection holdings;
-
- @OneToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "PROFILE_USERID")
- private AccountProfileDataBean profile;
-
- /*
- * Accessor methods for relationship fields are only included for the
- * AccountProfile profileID
- */
- @Transient
- private String profileID;
-
- public AccountDataBean() {
- }
-
- public AccountDataBean(Integer accountID, int loginCount, int logoutCount, Date lastLogin, Date creationDate, BigDecimal balance, BigDecimal openBalance,
- String profileID) {
- setAccountID(accountID);
- setLoginCount(loginCount);
- setLogoutCount(logoutCount);
- setLastLogin(lastLogin);
- setCreationDate(creationDate);
- setBalance(balance);
- setOpenBalance(openBalance);
- setProfileID(profileID);
- }
-
- public AccountDataBean(int loginCount, int logoutCount, Date lastLogin, Date creationDate, BigDecimal balance, BigDecimal openBalance, String profileID) {
- setLoginCount(loginCount);
- setLogoutCount(logoutCount);
- setLastLogin(lastLogin);
- setCreationDate(creationDate);
- setBalance(balance);
- setOpenBalance(openBalance);
- setProfileID(profileID);
- }
-
- public static AccountDataBean getRandomInstance() {
- return new AccountDataBean(new Integer(TradeConfig.rndInt(100000)), // accountID
- TradeConfig.rndInt(10000), // loginCount
- TradeConfig.rndInt(10000), // logoutCount
- new java.util.Date(), // lastLogin
- new java.util.Date(TradeConfig.rndInt(Integer.MAX_VALUE)), // creationDate
- TradeConfig.rndBigDecimal(1000000.0f), // balance
- TradeConfig.rndBigDecimal(1000000.0f), // openBalance
- TradeConfig.rndUserID() // profileID
- );
- }
-
- @Override
- public String toString() {
- return "\n\tAccount Data for account: " + getAccountID() + "\n\t\t loginCount:" + getLoginCount() + "\n\t\t logoutCount:" + getLogoutCount()
- + "\n\t\t lastLogin:" + getLastLogin() + "\n\t\t creationDate:" + getCreationDate() + "\n\t\t balance:" + getBalance()
- + "\n\t\t openBalance:" + getOpenBalance() + "\n\t\t profileID:" + getProfileID();
- }
-
- public String toHTML() {
- return " Account Data for account: " + getAccountID() + " " + " loginCount:" + getLoginCount() + " " + " logoutCount:"
- + getLogoutCount() + " " + " lastLogin:" + getLastLogin() + " " + " creationDate:" + getCreationDate() + " "
- + " balance:" + getBalance() + " " + " openBalance:" + getOpenBalance() + " " + " profileID:" + getProfileID()
- + " ";
- }
-
- public void print() {
- Log.log(this.toString());
- }
-
- public Integer getAccountID() {
- return accountID;
- }
-
- public void setAccountID(Integer accountID) {
- this.accountID = accountID;
- }
-
- public int getLoginCount() {
- return loginCount;
- }
-
- public void setLoginCount(int loginCount) {
- this.loginCount = loginCount;
- }
-
- public int getLogoutCount() {
- return logoutCount;
- }
-
- public void setLogoutCount(int logoutCount) {
- this.logoutCount = logoutCount;
- }
-
- public Date getLastLogin() {
- return lastLogin;
- }
-
- public void setLastLogin(Date lastLogin) {
- this.lastLogin = lastLogin;
- }
-
- public Date getCreationDate() {
- return creationDate;
- }
-
- public void setCreationDate(Date creationDate) {
- this.creationDate = creationDate;
- }
-
- public BigDecimal getBalance() {
- return balance;
- }
-
- public void setBalance(BigDecimal balance) {
- this.balance = balance;
- }
-
- public BigDecimal getOpenBalance() {
- return openBalance;
- }
-
- public void setOpenBalance(BigDecimal openBalance) {
- this.openBalance = openBalance;
- }
-
- public String getProfileID() {
- return profileID;
- }
-
- public void setProfileID(String profileID) {
- this.profileID = profileID;
- }
-
- /*
- * Disabled for D185273 public String getUserID() { return getProfileID(); }
- */
-
- public Collection getOrders() {
- return orders;
- }
-
- public void setOrders(Collection orders) {
- this.orders = orders;
- }
-
- public Collection getHoldings() {
- return holdings;
- }
-
- public void setHoldings(Collection holdings) {
- this.holdings = holdings;
- }
-
- public AccountProfileDataBean getProfile() {
- return profile;
- }
-
- public void setProfile(AccountProfileDataBean profile) {
- this.profile = profile;
- }
-
- public void login(String password) {
- AccountProfileDataBean profile = getProfile();
- if ((profile == null) || (profile.getPassword().equals(password) == false)) {
- String error = "AccountBean:Login failure for account: " + getAccountID()
- + ((profile == null) ? "null AccountProfile" : "\n\tIncorrect password-->" + profile.getUserID() + ":" + profile.getPassword());
- throw new EJBException(error);
- }
-
- setLastLogin(new Timestamp(System.currentTimeMillis()));
- setLoginCount(getLoginCount() + 1);
- }
-
- public void logout() {
- setLogoutCount(getLogoutCount() + 1);
- }
-
- @Override
- public int hashCode() {
- int hash = 0;
- hash += (this.accountID != null ? this.accountID.hashCode() : 0);
- return hash;
- }
-
- @Override
- public boolean equals(Object object) {
-
- if (!(object instanceof AccountDataBean)) {
- return false;
- }
- AccountDataBean other = (AccountDataBean) object;
-
- if (this.accountID != other.accountID && (this.accountID == null || !this.accountID.equals(other.accountID))) {
- return false;
- }
-
- return true;
- }
-}
diff --git a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/entities/AccountProfileDataBean.java b/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/entities/AccountProfileDataBean.java
deleted file mode 100644
index 9da49780..00000000
--- a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/entities/AccountProfileDataBean.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/**
- * (C) Copyright IBM Corporation 2015.
- *
- * Licensed 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.
- */
-package com.ibm.websphere.samples.daytrader.entities;
-
-//import java.sql.Timestamp;
-
-import com.ibm.websphere.samples.daytrader.util.Log;
-import com.ibm.websphere.samples.daytrader.util.TradeConfig;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.Id;
-import javax.persistence.OneToOne;
-import javax.persistence.Table;
-import javax.validation.constraints.Email;
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-
-@Entity(name = "accountprofileejb")
-@Table(name = "accountprofileejb")
-public class AccountProfileDataBean implements java.io.Serializable {
-
- /* Accessor methods for persistent fields */
-
- private static final long serialVersionUID = 2794584136675420624L;
-
- @Id
- @NotNull
- @Column(name = "USERID", nullable = false)
- private String userID; /* userID */
-
- @Column(name = "PASSWD")
- @NotBlank
- private String passwd; /* password */
-
- @Column(name = "FULLNAME")
- @NotBlank
- private String fullName; /* fullName */
-
- @Column(name = "ADDRESS")
- @NotBlank
- private String address; /* address */
-
- @Column(name = "EMAIL")
- @Email(message = "Email should be valid")
- private String email; /* email */
-
- @Column(name = "CREDITCARD")
- @NotBlank
- private String creditCard; /* creditCard */
-
- @OneToOne(mappedBy = "profile", fetch = FetchType.LAZY)
- private AccountDataBean account;
-
- public AccountProfileDataBean() {
- }
-
- public AccountProfileDataBean(String userID, String password, String fullName, String address, String email, String creditCard) {
- setUserID(userID);
- setPassword(password);
- setFullName(fullName);
- setAddress(address);
- setEmail(email);
- setCreditCard(creditCard);
- }
-
- public static AccountProfileDataBean getRandomInstance() {
- return new AccountProfileDataBean(TradeConfig.rndUserID(), // userID
- TradeConfig.rndUserID(), // passwd
- TradeConfig.rndFullName(), // fullname
- TradeConfig.rndAddress(), // address
- TradeConfig.rndEmail(TradeConfig.rndUserID()), // email
- TradeConfig.rndCreditCard() // creditCard
- );
- }
-
- @Override
- public String toString() {
- return "\n\tAccount Profile Data for userID:" + getUserID() + "\n\t\t passwd:" + getPassword() + "\n\t\t fullName:" + getFullName()
- + "\n\t\t address:" + getAddress() + "\n\t\t email:" + getEmail() + "\n\t\t creditCard:" + getCreditCard();
- }
-
- public String toHTML() {
- return " Account Profile Data for userID: " + getUserID() + " " + " passwd:" + getPassword() + " " + " fullName:"
- + getFullName() + " " + " address:" + getAddress() + " " + " email:" + getEmail() + " " + " creditCard:"
- + getCreditCard() + " ";
- }
-
- public void print() {
- Log.log(this.toString());
- }
-
- public String getUserID() {
- return userID;
- }
-
- public void setUserID(String userID) {
- this.userID = userID;
- }
-
- public String getPassword() {
- return passwd;
- }
-
- public void setPassword(String password) {
- this.passwd = password;
- }
-
- public String getFullName() {
- return fullName;
- }
-
- public void setFullName(String fullName) {
- this.fullName = fullName;
- }
-
- public String getAddress() {
- return address;
- }
-
- public void setAddress(String address) {
- this.address = address;
- }
-
- public String getEmail() {
- return email;
- }
-
- public void setEmail(String email) {
- this.email = email;
- }
-
- public String getCreditCard() {
- return creditCard;
- }
-
- public void setCreditCard(String creditCard) {
- this.creditCard = creditCard;
- }
-
- public AccountDataBean getAccount() {
- return account;
- }
-
- public void setAccount(AccountDataBean account) {
- this.account = account;
- }
-
- @Override
- public int hashCode() {
- int hash = 0;
- hash += (this.userID != null ? this.userID.hashCode() : 0);
- return hash;
- }
-
- @Override
- public boolean equals(Object object) {
-
- if (!(object instanceof AccountProfileDataBean)) {
- return false;
- }
- AccountProfileDataBean other = (AccountProfileDataBean) object;
-
- if (this.userID != other.userID && (this.userID == null || !this.userID.equals(other.userID))) {
- return false;
- }
-
- return true;
- }
-}
diff --git a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/entities/HoldingDataBean.java b/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/entities/HoldingDataBean.java
deleted file mode 100644
index 703da644..00000000
--- a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/entities/HoldingDataBean.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/**
- * (C) Copyright IBM Corporation 2015.
- *
- * Licensed 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.
- */
-package com.ibm.websphere.samples.daytrader.entities;
-
-import com.ibm.websphere.samples.daytrader.util.Log;
-import com.ibm.websphere.samples.daytrader.util.TradeConfig;
-import java.io.Serializable;
-import java.math.BigDecimal;
-import java.util.Date;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.JoinColumn;
-import javax.persistence.ManyToOne;
-import javax.persistence.Table;
-import javax.persistence.TableGenerator;
-import javax.persistence.Temporal;
-import javax.persistence.TemporalType;
-import javax.persistence.Transient;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.PastOrPresent;
-import javax.validation.constraints.Positive;
-
-@Entity(name = "holdingejb")
-@Table(name = "holdingejb")
-public class HoldingDataBean implements Serializable {
-
- /* persistent/relationship fields */
-
- private static final long serialVersionUID = -2338411656251935480L;
-
- @Id
- @TableGenerator(name = "holdingIdGen", table = "KEYGENEJB", pkColumnName = "KEYNAME", valueColumnName = "KEYVAL", pkColumnValue = "holding", allocationSize = 1000)
- @GeneratedValue(strategy = GenerationType.TABLE, generator = "holdingIdGen")
- @Column(name = "HOLDINGID", nullable = false)
- private Integer holdingID; /* holdingID */
-
- @NotNull
- @Positive
- @Column(name = "QUANTITY", nullable = false)
- private double quantity; /* quantity */
-
- @Column(name = "PURCHASEPRICE")
- @Positive
- private BigDecimal purchasePrice; /* purchasePrice */
-
- @Column(name = "PURCHASEDATE")
- @Temporal(TemporalType.TIMESTAMP)
- @PastOrPresent
- private Date purchaseDate; /* purchaseDate */
-
- @Transient
- private String quoteID; /* Holding(*) ---> Quote(1) */
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "ACCOUNT_ACCOUNTID")
- private AccountDataBean account;
-
- @ManyToOne(fetch = FetchType.EAGER)
- @JoinColumn(name = "QUOTE_SYMBOL")
- private QuoteDataBean quote;
-
- public HoldingDataBean() {
- }
-
- public HoldingDataBean(Integer holdingID, double quantity, BigDecimal purchasePrice, Date purchaseDate, String quoteID) {
- setHoldingID(holdingID);
- setQuantity(quantity);
- setPurchasePrice(purchasePrice);
- setPurchaseDate(purchaseDate);
- setQuoteID(quoteID);
- }
-
- public HoldingDataBean(double quantity, BigDecimal purchasePrice, Date purchaseDate, AccountDataBean account, QuoteDataBean quote) {
- setQuantity(quantity);
- setPurchasePrice(purchasePrice);
- setPurchaseDate(purchaseDate);
- setAccount(account);
- setQuote(quote);
- }
-
- public static HoldingDataBean getRandomInstance() {
- return new HoldingDataBean(new Integer(TradeConfig.rndInt(100000)), // holdingID
- TradeConfig.rndQuantity(), // quantity
- TradeConfig.rndBigDecimal(1000.0f), // purchasePrice
- new java.util.Date(TradeConfig.rndInt(Integer.MAX_VALUE)), // purchaseDate
- TradeConfig.rndSymbol() // symbol
- );
- }
-
- @Override
- public String toString() {
- return "\n\tHolding Data for holding: " + getHoldingID() + "\n\t\t quantity:" + getQuantity() + "\n\t\t purchasePrice:" + getPurchasePrice()
- + "\n\t\t purchaseDate:" + getPurchaseDate() + "\n\t\t quoteID:" + getQuoteID();
- }
-
- public String toHTML() {
- return " Holding Data for holding: " + getHoldingID() + "" + " quantity:" + getQuantity() + " " + " purchasePrice:"
- + getPurchasePrice() + " " + " purchaseDate:" + getPurchaseDate() + " " + " quoteID:" + getQuoteID() + " ";
- }
-
- public void print() {
- Log.log(this.toString());
- }
-
- public Integer getHoldingID() {
- return holdingID;
- }
-
- public void setHoldingID(Integer holdingID) {
- this.holdingID = holdingID;
- }
-
- public double getQuantity() {
- return quantity;
- }
-
- public void setQuantity(double quantity) {
- this.quantity = quantity;
- }
-
- public BigDecimal getPurchasePrice() {
- return purchasePrice;
- }
-
- public void setPurchasePrice(BigDecimal purchasePrice) {
- this.purchasePrice = purchasePrice;
- }
-
- public Date getPurchaseDate() {
- return purchaseDate;
- }
-
- public void setPurchaseDate(Date purchaseDate) {
- this.purchaseDate = purchaseDate;
- }
-
- public String getQuoteID() {
- if (quote != null) {
- return quote.getSymbol();
- }
- return quoteID;
- }
-
- public void setQuoteID(String quoteID) {
- this.quoteID = quoteID;
- }
-
- public AccountDataBean getAccount() {
- return account;
- }
-
- public void setAccount(AccountDataBean account) {
- this.account = account;
- }
-
- public QuoteDataBean getQuote() {
- return quote;
- }
-
- public void setQuote(QuoteDataBean quote) {
- this.quote = quote;
- }
-
- @Override
- public int hashCode() {
- int hash = 0;
- hash += (this.holdingID != null ? this.holdingID.hashCode() : 0);
- return hash;
- }
-
- @Override
- public boolean equals(Object object) {
-
- if (!(object instanceof HoldingDataBean)) {
- return false;
- }
- HoldingDataBean other = (HoldingDataBean) object;
-
- if (this.holdingID != other.holdingID && (this.holdingID == null || !this.holdingID.equals(other.holdingID))) {
- return false;
- }
-
- return true;
- }
-}
diff --git a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/entities/OrderDataBean.java b/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/entities/OrderDataBean.java
deleted file mode 100644
index 040ca66a..00000000
--- a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/entities/OrderDataBean.java
+++ /dev/null
@@ -1,336 +0,0 @@
-/**
- * (C) Copyright IBM Corporation 2015.
- *
- * Licensed 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.
- */
-package com.ibm.websphere.samples.daytrader.entities;
-
-import com.ibm.websphere.samples.daytrader.util.Log;
-import com.ibm.websphere.samples.daytrader.util.TradeConfig;
-import java.io.Serializable;
-import java.math.BigDecimal;
-import java.util.Date;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.JoinColumn;
-import javax.persistence.ManyToOne;
-import javax.persistence.NamedQueries;
-import javax.persistence.NamedQuery;
-import javax.persistence.OneToOne;
-import javax.persistence.Table;
-import javax.persistence.TableGenerator;
-import javax.persistence.Temporal;
-import javax.persistence.TemporalType;
-import javax.persistence.Transient;
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.PastOrPresent;
-import javax.validation.constraints.Positive;
-
-@Entity(name = "orderejb")
-@Table(name = "orderejb")
-@NamedQueries({
- @NamedQuery(name = "orderejb.findByOrderfee", query = "SELECT o FROM orderejb o WHERE o.orderFee = :orderfee"),
- @NamedQuery(name = "orderejb.findByCompletiondate", query = "SELECT o FROM orderejb o WHERE o.completionDate = :completiondate"),
- @NamedQuery(name = "orderejb.findByOrdertype", query = "SELECT o FROM orderejb o WHERE o.orderType = :ordertype"),
- @NamedQuery(name = "orderejb.findByOrderstatus", query = "SELECT o FROM orderejb o WHERE o.orderStatus = :orderstatus"),
- @NamedQuery(name = "orderejb.findByPrice", query = "SELECT o FROM orderejb o WHERE o.price = :price"),
- @NamedQuery(name = "orderejb.findByQuantity", query = "SELECT o FROM orderejb o WHERE o.quantity = :quantity"),
- @NamedQuery(name = "orderejb.findByOpendate", query = "SELECT o FROM orderejb o WHERE o.openDate = :opendate"),
- @NamedQuery(name = "orderejb.findByOrderid", query = "SELECT o FROM orderejb o WHERE o.orderID = :orderid"),
- @NamedQuery(name = "orderejb.findByAccountAccountid", query = "SELECT o FROM orderejb o WHERE o.account.accountID = :accountAccountid"),
- @NamedQuery(name = "orderejb.findByQuoteSymbol", query = "SELECT o FROM orderejb o WHERE o.quote.symbol = :quoteSymbol"),
- @NamedQuery(name = "orderejb.findByHoldingHoldingid", query = "SELECT o FROM orderejb o WHERE o.holding.holdingID = :holdingHoldingid"),
- @NamedQuery(name = "orderejb.closedOrders", query = "SELECT o FROM orderejb o WHERE o.orderStatus = 'closed' AND o.account.profile.userID = :userID"),
- @NamedQuery(name = "orderejb.completeClosedOrders", query = "UPDATE orderejb o SET o.orderStatus = 'completed' WHERE o.orderStatus = 'closed' AND o.account.profile.userID = :userID") })
-public class OrderDataBean implements Serializable {
-
- private static final long serialVersionUID = 120650490200739057L;
-
- @Id
- @TableGenerator(name = "orderIdGen", table = "KEYGENEJB", pkColumnName = "KEYNAME", valueColumnName = "KEYVAL", pkColumnValue = "order", allocationSize = 1000)
- @GeneratedValue(strategy = GenerationType.TABLE, generator = "orderIdGen")
- @Column(name = "ORDERID", nullable = false)
- private Integer orderID; /* orderID */
-
- @Column(name = "ORDERTYPE")
- @NotBlank
- private String orderType; /* orderType (buy, sell, etc.) */
-
- @Column(name = "ORDERSTATUS")
- @NotBlank
- private String orderStatus; /*
- * orderStatus (open, processing, completed,
- * closed, cancelled)
- */
-
- @Column(name = "OPENDATE")
- @Temporal(TemporalType.TIMESTAMP)
- @PastOrPresent
- private Date openDate; /* openDate (when the order was entered) */
-
- @Column(name = "COMPLETIONDATE")
- @PastOrPresent
- @Temporal(TemporalType.TIMESTAMP)
- private Date completionDate; /* completionDate */
-
- @NotNull
- @Column(name = "QUANTITY", nullable = false)
- private double quantity; /* quantity */
-
- @Column(name = "PRICE")
- @Positive
- private BigDecimal price; /* price */
-
- @Column(name = "ORDERFEE")
- @Positive
- private BigDecimal orderFee; /* price */
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "ACCOUNT_ACCOUNTID")
- private AccountDataBean account;
-
- @ManyToOne(fetch = FetchType.EAGER)
- @JoinColumn(name = "QUOTE_SYMBOL")
- private QuoteDataBean quote;
-
- @OneToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "HOLDING_HOLDINGID")
- private HoldingDataBean holding;
-
- /* Fields for relationship fields are not kept in the Data Bean */
- @Transient
- private String symbol;
-
- public OrderDataBean() {
- }
-
- public OrderDataBean(Integer orderID, String orderType, String orderStatus, Date openDate, Date completionDate, double quantity, BigDecimal price,
- BigDecimal orderFee, String symbol) {
- setOrderID(orderID);
- setOrderType(orderType);
- setOrderStatus(orderStatus);
- setOpenDate(openDate);
- setCompletionDate(completionDate);
- setQuantity(quantity);
- setPrice(price);
- setOrderFee(orderFee);
- setSymbol(symbol);
- }
-
- public OrderDataBean(String orderType, String orderStatus, Date openDate, Date completionDate, double quantity, BigDecimal price, BigDecimal orderFee,
- AccountDataBean account, QuoteDataBean quote, HoldingDataBean holding) {
- setOrderType(orderType);
- setOrderStatus(orderStatus);
- setOpenDate(openDate);
- setCompletionDate(completionDate);
- setQuantity(quantity);
- setPrice(price);
- setOrderFee(orderFee);
- setAccount(account);
- setQuote(quote);
- setHolding(holding);
- }
-
- public static OrderDataBean getRandomInstance() {
- return new OrderDataBean(new Integer(TradeConfig.rndInt(100000)), TradeConfig.rndBoolean() ? "buy" : "sell", "open", new java.util.Date(
- TradeConfig.rndInt(Integer.MAX_VALUE)), new java.util.Date(TradeConfig.rndInt(Integer.MAX_VALUE)), TradeConfig.rndQuantity(),
- TradeConfig.rndBigDecimal(1000.0f), TradeConfig.rndBigDecimal(1000.0f), TradeConfig.rndSymbol());
- }
-
- @Override
- public String toString() {
- return "Order " + getOrderID() + "\n\t orderType: " + getOrderType() + "\n\t orderStatus: " + getOrderStatus() + "\n\t openDate: "
- + getOpenDate() + "\n\t completionDate: " + getCompletionDate() + "\n\t quantity: " + getQuantity() + "\n\t price: "
- + getPrice() + "\n\t orderFee: " + getOrderFee() + "\n\t symbol: " + getSymbol();
- }
-
- public String toHTML() {
- return " Order " + getOrderID() + " " + " orderType: " + getOrderType() + " " + " orderStatus: " + getOrderStatus()
- + " " + " openDate: " + getOpenDate() + " " + " completionDate: " + getCompletionDate() + " "
- + " quantity: " + getQuantity() + " " + " price: " + getPrice() + " " + " orderFee: " + getOrderFee()
- + " " + " symbol: " + getSymbol() + " ";
- }
-
- public void print() {
- Log.log(this.toString());
- }
-
- public Integer getOrderID() {
- return orderID;
- }
-
- public void setOrderID(Integer orderID) {
- this.orderID = orderID;
- }
-
- public String getOrderType() {
- return orderType;
- }
-
- public void setOrderType(String orderType) {
- this.orderType = orderType;
- }
-
- public String getOrderStatus() {
- return orderStatus;
- }
-
- public void setOrderStatus(String orderStatus) {
- this.orderStatus = orderStatus;
- }
-
- public Date getOpenDate() {
- return openDate;
- }
-
- public void setOpenDate(Date openDate) {
- this.openDate = openDate;
- }
-
- public Date getCompletionDate() {
- return completionDate;
- }
-
- public void setCompletionDate(Date completionDate) {
- this.completionDate = completionDate;
- }
-
- public double getQuantity() {
- return quantity;
- }
-
- public void setQuantity(double quantity) {
- this.quantity = quantity;
- }
-
- public BigDecimal getPrice() {
- return price;
- }
-
- public void setPrice(BigDecimal price) {
- this.price = price;
- }
-
- public BigDecimal getOrderFee() {
- return orderFee;
- }
-
- public void setOrderFee(BigDecimal orderFee) {
- this.orderFee = orderFee;
- }
-
- public String getSymbol() {
- if (quote != null) {
- return quote.getSymbol();
- }
- return symbol;
- }
-
- public void setSymbol(String symbol) {
- this.symbol = symbol;
- }
-
- public AccountDataBean getAccount() {
- return account;
- }
-
- public void setAccount(AccountDataBean account) {
- this.account = account;
- }
-
- public QuoteDataBean getQuote() {
- return quote;
- }
-
- public void setQuote(QuoteDataBean quote) {
- this.quote = quote;
- }
-
- public HoldingDataBean getHolding() {
- return holding;
- }
-
- public void setHolding(HoldingDataBean holding) {
- this.holding = holding;
- }
-
- public boolean isBuy() {
- String orderType = getOrderType();
- if (orderType.compareToIgnoreCase("buy") == 0) {
- return true;
- }
- return false;
- }
-
- public boolean isSell() {
- String orderType = getOrderType();
- if (orderType.compareToIgnoreCase("sell") == 0) {
- return true;
- }
- return false;
- }
-
- public boolean isOpen() {
- String orderStatus = getOrderStatus();
- if ((orderStatus.compareToIgnoreCase("open") == 0) || (orderStatus.compareToIgnoreCase("processing") == 0)) {
- return true;
- }
- return false;
- }
-
- public boolean isCompleted() {
- String orderStatus = getOrderStatus();
- if ((orderStatus.compareToIgnoreCase("completed") == 0) || (orderStatus.compareToIgnoreCase("alertcompleted") == 0)
- || (orderStatus.compareToIgnoreCase("cancelled") == 0)) {
- return true;
- }
- return false;
- }
-
- public boolean isCancelled() {
- String orderStatus = getOrderStatus();
- if (orderStatus.compareToIgnoreCase("cancelled") == 0) {
- return true;
- }
- return false;
- }
-
- public void cancel() {
- setOrderStatus("cancelled");
- }
-
- @Override
- public int hashCode() {
- int hash = 0;
- hash += (this.orderID != null ? this.orderID.hashCode() : 0);
- return hash;
- }
-
- @Override
- public boolean equals(Object object) {
-
- if (!(object instanceof OrderDataBean)) {
- return false;
- }
- OrderDataBean other = (OrderDataBean) object;
- if (this.orderID != other.orderID && (this.orderID == null || !this.orderID.equals(other.orderID))) {
- return false;
- }
- return true;
- }
-}
diff --git a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/entities/QuoteDataBean.java b/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/entities/QuoteDataBean.java
deleted file mode 100644
index 6db26713..00000000
--- a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/entities/QuoteDataBean.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/**
- * (C) Copyright IBM Corporation 2015.
- *
- * Licensed 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.
- */
-package com.ibm.websphere.samples.daytrader.entities;
-
-import com.ibm.websphere.samples.daytrader.util.Log;
-import com.ibm.websphere.samples.daytrader.util.TradeConfig;
-import java.io.Serializable;
-import java.math.BigDecimal;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.Id;
-import javax.persistence.NamedNativeQueries;
-import javax.persistence.NamedNativeQuery;
-import javax.persistence.NamedQueries;
-import javax.persistence.NamedQuery;
-import javax.persistence.Table;
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Positive;
-
-@Entity(name = "quoteejb")
-@Table(name = "quoteejb")
-@NamedQueries({
- @NamedQuery(name = "quoteejb.allQuotes", query = "SELECT q FROM quoteejb q")})
-@NamedNativeQueries({ @NamedNativeQuery(name = "quoteejb.quoteForUpdate", query = "select * from quoteejb q where q.symbol=? for update", resultClass = com.ibm.websphere.samples.daytrader.entities.QuoteDataBean.class) })
-public class QuoteDataBean implements Serializable {
-
- /* Accessor methods for persistent fields */
-
- private static final long serialVersionUID = 1847932261895838791L;
-
- @Id
- @NotNull
- @Column(name = "SYMBOL", nullable = false)
- private String symbol; /* symbol */
-
- @Column(name = "COMPANYNAME")
- @NotBlank
- private String companyName; /* companyName */
-
- @NotNull
- @Column(name = "VOLUME", nullable = false)
- private double volume; /* volume */
-
- @Column(name = "PRICE")
- @Positive
- private BigDecimal price; /* price */
-
- @Column(name = "OPEN1")
- @Positive
- private BigDecimal open1; /* open1 price */
-
- @Column(name = "LOW")
- @Positive
- private BigDecimal low; /* low price */
-
- @Column(name = "HIGH")
- @Positive
- private BigDecimal high; /* high price */
-
- @NotNull
- @Column(name = "CHANGE1", nullable = false)
- private double change1; /* price change */
-
- /* Accessor methods for relationship fields are not kept in the DataBean */
-
- public QuoteDataBean() {
- }
-
- public QuoteDataBean(String symbol, String companyName, double volume, BigDecimal price, BigDecimal open, BigDecimal low, BigDecimal high, double change) {
- setSymbol(symbol);
- setCompanyName(companyName);
- setVolume(volume);
- setPrice(price);
- setOpen(open);
- setLow(low);
- setHigh(high);
- setChange(change);
- }
-
- public static QuoteDataBean getRandomInstance() {
- return new QuoteDataBean(TradeConfig.rndSymbol(), // symbol
- TradeConfig.rndSymbol() + " Incorporated", // Company Name
- TradeConfig.rndFloat(100000), // volume
- TradeConfig.rndBigDecimal(1000.0f), // price
- TradeConfig.rndBigDecimal(1000.0f), // open1
- TradeConfig.rndBigDecimal(1000.0f), // low
- TradeConfig.rndBigDecimal(1000.0f), // high
- TradeConfig.rndFloat(100000) // volume
- );
- }
-
- // Create a "zero" value quoteDataBean for the given symbol
- public QuoteDataBean(String symbol) {
- setSymbol(symbol);
- }
-
- @Override
- public String toString() {
- return "\n\tQuote Data for: " + getSymbol() + "\n\t\t companyName: " + getCompanyName() + "\n\t\t volume: " + getVolume() + "\n\t\t price: "
- + getPrice() + "\n\t\t open1: " + getOpen() + "\n\t\t low: " + getLow() + "\n\t\t high: " + getHigh()
- + "\n\t\t change1: " + getChange();
- }
-
- public String toHTML() {
- return " Quote Data for: " + getSymbol() + " companyName: " + getCompanyName() + " " + " volume: " + getVolume() + " "
- + " price: " + getPrice() + " " + " open1: " + getOpen() + " " + " low: " + getLow() + " "
- + " high: " + getHigh() + " " + " change1: " + getChange() + " ";
- }
-
- public void print() {
- Log.log(this.toString());
- }
-
- public String getSymbol() {
- return symbol;
- }
-
- public void setSymbol(String symbol) {
- this.symbol = symbol;
- }
-
- public String getCompanyName() {
- return companyName;
- }
-
- public void setCompanyName(String companyName) {
- this.companyName = companyName;
- }
-
- public BigDecimal getPrice() {
- return price;
- }
-
- public void setPrice(BigDecimal price) {
- this.price = price;
- }
-
- public BigDecimal getOpen() {
- return open1;
- }
-
- public void setOpen(BigDecimal open) {
- this.open1 = open;
- }
-
- public BigDecimal getLow() {
- return low;
- }
-
- public void setLow(BigDecimal low) {
- this.low = low;
- }
-
- public BigDecimal getHigh() {
- return high;
- }
-
- public void setHigh(BigDecimal high) {
- this.high = high;
- }
-
- public double getChange() {
- return change1;
- }
-
- public void setChange(double change) {
- this.change1 = change;
- }
-
- public double getVolume() {
- return volume;
- }
-
- public void setVolume(double volume) {
- this.volume = volume;
- }
-
- @Override
- public int hashCode() {
- int hash = 0;
- hash += (this.symbol != null ? this.symbol.hashCode() : 0);
- return hash;
- }
-
- @Override
- public boolean equals(Object object) {
-
- if (!(object instanceof QuoteDataBean)) {
- return false;
- }
- QuoteDataBean other = (QuoteDataBean) object;
- if (this.symbol != other.symbol && (this.symbol == null || !this.symbol.equals(other.symbol))) {
- return false;
- }
- return true;
- }
-}
diff --git a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/AsyncOrder.java b/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/AsyncOrder.java
deleted file mode 100644
index 1309d4ba..00000000
--- a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/AsyncOrder.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * (C) Copyright IBM Corporation 2019.
- *
- * Licensed 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.
- */
-package com.ibm.websphere.samples.daytrader.impl.direct;
-
-import com.ibm.websphere.samples.daytrader.interfaces.TradeJDBC;
-import com.ibm.websphere.samples.daytrader.interfaces.TradeServices;
-import javax.annotation.Resource;
-import javax.enterprise.context.Dependent;
-import javax.inject.Inject;
-import javax.transaction.UserTransaction;
-
-@Dependent
-public class AsyncOrder implements Runnable {
-
- @Inject
- @TradeJDBC
- TradeServices tradeService;
-
- @Resource
- UserTransaction ut;
-
- Integer orderID;
- boolean twoPhase;
-
- public void setProperties(Integer orderID, boolean twoPhase) {
- this.orderID = orderID;
- this.twoPhase = twoPhase;
- }
-
- @Override
- public void run() {
-
-
- try {
- ut.begin();
- tradeService.completeOrder(orderID, twoPhase);
- ut.commit();
- } catch (Exception e) {
-
- try {
- ut.rollback();
- } catch (Exception e1) {
- try {
- throw new Exception(e1);
- } catch (Exception e2) {
- e2.printStackTrace();
- }
- }
- try {
- throw new Exception(e);
- } catch (Exception e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
- }
- }
-}
diff --git a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/AsyncOrderSubmitter.java b/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/AsyncOrderSubmitter.java
deleted file mode 100644
index ccb8051e..00000000
--- a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/AsyncOrderSubmitter.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * (C) Copyright IBM Corporation 2019.
- *
- * Licensed 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.
- */
-package com.ibm.websphere.samples.daytrader.impl.direct;
-
-import java.util.concurrent.Future;
-import javax.annotation.Resource;
-import javax.enterprise.concurrent.ManagedExecutorService;
-import javax.enterprise.context.RequestScoped;
-import javax.inject.Inject;
-
-@RequestScoped
-public class AsyncOrderSubmitter {
-
-
- @Resource
- private ManagedExecutorService mes;
-
- @Inject
- private AsyncOrder asyncOrder;
-
-
- public Future> submitOrder(Integer orderID, boolean twoPhase) {
- asyncOrder.setProperties(orderID,twoPhase);
- return mes.submit(asyncOrder);
- }
-}
diff --git a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/KeySequenceDirect.java b/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/KeySequenceDirect.java
deleted file mode 100644
index 4165da0e..00000000
--- a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/KeySequenceDirect.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/**
- * (C) Copyright IBM Corporation 2015.
- *
- * Licensed 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.
- */
-package com.ibm.websphere.samples.daytrader.impl.direct;
-
-import com.ibm.websphere.samples.daytrader.util.KeyBlock;
-import com.ibm.websphere.samples.daytrader.util.Log;
-import com.ibm.websphere.samples.daytrader.util.TradeConfig;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-
-public class KeySequenceDirect {
-
- private static HashMap> keyMap = new HashMap>();
-
- public static synchronized Integer getNextID(Connection conn, String keyName, boolean inSession, boolean inGlobalTxn) throws Exception {
- Integer nextID = null;
- // First verify we have allocated a block of keys
- // for this key name
- // Then verify the allocated block has not been depleted
- // allocate a new block if necessary
- if (keyMap.containsKey(keyName) == false) {
- allocNewBlock(conn, keyName, inSession, inGlobalTxn);
- }
- Collection> block = keyMap.get(keyName);
-
- Iterator> ids = block.iterator();
- if (ids.hasNext() == false) {
- ids = allocNewBlock(conn, keyName, inSession, inGlobalTxn).iterator();
- }
- // get and return a new unique key
- nextID = (Integer) ids.next();
-
-
- Log.trace("KeySequenceDirect:getNextID inSession(" + inSession + ") - return new PK ID for Entity type: " + keyName + " ID=" + nextID);
-
- return nextID;
- }
-
- private static Collection> allocNewBlock(Connection conn, String keyName, boolean inSession, boolean inGlobalTxn) throws Exception {
- try {
-
- if (inGlobalTxn == false && !inSession) {
- conn.commit(); // commit any pending txns
- }
-
- PreparedStatement stmt = conn.prepareStatement(getKeyForUpdateSQL);
- stmt.setString(1, keyName);
- ResultSet rs = stmt.executeQuery();
-
- if (!rs.next()) {
- // No keys found for this name - create a new one
- PreparedStatement stmt2 = conn.prepareStatement(createKeySQL);
- int keyVal = 0;
- stmt2.setString(1, keyName);
- stmt2.setInt(2, keyVal);
- stmt2.executeUpdate();
- stmt2.close();
- stmt.close();
- stmt = conn.prepareStatement(getKeyForUpdateSQL);
- stmt.setString(1, keyName);
- rs = stmt.executeQuery();
- rs.next();
- }
-
- int keyVal = rs.getInt("keyval");
-
- stmt.close();
-
- stmt = conn.prepareStatement(updateKeyValueSQL);
- stmt.setInt(1, keyVal + TradeConfig.KEYBLOCKSIZE);
- stmt.setString(2, keyName);
- stmt.executeUpdate();
- stmt.close();
-
- Collection> block = new KeyBlock(keyVal, keyVal + TradeConfig.KEYBLOCKSIZE - 1);
- keyMap.put(keyName, block);
-
- if (inGlobalTxn == false && !inSession) {
- conn.commit();
- }
-
- return block;
- } catch (Exception e) {
- String error = "KeySequenceDirect:allocNewBlock - failure to allocate new block of keys for Entity type: " + keyName;
- Log.error(e, error);
- throw new Exception(error + e.toString());
- }
- }
-
- private static final String getKeyForUpdateSQL = "select * from keygenejb kg where kg.keyname = ? for update";
-
- private static final String createKeySQL = "insert into keygenejb " + "( keyname, keyval ) " + "VALUES ( ? , ? )";
-
- private static final String updateKeyValueSQL = "update keygenejb set keyval = ? " + "where keyname = ?";
-
-}
diff --git a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/TradeDirect.java b/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/TradeDirect.java
deleted file mode 100644
index 94d3d3b4..00000000
--- a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/TradeDirect.java
+++ /dev/null
@@ -1,1835 +0,0 @@
-/**
- * (C) Copyright IBM Corporation 2015.
- *
- * Licensed 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.
- */
-package com.ibm.websphere.samples.daytrader.impl.direct;
-
-import com.ibm.websphere.samples.daytrader.beans.MarketSummaryDataBean;
-import com.ibm.websphere.samples.daytrader.entities.AccountDataBean;
-import com.ibm.websphere.samples.daytrader.entities.AccountProfileDataBean;
-import com.ibm.websphere.samples.daytrader.entities.HoldingDataBean;
-import com.ibm.websphere.samples.daytrader.entities.OrderDataBean;
-import com.ibm.websphere.samples.daytrader.entities.QuoteDataBean;
-import com.ibm.websphere.samples.daytrader.interfaces.MarketSummaryUpdate;
-import com.ibm.websphere.samples.daytrader.interfaces.RuntimeMode;
-import com.ibm.websphere.samples.daytrader.interfaces.Trace;
-import com.ibm.websphere.samples.daytrader.interfaces.TradeJDBC;
-import com.ibm.websphere.samples.daytrader.interfaces.TradeServices;
-import com.ibm.websphere.samples.daytrader.util.FinancialUtils;
-import com.ibm.websphere.samples.daytrader.util.Log;
-import com.ibm.websphere.samples.daytrader.util.MDBStats;
-import com.ibm.websphere.samples.daytrader.util.RecentQuotePriceChangeList;
-import com.ibm.websphere.samples.daytrader.util.TradeConfig;
-import java.io.Serializable;
-import java.math.BigDecimal;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.concurrent.Future;
-import javax.annotation.Resource;
-import javax.enterprise.concurrent.ManagedExecutorService;
-import javax.enterprise.context.Dependent;
-import javax.enterprise.event.Event;
-import javax.enterprise.event.NotificationOptions;
-import javax.inject.Inject;
-import javax.jms.JMSContext;
-import javax.jms.JMSException;
-import javax.jms.Queue;
-import javax.jms.QueueConnectionFactory;
-import javax.jms.TextMessage;
-import javax.jms.Topic;
-import javax.jms.TopicConnectionFactory;
-import javax.sql.DataSource;
-import javax.transaction.UserTransaction;
-import javax.validation.constraints.NotNull;
-
-/**
- * TradeDirect uses direct JDBC and JMS access to a
- * javax.sql.DataSource to implement the business methods of the
- * Trade online broker application. These business methods represent the
- * features and operations that can be performed by customers of the brokerage
- * such as login, logout, get a stock quote, buy or sell a stock, etc. and are
- * specified in the {@link com.ibm.websphere.samples.daytrader.TradeServices}
- * interface
- *
- * Note: In order for this class to be thread-safe, a new TradeJDBC must be
- * created for each call to a method from the TradeInterface interface.
- * Otherwise, pooled connections may not be released.
- *
- * @see com.ibm.websphere.samples.daytrader.TradeServices
- *
- */
-
-@Dependent
-@TradeJDBC
-@RuntimeMode("Direct (JDBC)")
-@Trace
-public class TradeDirect implements TradeServices, Serializable {
- /**
- *
- */
- private static final long serialVersionUID = -8089049090952927985L;
-
- //This lock is used to serialize market summary operations.
- private static final Integer marketSummaryLock = new Integer(0);
- private static long nextMarketSummary = System.currentTimeMillis();
- private static MarketSummaryDataBean cachedMSDB = MarketSummaryDataBean.getRandomInstance();
-
- private static BigDecimal ZERO = new BigDecimal(0.0);
- private boolean inGlobalTxn = false;
- private boolean inSession = false;
-
- // For Wildfly - add java:/ to these resource names.
-
- @Resource(name = "jms/QueueConnectionFactory", authenticationType = javax.annotation.Resource.AuthenticationType.APPLICATION)
- //@Resource(name = "java:/jms/QueueConnectionFactory", authenticationType = javax.annotation.Resource.AuthenticationType.APPLICATION)
- private QueueConnectionFactory queueConnectionFactory;
-
- @Resource(name = "jms/TopicConnectionFactory", authenticationType = javax.annotation.Resource.AuthenticationType.APPLICATION)
- //@Resource(name = "java:/jms/TopicConnectionFactory", authenticationType = javax.annotation.Resource.AuthenticationType.APPLICATION)
- private TopicConnectionFactory topicConnectionFactory;
-
- @Resource(lookup = "jms/TradeStreamerTopic")
- //@Resource(lookup = "java:/jms/TradeStreamerTopic")
- private Topic tradeStreamerTopic;
-
- @Resource(lookup = "jms/TradeBrokerQueue")
- //@Resource(lookup = "java:/jms/TradeBrokerQueue")
- private Queue tradeBrokerQueue;
-
- @Resource(lookup = "jdbc/TradeDataSource")
- //@Resource(lookup = "java:/jdbc/TradeDataSource")
- private DataSource datasource;
-
- @Resource
- private UserTransaction txn;
-
- @Inject
- RecentQuotePriceChangeList recentQuotePriceChangeList;
-
- @Inject
- AsyncOrderSubmitter asyncOrderSubmitter;
-
- @Inject
- @MarketSummaryUpdate
- Event mkSummaryUpdateEvent;
-
- @Resource
- private ManagedExecutorService mes;
-
-
- @Override
- public MarketSummaryDataBean getMarketSummary() throws Exception {
-
- if (TradeConfig.getMarketSummaryInterval() == 0) {
- return getMarketSummaryInternal();
- }
- if (TradeConfig.getMarketSummaryInterval() < 0) {
- return cachedMSDB;
- }
-
- /**
- * This is a little funky. If its time to fetch a new Market summary
- * then we'll synchronize access to make sure only one requester does
- * it. Others will merely return the old copy until the new
- * MarketSummary has been executed.
- */
-
- long currentTime = System.currentTimeMillis();
-
- if (currentTime > nextMarketSummary) {
- long oldNextMarketSummary = nextMarketSummary;
- boolean fetch = false;
-
- synchronized (marketSummaryLock) {
- /**
- * Is it still ahead or did we miss lose the race? If we lost
- * then let's get out of here as the work has already been done.
- */
- if (oldNextMarketSummary == nextMarketSummary) {
- fetch = true;
- nextMarketSummary += TradeConfig.getMarketSummaryInterval() * 1000;
-
- /**
- * If the server has been idle for a while then its possible
- * that nextMarketSummary could be way off. Rather than try
- * and play catch up we'll simply get in sync with the
- * current time + the interval.
- */
- if (nextMarketSummary < currentTime) {
- nextMarketSummary = currentTime + TradeConfig.getMarketSummaryInterval() * 1000;
- }
- }
- }
-
- /**
- * If we're the lucky one then let's update the MarketSummary
- */
- if (fetch) {
- cachedMSDB = getMarketSummaryInternal();
-
- }
- }
-
- return cachedMSDB;
- }
-
-
-
- /**
- * @see TradeServices#getMarketSummary()
- */
-
- public MarketSummaryDataBean getMarketSummaryInternal() throws Exception {
-
- MarketSummaryDataBean marketSummaryData = null;
- Connection conn = null;
- try {
-
- Log.trace("TradeDirect:getMarketSummary - inSession(" + this.inSession + ")");
-
- conn = getConn();
- PreparedStatement stmt = getStatement(conn, getTSIAQuotesOrderByChangeSQL, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
-
- ArrayList topGainersData = new ArrayList(5);
- ArrayList topLosersData = new ArrayList(5);
-
- ResultSet rs = stmt.executeQuery();
-
- int count = 0;
- while (rs.next() && (count++ < 5)) {
- QuoteDataBean quoteData = getQuoteDataFromResultSet(rs);
- topLosersData.add(quoteData);
- }
-
- stmt.close();
- stmt = getStatement(conn, "select * from quoteejb q order by q.change1 DESC", ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
- rs = stmt.executeQuery();
-
- count = 0;
- while (rs.next() && (count++ < 5)) {
- QuoteDataBean quoteData = getQuoteDataFromResultSet(rs);
- topGainersData.add(quoteData);
- }
-
- /*
- * rs.last(); count = 0; while (rs.previous() && (count++ < 5) ) {
- * QuoteDataBean quoteData = getQuoteDataFromResultSet(rs);
- * topGainersData.add(quoteData); }
- */
-
- stmt.close();
-
- BigDecimal TSIA = ZERO;
- BigDecimal openTSIA = ZERO;
- double volume = 0.0;
-
- if ((topGainersData.size() > 0) || (topLosersData.size() > 0)) {
-
- stmt = getStatement(conn, getTSIASQL);
- rs = stmt.executeQuery();
-
- if (!rs.next()) {
- Log.error("TradeDirect:getMarketSummary -- error w/ getTSIASQL -- no results");
- } else {
- TSIA = rs.getBigDecimal("TSIA");
- }
- stmt.close();
-
- stmt = getStatement(conn, getOpenTSIASQL);
- rs = stmt.executeQuery();
-
- if (!rs.next()) {
- Log.error("TradeDirect:getMarketSummary -- error w/ getOpenTSIASQL -- no results");
- } else {
- openTSIA = rs.getBigDecimal("openTSIA");
- }
- stmt.close();
-
- stmt = getStatement(conn, getTSIATotalVolumeSQL);
- rs = stmt.executeQuery();
-
- if (!rs.next()) {
- Log.error("TradeDirect:getMarketSummary -- error w/ getTSIATotalVolumeSQL -- no results");
- } else {
- volume = rs.getDouble("totalVolume");
- }
- stmt.close();
- }
- commit(conn);
-
- marketSummaryData = new MarketSummaryDataBean(TSIA, openTSIA, volume, topGainersData, topLosersData);
- mkSummaryUpdateEvent.fireAsync("MarketSummaryUpdate", NotificationOptions.builder().setExecutor(mes).build());
-
- }
-
- catch (Exception e) {
- Log.error("TradeDirect:login -- error logging in user", e);
- rollBack(conn, e);
- } finally {
- releaseConn(conn);
- }
- return marketSummaryData;
-
- }
-
- /**
- * @see TradeServices#buy(String, String, double)
- */
- @Override
- @NotNull
- public OrderDataBean buy(String userID, String symbol, double quantity, int orderProcessingMode) throws Exception {
-
- final Connection conn = getConn();
- OrderDataBean orderData = null;
-
- BigDecimal total;
-
- try {
-
- Log.trace("TradeDirect:buy - inSession(" + this.inSession + ")");//, userID, symbol, new Double(quantity));
-
-
- if (!inSession && orderProcessingMode == TradeConfig.ASYNCH_2PHASE) {
-
- Log.trace("TradeDirect:buy create/begin global transaction");
-
-
- txn.begin();
- setInGlobalTxn(true);
- }
-
- //conn = getConn();
-
- AccountDataBean accountData = getAccountData(conn, userID);
- QuoteDataBean quoteData = getQuoteData(conn, symbol);
- HoldingDataBean holdingData = null; // the buy operation will create
- // the holding
-
- orderData = createOrder(accountData, quoteData, holdingData, "buy", quantity);
-
- // Update -- account should be credited during completeOrder
- BigDecimal price = quoteData.getPrice();
- BigDecimal orderFee = orderData.getOrderFee();
- total = (new BigDecimal(quantity).multiply(price)).add(orderFee);
- // subtract total from account balance
- creditAccountBalance(conn, accountData, total.negate());
- final Integer orderID = orderData.getOrderID();
-
- try {
-
- if (orderProcessingMode == TradeConfig.SYNCH) {
- completeOrder(conn, orderData.getOrderID());
- } else if (orderProcessingMode == TradeConfig.ASYNCH) {
- completeOrderAsync(orderID, true);
- } else if (orderProcessingMode == TradeConfig.ASYNCH_2PHASE) {
- queueOrder(orderID, true); // 2-phase
- }
- } catch (JMSException je) {
- Log.error("TradeBean:buy(" + userID + "," + symbol + "," + quantity + ") --> failed to queueOrder", je);
-
-
- cancelOrder(conn, orderData.getOrderID());
- }
-
- orderData = getOrderData(conn, orderData.getOrderID().intValue());
-
- if (getInGlobalTxn()) {
-
- Log.trace("TradeDirect:buy committing global transaction");
-
- if (!inSession && orderProcessingMode == TradeConfig.ASYNCH_2PHASE) {
- txn.commit();
- setInGlobalTxn(false);
- }
- } else {
- commit(conn);
- }
- } catch (Exception e) {
- Log.error("TradeDirect:buy error - rolling back", e);
- if (getInGlobalTxn()) {
- txn.rollback();
- } else {
- rollBack(conn, e);
- }
- } finally {
- releaseConn(conn);
- }
-
- return orderData;
- }
-
- /**
- * @see TradeServices#sell(String, Integer)
- */
- @Override
- @NotNull
- public OrderDataBean sell(String userID, Integer holdingID, int orderProcessingMode) throws Exception {
- Connection conn = null;
- OrderDataBean orderData = null;
- //UserTransaction txn = null;
-
- /*
- * total = (quantity * purchasePrice) + orderFee
- */
- BigDecimal total;
-
- try {
-
- Log.trace("TradeDirect:sell - inSession(" + this.inSession + ")", userID, holdingID);
-
-
- if (!inSession && orderProcessingMode == TradeConfig.ASYNCH_2PHASE) {
-
- Log.trace("TradeDirect:sell create/begin global transaction");
-
- txn.begin();
- setInGlobalTxn(true);
- }
-
- conn = getConn();
-
- AccountDataBean accountData = getAccountData(conn, userID);
- HoldingDataBean holdingData = getHoldingData(conn, holdingID.intValue());
- QuoteDataBean quoteData = null;
- if (holdingData != null) {
- quoteData = getQuoteData(conn, holdingData.getQuoteID());
- }
-
- if ((accountData == null) || (holdingData == null) || (quoteData == null)) {
- String error = "TradeDirect:sell -- error selling stock -- unable to find: \n\taccount=" + accountData + "\n\tholding=" + holdingData
- + "\n\tquote=" + quoteData + "\nfor user: " + userID + " and holdingID: " + holdingID;
- Log.debug(error);
- if (getInGlobalTxn()) {
- txn.rollback();
- } else {
- rollBack(conn, new Exception(error));
- }
- orderData = new OrderDataBean();
- orderData.setOrderStatus("cancelled");
- return orderData;
- }
-
- double quantity = holdingData.getQuantity();
-
- orderData = createOrder(accountData, quoteData, holdingData, "sell", quantity);
-
- // Set the holdingSymbol purchaseDate to selling to signify the sell
- // is "inflight"
- updateHoldingStatus(conn, holdingData.getHoldingID(), holdingData.getQuoteID());
-
- // UPDATE -- account should be credited during completeOrder
- BigDecimal price = quoteData.getPrice();
- BigDecimal orderFee = orderData.getOrderFee();
- total = (new BigDecimal(quantity).multiply(price)).subtract(orderFee);
- creditAccountBalance(conn, accountData, total);
-
- try {
- if (orderProcessingMode == TradeConfig.SYNCH) {
- completeOrder(conn, orderData.getOrderID());
- } else if (orderProcessingMode == TradeConfig.ASYNCH) {
- this.completeOrderAsync(orderData.getOrderID(), true);
- } else if (orderProcessingMode == TradeConfig.ASYNCH_2PHASE) {
- queueOrder(orderData.getOrderID(), true);
- }
- } catch (JMSException je) {
- Log.error("TradeBean:sell(" + userID + "," + holdingID + ") --> failed to queueOrder", je);
-
- cancelOrder(conn, orderData.getOrderID());
- }
-
- orderData = getOrderData(conn, orderData.getOrderID().intValue());
-
- if (!inSession && orderProcessingMode == TradeConfig.ASYNCH_2PHASE) {
-
- Log.trace("TradeDirect:sell committing global transaction");
-
- txn.commit();
- setInGlobalTxn(false);
- } else {
- commit(conn);
- }
- } catch (Exception e) {
- Log.error("TradeDirect:sell error", e);
- if (getInGlobalTxn()) {
- txn.rollback();
- } else {
- rollBack(conn, e);
- }
- } finally {
- releaseConn(conn);
- }
-
- return orderData;
- }
-
- /**
- * @see TradeServices#queueOrder(Integer)
- */
- @Override
- public void queueOrder(Integer orderID, boolean twoPhase) throws Exception {
-
-
- Log.trace("TradeDirect:queueOrder - inSession(" + this.inSession + ")", orderID);
-
-
- try (JMSContext context = queueConnectionFactory.createContext();){
- TextMessage message = context.createTextMessage();
-
- message.setStringProperty("command", "neworder");
- message.setIntProperty("orderID", orderID.intValue());
- message.setBooleanProperty("twoPhase", twoPhase);
- message.setBooleanProperty("direct", true);
- message.setLongProperty("publishTime", System.currentTimeMillis());
- message.setText("neworder: orderID=" + orderID + " runtimeMode=Direct twoPhase=" + twoPhase);
-
- context.createProducer().send(tradeBrokerQueue, message);
- } catch (Exception e) {
- throw e; // pass the exception
- }
- }
-
- /**
- * @see TradeServices#completeOrder(Integer)
- */
- @Override
- public OrderDataBean completeOrder(Integer orderID, boolean twoPhase) throws Exception {
- OrderDataBean orderData = null;
- Connection conn = null;
-
- try { // twoPhase
-
-
- Log.trace("TradeDirect:completeOrder - inSession(" + this.inSession + ")", orderID);
-
- setInGlobalTxn(!inSession && twoPhase);
- conn = getConn();
-
- orderData = completeOrder(conn, orderID);
-
- commit(conn);
-
- } catch (Exception e) {
- Log.error("TradeDirect:completeOrder -- error completing order", e);
- rollBack(conn, e);
- cancelOrder(orderID, twoPhase);
- } finally {
- releaseConn(conn);
- }
-
- return orderData;
-
- }
-
- @Override
- public Future completeOrderAsync(Integer orderID, boolean twoPhase) throws Exception {
- if (!inSession) {
- asyncOrderSubmitter.submitOrder(orderID, twoPhase);
- }
- return null;
- }
-
-
- private OrderDataBean completeOrder(Connection conn, Integer orderID) throws Exception {
- //conn = getConn();
- OrderDataBean orderData = null;
-
- Log.trace("TradeDirect:completeOrderInternal - inSession(" + this.inSession + ")", orderID);
-
-
- PreparedStatement stmt = getStatement(conn, getOrderSQL);
- stmt.setInt(1, orderID.intValue());
-
- ResultSet rs = stmt.executeQuery();
-
- if (!rs.next()) {
- Log.error("TradeDirect:completeOrder -- unable to find order: " + orderID);
- stmt.close();
- return orderData;
- }
- orderData = getOrderDataFromResultSet(rs);
-
- String orderType = orderData.getOrderType();
- String orderStatus = orderData.getOrderStatus();
-
- // if (order.isCompleted())
- if ((orderStatus.compareToIgnoreCase("completed") == 0) || (orderStatus.compareToIgnoreCase("alertcompleted") == 0)
- || (orderStatus.compareToIgnoreCase("cancelled") == 0)) {
- throw new Exception("TradeDirect:completeOrder -- attempt to complete Order that is already completed");
- }
-
- int accountID = rs.getInt("account_accountID");
- String quoteID = rs.getString("quote_symbol");
- int holdingID = rs.getInt("holding_holdingID");
-
- BigDecimal price = orderData.getPrice();
- double quantity = orderData.getQuantity();
-
- // get the data for the account and quote
- // the holding will be created for a buy or extracted for a sell
-
- /*
- * Use the AccountID and Quote Symbol from the Order AccountDataBean
- * accountData = getAccountData(accountID, conn); QuoteDataBean
- * quoteData = getQuoteData(conn, quoteID);
- */
- String userID = getAccountProfileData(conn, new Integer(accountID)).getUserID();
-
- HoldingDataBean holdingData = null;
-
-
- Log.trace("TradeDirect:completeOrder--> Completing Order " + orderData.getOrderID() + "\n\t Order info: " + orderData + "\n\t Account info: "
- + accountID + "\n\t Quote info: " + quoteID);
-
-
- // if (order.isBuy())
- if (orderType.compareToIgnoreCase("buy") == 0) {
- /*
- * Complete a Buy operation - create a new Holding for the Account -
- * deduct the Order cost from the Account balance
- */
-
- holdingData = createHolding(conn, accountID, quoteID, quantity, price);
- updateOrderHolding(conn, orderID.intValue(), holdingData.getHoldingID().intValue());
- updateOrderStatus(conn, orderData.getOrderID(), "closed");
- updateQuotePriceVolume(orderData.getSymbol(), TradeConfig.getRandomPriceChangeFactor(), orderData.getQuantity());
- }
-
- // if (order.isSell()) {
- if (orderType.compareToIgnoreCase("sell") == 0) {
- /*
- * Complete a Sell operation - remove the Holding from the Account -
- * deposit the Order proceeds to the Account balance
- */
- holdingData = getHoldingData(conn, holdingID);
- if (holdingData == null) {
- Log.debug("TradeDirect:completeOrder:sell -- user: " + userID + " already sold holding: " + holdingID);
- updateOrderStatus(conn, orderData.getOrderID(), "cancelled");
- } else {
- removeHolding(conn, holdingID, orderID.intValue());
- updateOrderStatus(conn, orderData.getOrderID(), "closed");
- updateQuotePriceVolume(orderData.getSymbol(), TradeConfig.getRandomPriceChangeFactor(), orderData.getQuantity());
- }
-
- }
-
-
-
- Log.trace("TradeDirect:completeOrder--> Completed Order " + orderData.getOrderID() + "\n\t Order info: " + orderData + "\n\t Account info: "
- + accountID + "\n\t Quote info: " + quoteID + "\n\t Holding info: " + holdingData);
-
- stmt.close();
-
- commit(conn);
-
-
-
- return orderData;
- }
-
- /**
- * @see TradeServices#cancelOrder(Integer, boolean)
- */
- @Override
- public void cancelOrder(Integer orderID, boolean twoPhase) throws Exception {
-
- Connection conn = null;
- try {
-
- Log.trace("TradeDirect:cancelOrder - inSession(" + this.inSession + ")", orderID);
-
- setInGlobalTxn(!inSession && twoPhase);
- conn = getConn();
- cancelOrder(conn, orderID);
- commit(conn);
-
- } catch (Exception e) {
- Log.error("TradeDirect:cancelOrder -- error cancelling order: " + orderID, e);
- rollBack(conn, e);
- } finally {
- releaseConn(conn);
- }
- }
-
- private void cancelOrder(Connection conn, Integer orderID) throws Exception {
- updateOrderStatus(conn, orderID, "cancelled");
- }
-
- @Override
- public void orderCompleted(String userID, Integer orderID) throws Exception {
- throw new UnsupportedOperationException("TradeDirect:orderCompleted method not supported");
- }
-
- private HoldingDataBean createHolding(Connection conn, int accountID, String symbol, double quantity, BigDecimal purchasePrice) throws Exception {
-
- Timestamp purchaseDate = new Timestamp(System.currentTimeMillis());
- PreparedStatement stmt = getStatement(conn, createHoldingSQL);
-
- Integer holdingID = KeySequenceDirect.getNextID(conn, "holding", inSession, getInGlobalTxn());
- stmt.setInt(1, holdingID.intValue());
- stmt.setTimestamp(2, purchaseDate);
- stmt.setBigDecimal(3, purchasePrice);
- stmt.setDouble(4, quantity);
- stmt.setString(5, symbol);
- stmt.setInt(6, accountID);
- stmt.executeUpdate();
-
- stmt.close();
-
- return getHoldingData(conn, holdingID.intValue());
- }
-
- private void removeHolding(Connection conn, int holdingID, int orderID) throws Exception {
- PreparedStatement stmt = getStatement(conn, removeHoldingSQL);
-
- stmt.setInt(1, holdingID);
- stmt.executeUpdate();
- stmt.close();
-
- // set the HoldingID to NULL for the purchase and sell order now that
- // the holding as been removed
- stmt = getStatement(conn, removeHoldingFromOrderSQL);
-
- stmt.setInt(1, holdingID);
- stmt.executeUpdate();
- stmt.close();
-
- }
-
- public OrderDataBean createOrder(AccountDataBean accountData, QuoteDataBean quoteData, HoldingDataBean holdingData, String orderType,
- double quantity) throws Exception {
- OrderDataBean orderData = null;
- Connection conn = null;
- try {
-
- conn = getConn();
- Timestamp currentDate = new Timestamp(System.currentTimeMillis());
-
- PreparedStatement stmt = getStatement(conn, createOrderSQL);
-
- Integer orderID = KeySequenceDirect.getNextID(conn, "order", inSession, getInGlobalTxn());
- stmt.setInt(1, orderID.intValue());
- stmt.setString(2, orderType);
- stmt.setString(3, "open");
- stmt.setTimestamp(4, currentDate);
- stmt.setDouble(5, quantity);
- stmt.setBigDecimal(6, quoteData.getPrice().setScale(FinancialUtils.SCALE, FinancialUtils.ROUND));
- stmt.setBigDecimal(7, TradeConfig.getOrderFee(orderType));
- stmt.setInt(8, accountData.getAccountID().intValue());
- if (holdingData == null) {
- stmt.setNull(9, java.sql.Types.INTEGER);
- } else {
- stmt.setInt(9, holdingData.getHoldingID().intValue());
- }
- stmt.setString(10, quoteData.getSymbol());
- stmt.executeUpdate();
-
- orderData = getOrderData(conn, orderID.intValue());
-
- stmt.close();
-
- commit(conn);
- } catch (Exception e) {
- Log.error("TradeDirect:createOrder -- error getting user orders", e);
- rollBack(conn, e);
- } finally {
- releaseConn(conn);
- }
-
- return orderData;
- }
-
- /**
- * @see TradeServices#getOrders(String)
- */
- @Override
- public Collection getOrders(String userID) throws Exception {
- Collection orderDataBeans = new ArrayList();
- Connection conn = null;
- try {
- Log.trace("TradeDirect:getOrders - inSession(" + this.inSession + ")", userID);
-
-
- conn = getConn();
- PreparedStatement stmt = getStatement(conn, getOrdersByUserSQL);
- stmt.setString(1, userID);
-
- ResultSet rs = stmt.executeQuery();
-
- // TODO: return top 5 orders for now -- next version will add a
- // getAllOrders method
- // also need to get orders sorted by order id descending
- int i = 0;
- while ((rs.next()) && (i++ < 5)) {
- OrderDataBean orderData = getOrderDataFromResultSet(rs);
- orderDataBeans.add(orderData);
- }
-
- stmt.close();
- commit(conn);
-
- } catch (Exception e) {
- Log.error("TradeDirect:getOrders -- error getting user orders", e);
- rollBack(conn, e);
- } finally {
- releaseConn(conn);
- }
- return orderDataBeans;
- }
-
- /**
- * @see TradeServices#getClosedOrders(String)
- */
- @Override
- public Collection getClosedOrders(String userID) throws Exception {
- Collection orderDataBeans = new ArrayList();
- Connection conn = null;
- try {
-
- Log.trace("TradeDirect:getClosedOrders - inSession(" + this.inSession + ")", userID);
-
-
- conn = getConn();
- PreparedStatement stmt = getStatement(conn, getClosedOrdersSQL);
- stmt.setString(1, userID);
-
- ResultSet rs = stmt.executeQuery();
-
- while (rs.next()) {
- OrderDataBean orderData = getOrderDataFromResultSet(rs);
- orderData.setOrderStatus("completed");
- updateOrderStatus(conn, orderData.getOrderID(), orderData.getOrderStatus());
- orderDataBeans.add(orderData);
-
- }
-
- stmt.close();
- commit(conn);
- } catch (Exception e) {
- Log.error("TradeDirect:getOrders -- error getting user orders", e);
- rollBack(conn, e);
- } finally {
- releaseConn(conn);
- }
- return orderDataBeans;
- }
-
- /**
- * @see TradeServices#createQuote(String, String, BigDecimal)
- */
- @Override
- public QuoteDataBean createQuote(String symbol, String companyName, BigDecimal price) throws Exception {
-
- QuoteDataBean quoteData = null;
- Connection conn = null;
- try {
-
- Log.trace("TradeDirect:createQuote - inSession(" + this.inSession + ")");
-
-
- price = price.setScale(FinancialUtils.SCALE, FinancialUtils.ROUND);
- double volume = 0.0, change = 0.0;
-
- conn = getConn();
- PreparedStatement stmt = getStatement(conn, createQuoteSQL);
- stmt.setString(1, symbol); // symbol
- stmt.setString(2, companyName); // companyName
- stmt.setDouble(3, volume); // volume
- stmt.setBigDecimal(4, price); // price
- stmt.setBigDecimal(5, price); // open
- stmt.setBigDecimal(6, price); // low
- stmt.setBigDecimal(7, price); // high
- stmt.setDouble(8, change); // change
-
- stmt.executeUpdate();
- stmt.close();
- commit(conn);
-
- quoteData = new QuoteDataBean(symbol, companyName, volume, price, price, price, price, change);
- } catch (Exception e) {
- Log.error("TradeDirect:createQuote -- error creating quote", e);
- } finally {
- releaseConn(conn);
- }
- return quoteData;
- }
-
- /**
- * @see TradeServices#getQuote(String)
- */
-
- @Override
- public QuoteDataBean getQuote(String symbol) throws Exception {
- QuoteDataBean quoteData = null;
- Connection conn = null;
-
- try {
-
- Log.trace("TradeDirect:getQuote - inSession(" + this.inSession + ")", symbol);
-
-
- conn = getConn();
- quoteData = getQuote(conn, symbol);
- commit(conn);
- } catch (Exception e) {
- Log.error("TradeDirect:getQuote -- error getting quote", e);
- rollBack(conn, e);
- } finally {
- releaseConn(conn);
- }
- return quoteData;
- }
-
- private QuoteDataBean getQuote(Connection conn, String symbol) throws Exception {
- QuoteDataBean quoteData = null;
- PreparedStatement stmt = getStatement(conn, getQuoteSQL);
- stmt.setString(1, symbol); // symbol
-
- ResultSet rs = stmt.executeQuery();
-
- if (!rs.next()) {
- Log.error("TradeDirect:getQuote -- failure no result.next() for symbol: " + symbol);
- } else {
- quoteData = getQuoteDataFromResultSet(rs);
- }
-
- stmt.close();
-
- return quoteData;
- }
-
- private QuoteDataBean getQuoteForUpdate(Connection conn, String symbol) throws Exception {
- QuoteDataBean quoteData = null;
- PreparedStatement stmt = getStatement(conn, getQuoteForUpdateSQL);
- stmt.setString(1, symbol); // symbol
-
- ResultSet rs = stmt.executeQuery();
-
- if (!rs.next()) {
- Log.error("TradeDirect:getQuote -- failure no result.next()");
- } else {
- quoteData = getQuoteDataFromResultSet(rs);
- }
-
- stmt.close();
-
- return quoteData;
- }
-
- /**
- * @see TradeServices#getAllQuotes(String)
- */
- @Override
- public Collection getAllQuotes() throws Exception {
- Collection quotes = new ArrayList();
- QuoteDataBean quoteData = null;
-
- Connection conn = null;
- try {
- conn = getConn();
-
- PreparedStatement stmt = getStatement(conn, getAllQuotesSQL);
-
- ResultSet rs = stmt.executeQuery();
-
- while (!rs.next()) {
- quoteData = getQuoteDataFromResultSet(rs);
- quotes.add(quoteData);
- }
-
- stmt.close();
- } catch (Exception e) {
- Log.error("TradeDirect:getAllQuotes", e);
- rollBack(conn, e);
- } finally {
- releaseConn(conn);
- }
-
- return quotes;
- }
-
- /**
- * @see TradeServices#getHoldings(String)
- */
- @Override
- public Collection getHoldings(String userID) throws Exception {
- Collection holdingDataBeans = new ArrayList();
- Connection conn = null;
- try {
-
- Log.trace("TradeDirect:getHoldings - inSession(" + this.inSession + ")", userID);
-
-
- conn = getConn();
- PreparedStatement stmt = getStatement(conn, getHoldingsForUserSQL);
- stmt.setString(1, userID);
-
- ResultSet rs = stmt.executeQuery();
-
- while (rs.next()) {
- HoldingDataBean holdingData = getHoldingDataFromResultSet(rs);
- holdingDataBeans.add(holdingData);
- }
-
- stmt.close();
- commit(conn);
-
- } catch (Exception e) {
- Log.error("TradeDirect:getHoldings -- error getting user holings", e);
- rollBack(conn, e);
- } finally {
- releaseConn(conn);
- }
- return holdingDataBeans;
- }
-
- /**
- * @see TradeServices#getHolding(Integer)
- */
- @Override
- public HoldingDataBean getHolding(Integer holdingID) throws Exception {
- HoldingDataBean holdingData = null;
- Connection conn = null;
- try {
-
- Log.trace("TradeDirect:getHolding - inSession(" + this.inSession + ")", holdingID);
-
-
- conn = getConn();
- holdingData = getHoldingData(holdingID.intValue());
-
- commit(conn);
-
- } catch (Exception e) {
- Log.error("TradeDirect:getHolding -- error getting holding " + holdingID + "", e);
- rollBack(conn, e);
- } finally {
- releaseConn(conn);
- }
- return holdingData;
- }
-
- /**
- * @see TradeServices#getAccountData(String)
- */
- @Override
- public AccountDataBean getAccountData(String userID) throws Exception {
- try {
- AccountDataBean accountData = null;
- Connection conn = null;
- try {
-
- Log.trace("TradeDirect:getAccountData - inSession(" + this.inSession + ")", userID);
-
-
- conn = getConn();
- accountData = getAccountData(conn, userID);
- commit(conn);
-
- } catch (Exception e) {
- Log.error("TradeDirect:getAccountData -- error getting account data", e);
- rollBack(conn, e);
- } finally {
- releaseConn(conn);
- }
- return accountData;
- } catch (Exception e) {
- throw new Exception(e.getMessage(), e);
- }
- }
-
- private AccountDataBean getAccountData(Connection conn, String userID) throws Exception {
- PreparedStatement stmt = getStatement(conn, getAccountForUserSQL);
- stmt.setString(1, userID);
- ResultSet rs = stmt.executeQuery();
- AccountDataBean accountData = getAccountDataFromResultSet(rs);
- stmt.close();
- return accountData;
- }
-
- /**
- * @see TradeServices#getAccountData(String)
- */
- public AccountDataBean getAccountData(int accountID) throws Exception {
- AccountDataBean accountData = null;
- Connection conn = null;
- try {
-
- Log.trace("TradeDirect:getAccountData - inSession(" + this.inSession + ")", new Integer(accountID));
-
- conn = getConn();
- accountData = getAccountData(accountID, conn);
- commit(conn);
-
- } catch (Exception e) {
- Log.error("TradeDirect:getAccountData -- error getting account data", e);
- rollBack(conn, e);
- } finally {
- releaseConn(conn);
- }
- return accountData;
- }
-
- private AccountDataBean getAccountData(int accountID, Connection conn) throws Exception {
- PreparedStatement stmt = getStatement(conn, getAccountSQL);
- stmt.setInt(1, accountID);
- ResultSet rs = stmt.executeQuery();
- AccountDataBean accountData = getAccountDataFromResultSet(rs);
- stmt.close();
- return accountData;
- }
-
- private QuoteDataBean getQuoteData(Connection conn, String symbol) throws Exception {
- QuoteDataBean quoteData = null;
- PreparedStatement stmt = getStatement(conn, getQuoteSQL);
- stmt.setString(1, symbol);
- ResultSet rs = stmt.executeQuery();
- if (!rs.next()) {
- Log.error("TradeDirect:getQuoteData -- could not find quote for symbol=" + symbol);
- } else {
- quoteData = getQuoteDataFromResultSet(rs);
- }
- stmt.close();
- return quoteData;
- }
-
- private HoldingDataBean getHoldingData(int holdingID) throws Exception {
- HoldingDataBean holdingData = null;
- Connection conn = null;
- try {
- conn = getConn();
- holdingData = getHoldingData(conn, holdingID);
- commit(conn);
- } catch (Exception e) {
- Log.error("TradeDirect:getHoldingData -- error getting data", e);
- rollBack(conn, e);
- } finally {
- releaseConn(conn);
- }
- return holdingData;
- }
-
- private HoldingDataBean getHoldingData(Connection conn, int holdingID) throws Exception {
- HoldingDataBean holdingData = null;
- PreparedStatement stmt = getStatement(conn, getHoldingSQL);
- stmt.setInt(1, holdingID);
- ResultSet rs = stmt.executeQuery();
- if (!rs.next()) {
- // already sold
- Log.debug("TradeDirect:getHoldingData -- no results -- holdingID=" + holdingID);
- } else {
- holdingData = getHoldingDataFromResultSet(rs);
- }
-
- stmt.close();
- return holdingData;
- }
-
- private OrderDataBean getOrderData(Connection conn, int orderID) throws Exception {
- OrderDataBean orderData = null;
-
- Log.trace("TradeDirect:getOrderData(conn, " + orderID + ")");
-
- PreparedStatement stmt = getStatement(conn, getOrderSQL);
- stmt.setInt(1, orderID);
- ResultSet rs = stmt.executeQuery();
- if (!rs.next()) {
- // already sold
- Log.error("TradeDirect:getOrderData -- no results for orderID:" + orderID);
- } else {
- orderData = getOrderDataFromResultSet(rs);
- }
- stmt.close();
- return orderData;
- }
-
- /**
- * @see TradeServices#getAccountProfileData(String)
- */
- @Override
- public AccountProfileDataBean getAccountProfileData(String userID) throws Exception {
- AccountProfileDataBean accountProfileData = null;
- Connection conn = null;
-
- try {
-
- Log.trace("TradeDirect:getAccountProfileData - inSession(" + this.inSession + ")", userID);
-
-
- conn = getConn();
- accountProfileData = getAccountProfileData(conn, userID);
- commit(conn);
- } catch (Exception e) {
- Log.error("TradeDirect:getAccountProfileData -- error getting profile data", e);
- rollBack(conn, e);
- } finally {
- releaseConn(conn);
- }
- return accountProfileData;
- }
-
- private AccountProfileDataBean getAccountProfileData(Connection conn, String userID) throws Exception {
- PreparedStatement stmt = getStatement(conn, getAccountProfileSQL);
- stmt.setString(1, userID);
-
- ResultSet rs = stmt.executeQuery();
-
- AccountProfileDataBean accountProfileData = getAccountProfileDataFromResultSet(rs);
- stmt.close();
- return accountProfileData;
- }
-
- private AccountProfileDataBean getAccountProfileData(Connection conn, Integer accountID) throws Exception {
- PreparedStatement stmt = getStatement(conn, getAccountProfileForAccountSQL);
- stmt.setInt(1, accountID.intValue());
-
- ResultSet rs = stmt.executeQuery();
-
- AccountProfileDataBean accountProfileData = getAccountProfileDataFromResultSet(rs);
- stmt.close();
- return accountProfileData;
- }
-
- /**
- * @see TradeServices#updateAccountProfile(AccountProfileDataBean)
- */
- @Override
- public AccountProfileDataBean updateAccountProfile(AccountProfileDataBean profileData) throws Exception {
- AccountProfileDataBean accountProfileData = null;
- Connection conn = null;
-
- try {
-
- Log.trace("TradeDirect:updateAccountProfileData - inSession(" + this.inSession + ")", profileData.getUserID());
-
- conn = getConn();
- updateAccountProfile(conn, profileData);
-
- accountProfileData = getAccountProfileData(conn, profileData.getUserID());
- commit(conn);
- } catch (Exception e) {
- Log.error("TradeDirect:getAccountProfileData -- error getting profile data", e);
- rollBack(conn, e);
- } finally {
- releaseConn(conn);
- }
- return accountProfileData;
- }
-
- private void creditAccountBalance(Connection conn, AccountDataBean accountData, BigDecimal credit) throws Exception {
- PreparedStatement stmt = getStatement(conn, creditAccountBalanceSQL);
-
- stmt.setBigDecimal(1, credit);
- stmt.setInt(2, accountData.getAccountID().intValue());
-
- stmt.executeUpdate();
- stmt.close();
-
- }
-
- // Set Timestamp to zero to denote sell is inflight
- // UPDATE -- could add a "status" attribute to holding
- private void updateHoldingStatus(Connection conn, Integer holdingID, String symbol) throws Exception {
- Timestamp ts = new Timestamp(0);
- PreparedStatement stmt = getStatement(conn, "update holdingejb set purchasedate= ? where holdingid = ?");
-
- stmt.setTimestamp(1, ts);
- stmt.setInt(2, holdingID.intValue());
- stmt.executeUpdate();
- stmt.close();
- }
-
- private void updateOrderStatus(Connection conn, Integer orderID, String status) throws Exception {
- PreparedStatement stmt = getStatement(conn, updateOrderStatusSQL);
-
- stmt.setString(1, status);
- stmt.setTimestamp(2, new Timestamp(System.currentTimeMillis()));
- stmt.setInt(3, orderID.intValue());
- stmt.executeUpdate();
- stmt.close();
- }
-
- private void updateOrderHolding(Connection conn, int orderID, int holdingID) throws Exception {
- PreparedStatement stmt = getStatement(conn, updateOrderHoldingSQL);
-
- stmt.setInt(1, holdingID);
- stmt.setInt(2, orderID);
- stmt.executeUpdate();
- stmt.close();
- }
-
- private void updateAccountProfile(Connection conn, AccountProfileDataBean profileData) throws Exception {
- PreparedStatement stmt = getStatement(conn, updateAccountProfileSQL);
-
- stmt.setString(1, profileData.getPassword());
- stmt.setString(2, profileData.getFullName());
- stmt.setString(3, profileData.getAddress());
- stmt.setString(4, profileData.getEmail());
- stmt.setString(5, profileData.getCreditCard());
- stmt.setString(6, profileData.getUserID());
-
- stmt.executeUpdate();
- stmt.close();
- }
-
- @Override
- public QuoteDataBean updateQuotePriceVolume(String symbol, BigDecimal changeFactor, double sharesTraded) throws Exception {
- return updateQuotePriceVolumeInt(symbol, changeFactor, sharesTraded, TradeConfig.getPublishQuotePriceChange());
- }
-
- /**
- * Update a quote's price and volume
- *
- * @param symbol
- * The PK of the quote
- * @param changeFactor
- * the percent to change the old price by (between 50% and 150%)
- * @param sharedTraded
- * the ammount to add to the current volume
- * @param publishQuotePriceChange
- * used by the PingJDBCWrite Primitive to ensure no JMS is used,
- * should be true for all normal calls to this API
- */
- public QuoteDataBean updateQuotePriceVolumeInt(String symbol, BigDecimal changeFactor, double sharesTraded, boolean publishQuotePriceChange)
- throws Exception {
-
- if (TradeConfig.getUpdateQuotePrices() == false) {
- return new QuoteDataBean();
- }
-
- QuoteDataBean quoteData = null;
- Connection conn = null;
-
- try {
- Log.trace("TradeDirect:updateQuotePriceVolume - inSession(" + this.inSession + ")", symbol, changeFactor, new Double(sharesTraded));
-
- conn = getConn();
-
- quoteData = getQuoteForUpdate(conn, symbol);
- BigDecimal oldPrice = quoteData.getPrice();
- BigDecimal openPrice = quoteData.getOpen();
-
- double newVolume = quoteData.getVolume() + sharesTraded;
-
- if (oldPrice.equals(TradeConfig.PENNY_STOCK_PRICE)) {
- changeFactor = TradeConfig.PENNY_STOCK_RECOVERY_MIRACLE_MULTIPLIER;
- } else if (oldPrice.compareTo(TradeConfig.MAXIMUM_STOCK_PRICE) > 0) {
- changeFactor = TradeConfig.MAXIMUM_STOCK_SPLIT_MULTIPLIER;
- }
-
- BigDecimal newPrice = changeFactor.multiply(oldPrice).setScale(2, BigDecimal.ROUND_HALF_UP);
- double change = newPrice.subtract(openPrice).doubleValue();
-
- updateQuotePriceVolume(conn, quoteData.getSymbol(), newPrice, newVolume, change);
- quoteData = getQuote(conn, symbol);
-
- commit(conn);
-
- if (publishQuotePriceChange) {
- publishQuotePriceChange(quoteData, oldPrice, changeFactor, sharesTraded);
- }
-
- recentQuotePriceChangeList.add(quoteData);
-
- } catch (Exception e) {
- Log.error("TradeDirect:updateQuotePriceVolume -- error updating quote price/volume for symbol:" + symbol);
- rollBack(conn, e);
- throw e;
- } finally {
- releaseConn(conn);
- }
- return quoteData;
- }
-
- private void updateQuotePriceVolume(Connection conn, String symbol, BigDecimal newPrice, double newVolume, double change) throws Exception {
-
- PreparedStatement stmt = getStatement(conn, updateQuotePriceVolumeSQL);
-
- stmt.setBigDecimal(1, newPrice);
- stmt.setDouble(2, change);
- stmt.setDouble(3, newVolume);
- stmt.setString(4, symbol);
-
- stmt.executeUpdate();
- stmt.close();
- }
-
- private void publishQuotePriceChange(QuoteDataBean quoteData, BigDecimal oldPrice, BigDecimal changeFactor, double sharesTraded) throws Exception {
-
- Log.trace("TradeDirect:publishQuotePrice PUBLISHING to MDB quoteData = " + quoteData);
-
- try (JMSContext context = topicConnectionFactory.createContext();){
- TextMessage message = context.createTextMessage();
-
- message.setStringProperty("command", "updateQuote");
- message.setStringProperty("symbol", quoteData.getSymbol());
- message.setStringProperty("company", quoteData.getCompanyName());
- message.setStringProperty("price", quoteData.getPrice().toString());
- message.setStringProperty("oldPrice", oldPrice.toString());
- message.setStringProperty("open", quoteData.getOpen().toString());
- message.setStringProperty("low", quoteData.getLow().toString());
- message.setStringProperty("high", quoteData.getHigh().toString());
- message.setDoubleProperty("volume", quoteData.getVolume());
-
- message.setStringProperty("changeFactor", changeFactor.toString());
- message.setDoubleProperty("sharesTraded", sharesTraded);
- message.setLongProperty("publishTime", System.currentTimeMillis());
- message.setText("Update Stock price for " + quoteData.getSymbol() + " old price = " + oldPrice + " new price = " + quoteData.getPrice());
-
-
- context.createProducer().send(tradeStreamerTopic, message);
-
- } catch (Exception e) {
- throw e; // pass exception back
-
- }
- }
-
- /**
- * @see TradeServices#login(String, String)
- */
-
- @Override
- public AccountDataBean login(String userID, String password) throws Exception {
-
- AccountDataBean accountData = null;
- Connection conn = null;
- try {
- Log.trace("TradeDirect:login - inSession(" + this.inSession + ")", userID, password);
-
- conn = getConn();
- PreparedStatement stmt = getStatement(conn, getAccountProfileSQL);
- stmt.setString(1, userID);
-
- ResultSet rs = stmt.executeQuery();
- if (!rs.next()) {
- Log.error("TradeDirect:login -- failure to find account for" + userID);
- throw new javax.ejb.FinderException("Cannot find account for" + userID);
- }
-
- String pw = rs.getString("passwd");
- stmt.close();
- if ((pw == null) || (pw.equals(password) == false)) {
- String error = "TradeDirect:Login failure for user: " + userID + "\n\tIncorrect password-->" + userID + ":" + password;
- Log.error(error);
- throw new Exception(error);
- }
-
- stmt = getStatement(conn, loginSQL);
- stmt.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
- stmt.setString(2, userID);
-
- stmt.executeUpdate();
- stmt.close();
-
- stmt = getStatement(conn, getAccountForUserSQL);
- stmt.setString(1, userID);
- rs = stmt.executeQuery();
-
- accountData = getAccountDataFromResultSet(rs);
-
- stmt.close();
-
- commit(conn);
- } catch (Exception e) {
- Log.error("TradeDirect:login -- error logging in user", e);
- rollBack(conn, e);
- } finally {
- releaseConn(conn);
- }
- return accountData;
-
- /*
- * setLastLogin( new Timestamp(System.currentTimeMillis()) );
- * setLoginCount( getLoginCount() + 1 );
- */
- }
-
- /**
- * @see TradeServices#logout(String)
- */
- @Override
- public void logout(String userID) throws Exception {
- Log.trace("TradeDirect:logout - inSession(" + this.inSession + ")", userID);
-
- Connection conn = null;
- try {
- conn = getConn();
- PreparedStatement stmt = getStatement(conn, logoutSQL);
- stmt.setString(1, userID);
- stmt.executeUpdate();
- stmt.close();
-
- commit(conn);
- } catch (Exception e) {
- Log.error("TradeDirect:logout -- error logging out user", e);
- rollBack(conn, e);
- } finally {
- releaseConn(conn);
- }
- }
-
- /**
- * @see TradeServices#register(String, String, String, String, String,
- * String, BigDecimal, boolean)
- */
-
- @Override
- public AccountDataBean register(String userID, String password, String fullname, String address, String email, String creditcard, BigDecimal openBalance)
- throws Exception {
-
- AccountDataBean accountData = null;
- Connection conn = null;
- try {
- Log.trace("TradeDirect:register - inSession(" + this.inSession + ")");
-
- conn = getConn();
- PreparedStatement stmt = getStatement(conn, createAccountSQL);
-
- Integer accountID = KeySequenceDirect.getNextID(conn, "account", inSession, getInGlobalTxn());
- BigDecimal balance = openBalance;
- Timestamp creationDate = new Timestamp(System.currentTimeMillis());
- Timestamp lastLogin = creationDate;
- int loginCount = 0;
- int logoutCount = 0;
-
- stmt.setInt(1, accountID.intValue());
- stmt.setTimestamp(2, creationDate);
- stmt.setBigDecimal(3, openBalance);
- stmt.setBigDecimal(4, balance);
- stmt.setTimestamp(5, lastLogin);
- stmt.setInt(6, loginCount);
- stmt.setInt(7, logoutCount);
- stmt.setString(8, userID);
- stmt.executeUpdate();
- stmt.close();
-
- stmt = getStatement(conn, createAccountProfileSQL);
- stmt.setString(1, userID);
- stmt.setString(2, password);
- stmt.setString(3, fullname);
- stmt.setString(4, address);
- stmt.setString(5, email);
- stmt.setString(6, creditcard);
- stmt.executeUpdate();
- stmt.close();
-
- commit(conn);
-
- accountData = new AccountDataBean(accountID, loginCount, logoutCount, lastLogin, creationDate, balance, openBalance, userID);
-
- } catch (Exception e) {
- Log.error("TradeDirect:register -- error registering new user", e);
- } finally {
- releaseConn(conn);
- }
- return accountData;
- }
-
- private AccountDataBean getAccountDataFromResultSet(ResultSet rs) throws Exception {
- AccountDataBean accountData = null;
-
- if (!rs.next()) {
- Log.error("TradeDirect:getAccountDataFromResultSet -- cannot find account data");
- } else {
- accountData = new AccountDataBean(new Integer(rs.getInt("accountID")), rs.getInt("loginCount"), rs.getInt("logoutCount"),
- rs.getTimestamp("lastLogin"), rs.getTimestamp("creationDate"), rs.getBigDecimal("balance"), rs.getBigDecimal("openBalance"),
- rs.getString("profile_userID"));
- }
- return accountData;
- }
-
- private AccountProfileDataBean getAccountProfileDataFromResultSet(ResultSet rs) throws Exception {
- AccountProfileDataBean accountProfileData = null;
-
- if (!rs.next()) {
- Log.error("TradeDirect:getAccountProfileDataFromResultSet -- cannot find accountprofile data");
- } else {
- accountProfileData = new AccountProfileDataBean(rs.getString("userID"), rs.getString("passwd"), rs.getString("fullName"), rs.getString("address"),
- rs.getString("email"), rs.getString("creditCard"));
- }
-
- return accountProfileData;
- }
-
- private HoldingDataBean getHoldingDataFromResultSet(ResultSet rs) throws Exception {
- HoldingDataBean holdingData = null;
-
- holdingData = new HoldingDataBean(new Integer(rs.getInt("holdingID")), rs.getDouble("quantity"), rs.getBigDecimal("purchasePrice"),
- rs.getTimestamp("purchaseDate"), rs.getString("quote_symbol"));
- return holdingData;
- }
-
- private QuoteDataBean getQuoteDataFromResultSet(ResultSet rs) throws Exception {
- QuoteDataBean quoteData = null;
-
- quoteData = new QuoteDataBean(rs.getString("symbol"), rs.getString("companyName"), rs.getDouble("volume"), rs.getBigDecimal("price"),
- rs.getBigDecimal("open1"), rs.getBigDecimal("low"), rs.getBigDecimal("high"), rs.getDouble("change1"));
- return quoteData;
- }
-
- private OrderDataBean getOrderDataFromResultSet(ResultSet rs) throws Exception {
- OrderDataBean orderData = null;
-
- orderData = new OrderDataBean(new Integer(rs.getInt("orderID")), rs.getString("orderType"), rs.getString("orderStatus"), rs.getTimestamp("openDate"),
- rs.getTimestamp("completionDate"), rs.getDouble("quantity"), rs.getBigDecimal("price"), rs.getBigDecimal("orderFee"),
- rs.getString("quote_symbol"));
- return orderData;
- }
-
- public boolean recreateDBTables(Object[] sqlBuffer, java.io.PrintWriter out) throws Exception {
- // Clear MDB Statistics
- MDBStats.getInstance().reset();
-
- Connection conn = null;
- boolean success = false;
- try {
-
- Log.trace("TradeDirect:recreateDBTables");
-
- conn = getConn();
- Statement stmt = conn.createStatement();
- int bufferLength = sqlBuffer.length;
- for (int i = 0; i < bufferLength; i++) {
- try {
- stmt.executeUpdate((String) sqlBuffer[i]);
- // commit(conn);
- } catch (SQLException ex) {
- // Ignore DROP statements as tables won't always exist.
- if (((String) sqlBuffer[i]).indexOf("DROP ") < 0) {
- Log.error("TradeDirect:recreateDBTables SQL Exception thrown on executing the foll sql command: " + sqlBuffer[i], ex);
- out.println(" SQL Exception thrown on executing the foll sql command: " + sqlBuffer[i] + " . Check log for details.");
- }
- }
- }
- stmt.close();
- commit(conn);
- success = true;
- } catch (Exception e) {
- Log.error(e, "TradeDirect:recreateDBTables() -- Error dropping and recreating the database tables");
- } finally {
- releaseConn(conn);
- }
- return success;
- }
-
- ;
-
-
-
- private void releaseConn(Connection conn) throws Exception {
- try {
- if (conn != null) {
- conn.close();
- if (Log.doTrace()) {
- synchronized (lock) {
- connCount--;
- }
- Log.trace("TradeDirect:releaseConn -- connection closed, connCount=" + connCount);
- }
- }
- } catch (Exception e) {
- Log.error("TradeDirect:releaseConnection -- failed to close connection", e);
- }
- }
-
-
- /*
- * Allocate a new connection to the datasource
- */
- private static int connCount = 0;
-
- private static Integer lock = new Integer(0);
-
- private Connection getConn() throws Exception {
-
- Connection conn = datasource.getConnection();
-
- if (!this.inGlobalTxn) {
- conn.setAutoCommit(false);
- }
- if (Log.doTrace()) {
- synchronized (lock) {
- connCount++;
- }
- Log.trace("TradeDirect:getConn -- new connection allocated, IsolationLevel=" + conn.getTransactionIsolation() + " connectionCount = " + connCount);
- }
-
- return conn;
- }
-
- public Connection getConnPublic() throws Exception {
- return getConn();
- }
-
- /*
- * Commit the provided connection if not under Global Transaction scope -
- * conn.commit() is not allowed in a global transaction. the txn manager
- * will perform the commit
- */
- private void commit(Connection conn) throws Exception {
- if (!inSession) {
- if ((getInGlobalTxn() == false) && (conn != null)) {
- conn.commit();
- }
- }
- }
-
- /*
- * Rollback the statement for the given connection
- */
- private void rollBack(Connection conn, Exception e) throws Exception {
- if (!inSession) {
- Log.log("TradeDirect:rollBack -- rolling back conn due to previously caught exception -- inGlobalTxn=" + getInGlobalTxn());
- if ((getInGlobalTxn() == false) && (conn != null)) {
- conn.rollback();
- } else {
- throw e; // Throw the exception
- // so the Global txn manager will rollBack
- }
- }
- }
-
- /*
- * Allocate a new prepared statment for this connection
- */
- private PreparedStatement getStatement(Connection conn, String sql) throws Exception {
- return conn.prepareStatement(sql);
- }
-
- private PreparedStatement getStatement(Connection conn, String sql, int type, int concurrency) throws Exception {
- return conn.prepareStatement(sql, type, concurrency);
- }
-
- private static final String createQuoteSQL = "insert into quoteejb " + "( symbol, companyName, volume, price, open1, low, high, change1 ) "
- + "VALUES ( ? , ? , ? , ? , ? , ? , ? , ? )";
-
- private static final String createAccountSQL = "insert into accountejb "
- + "( accountid, creationDate, openBalance, balance, lastLogin, loginCount, logoutCount, profile_userid) "
- + "VALUES ( ? , ? , ? , ? , ? , ? , ? , ? )";
-
- private static final String createAccountProfileSQL = "insert into accountprofileejb " + "( userid, passwd, fullname, address, email, creditcard ) "
- + "VALUES ( ? , ? , ? , ? , ? , ? )";
-
- private static final String createHoldingSQL = "insert into holdingejb "
- + "( holdingid, purchaseDate, purchasePrice, quantity, quote_symbol, account_accountid ) " + "VALUES ( ? , ? , ? , ? , ? , ? )";
-
- private static final String createOrderSQL = "insert into orderejb "
- + "( orderid, ordertype, orderstatus, opendate, quantity, price, orderfee, account_accountid, holding_holdingid, quote_symbol) "
- + "VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ?)";
-
- private static final String removeHoldingSQL = "delete from holdingejb where holdingid = ?";
-
- private static final String removeHoldingFromOrderSQL = "update orderejb set holding_holdingid=null where holding_holdingid = ?";
-
- private static final String updateAccountProfileSQL = "update accountprofileejb set " + "passwd = ?, fullname = ?, address = ?, email = ?, creditcard = ? "
- + "where userid = (select profile_userid from accountejb a " + "where a.profile_userid=?)";
-
- private static final String loginSQL = "update accountejb set lastLogin=?, logincount=logincount+1 " + "where profile_userid=?";
-
- private static final String logoutSQL = "update accountejb set logoutcount=logoutcount+1 " + "where profile_userid=?";
-
- private static final String getAccountSQL = "select * from accountejb a where a.accountid = ?";
-
- private static final String getAccountProfileSQL = "select * from accountprofileejb ap where ap.userid = "
- + "(select profile_userid from accountejb a where a.profile_userid=?)";
-
- private static final String getAccountProfileForAccountSQL = "select * from accountprofileejb ap where ap.userid = "
- + "(select profile_userid from accountejb a where a.accountid=?)";
-
- private static final String getAccountForUserSQL = "select * from accountejb a where a.profile_userid = "
- + "( select userid from accountprofileejb ap where ap.userid = ?)";
-
- private static final String getHoldingSQL = "select * from holdingejb h where h.holdingid = ?";
-
- private static final String getHoldingsForUserSQL = "select * from holdingejb h where h.account_accountid = "
- + "(select a.accountid from accountejb a where a.profile_userid = ?)";
-
- private static final String getOrderSQL = "select * from orderejb o where o.orderid = ?";
-
- private static final String getOrdersByUserSQL = "select * from orderejb o where o.account_accountid = "
- + "(select a.accountid from accountejb a where a.profile_userid = ?)";
-
- private static final String getClosedOrdersSQL = "select * from orderejb o " + "where o.orderstatus = 'closed' AND o.account_accountid = "
- + "(select a.accountid from accountejb a where a.profile_userid = ?)";
-
- private static final String getQuoteSQL = "select * from quoteejb q where q.symbol=?";
-
- private static final String getAllQuotesSQL = "select * from quoteejb q";
-
- private static final String getQuoteForUpdateSQL = "select * from quoteejb q where q.symbol=? For Update";
-
- private static final String getTSIAQuotesOrderByChangeSQL = "select * from quoteejb q order by q.change1";
-
- private static final String getTSIASQL = "select SUM(price)/count(*) as TSIA from quoteejb q ";
-
- private static final String getOpenTSIASQL = "select SUM(open1)/count(*) as openTSIA from quoteejb q ";
-
- private static final String getTSIATotalVolumeSQL = "select SUM(volume) as totalVolume from quoteejb q ";
-
- private static final String creditAccountBalanceSQL = "update accountejb set " + "balance = balance + ? " + "where accountid = ?";
-
- private static final String updateOrderStatusSQL = "update orderejb set " + "orderstatus = ?, completiondate = ? " + "where orderid = ?";
-
- private static final String updateOrderHoldingSQL = "update orderejb set " + "holding_holdingID = ? " + "where orderid = ?";
-
- private static final String updateQuotePriceVolumeSQL = "update quoteejb set " + "price = ?, change1 = ?, volume = ? " + "where symbol = ?";
-
- /**
- * Gets the inGlobalTxn
- *
- * @return Returns a boolean
- */
- private boolean getInGlobalTxn() {
- return inGlobalTxn;
- }
-
- /**
- * Sets the inGlobalTxn
- *
- * @param inGlobalTxn
- * The inGlobalTxn to set
- */
- private void setInGlobalTxn(boolean inGlobalTxn) {
- this.inGlobalTxn = inGlobalTxn;
- }
-
- public void setInSession(boolean inSession) {
- this.inSession = inSession;
- }
-
- @Override
- public int getImpl() {
- return TradeConfig.DIRECT;
- }
-
-
-
- @Override
- public QuoteDataBean pingTwoPhase(String symbol) {
- throw new UnsupportedOperationException();
- }
-
-
-
- @Override
- public double investmentReturn(double rnd1, double rnd2) {
- throw new UnsupportedOperationException();
- }
-}
diff --git a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/TradeDirectDBUtils.java b/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/TradeDirectDBUtils.java
deleted file mode 100644
index 8511c6b7..00000000
--- a/src/test/resources/test-applications/daytrader8/src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/TradeDirectDBUtils.java
+++ /dev/null
@@ -1,470 +0,0 @@
-/**
- * (C) Copyright IBM Corporation 2019.
- *
- * Licensed 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.
- */
-package com.ibm.websphere.samples.daytrader.impl.direct;
-
-import com.ibm.websphere.samples.daytrader.beans.RunStatsDataBean;
-import com.ibm.websphere.samples.daytrader.entities.AccountDataBean;
-import com.ibm.websphere.samples.daytrader.interfaces.TradeDB;
-import com.ibm.websphere.samples.daytrader.interfaces.TradeJDBC;
-import com.ibm.websphere.samples.daytrader.interfaces.TradeServices;
-import com.ibm.websphere.samples.daytrader.util.Log;
-import com.ibm.websphere.samples.daytrader.util.MDBStats;
-import com.ibm.websphere.samples.daytrader.util.TradeConfig;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.math.BigDecimal;
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import javax.annotation.Resource;
-import javax.enterprise.context.ApplicationScoped;
-import javax.inject.Inject;
-import javax.sql.DataSource;
-
-/**
- * TradeBuildDB uses operations provided by the TradeApplication to (a) create the Database tables
- * (b)populate a DayTrader database without creating the tables. Specifically, a
- * new DayTrader User population is created using UserIDs of the form "uid:xxx"
- * where xxx is a sequential number (e.g. uid:0, uid:1, etc.). New stocks are also created of the
- * form "s:xxx", again where xxx represents sequential numbers (e.g. s:1, s:2, etc.)
- */
-@ApplicationScoped
-public class TradeDirectDBUtils implements TradeDB {
-
- // For Wildfly - add java:/ to this resource.
-
- @Resource(lookup = "jdbc/TradeDataSource")
- //@Resource(lookup = "java:/jdbc/TradeDataSource")
- private DataSource datasource;
-
- @Inject
- @TradeJDBC
- TradeServices ts;
-
- public String checkDBProductName() throws Exception {
- Connection conn = null;
- String dbProductName = null;
-
- try {
-
- conn = datasource.getConnection();
- DatabaseMetaData dbmd = conn.getMetaData();
- dbProductName = dbmd.getDatabaseProductName();
- } catch (SQLException e) {
- Log.error(e, "TradeDirect:checkDBProductName() -- Error checking the Daytrader Database Product Name");
- } finally {
- conn.close();
- }
- return dbProductName;
- }
-
-
- /**
- * Re-create the DayTrader db tables and populate them OR just populate a DayTrader DB, logging to the provided output stream
- */
- public void buildDB(java.io.PrintWriter out, InputStream ddlFile) throws Exception {
- String symbol, companyName;
- int errorCount = 0; // Give up gracefully after 10 errors
-
- // TradeStatistics.statisticsEnabled=false; // disable statistics
- out.println(" TradeBuildDB: Building DayTrader Database... This operation will take several minutes. Please wait...");
- out.println("");
-
- if (ddlFile != null) {
- //out.println(" TradeBuildDB: **** warPath= "+warPath+" ****");
-
- boolean success = false;
-
- Object[] sqlBuffer = null;
-
- //parse the DDL file and fill the SQL commands into a buffer
- try {
- sqlBuffer = parseDDLToBuffer(ddlFile);
- } catch (Exception e) {
- Log.error(e, "TradeBuildDB: Unable to parse DDL file");
- out.println(" TradeBuildDB: **** Unable to parse DDL file for the specified database ****